curatare
This commit is contained in:
@@ -1,23 +0,0 @@
|
||||
================================================================================
|
||||
ROA2WEB DEPLOYMENT PACKAGE
|
||||
Generated: 2026-02-23 15:12:31
|
||||
From: Linux/LXC deployment script
|
||||
================================================================================
|
||||
|
||||
CONTENTS:
|
||||
---------
|
||||
backend/ Unified FastAPI backend
|
||||
frontend/ Vue.js SPA (production build)
|
||||
shared/ Shared Python modules
|
||||
config/ Configuration templates
|
||||
scripts/ PowerShell deployment scripts
|
||||
|
||||
DEPLOYMENT:
|
||||
-----------
|
||||
Server will auto-deploy within 5 minutes (Check-And-Deploy.ps1 scheduled task)
|
||||
|
||||
Or manually:
|
||||
cd scripts
|
||||
.\ROA2WEB-Console.ps1 -NonInteractive -Action DeployAll
|
||||
|
||||
================================================================================
|
||||
@@ -1,179 +0,0 @@
|
||||
# ============================================================================
|
||||
# ROA2WEB Unified Backend - Environment Configuration Template
|
||||
# ============================================================================
|
||||
# Single backend process serving Reports, Data Entry, and Telegram modules
|
||||
#
|
||||
# SETUP INSTRUCTIONS:
|
||||
# 1. Copy this template: cp .env.example .env.dev
|
||||
# 2. Fill in your actual values in .env.dev
|
||||
# 3. Run: ./start-dev.sh (auto-copies .env.dev to .env)
|
||||
#
|
||||
# ENVIRONMENT FILES:
|
||||
# - .env.dev → Development config (committed to git with real values)
|
||||
# - .env.test → Test config (committed to git)
|
||||
# - .env.prod → Production config template (committed, use placeholders!)
|
||||
# - .env → Active config (auto-generated, NOT committed)
|
||||
#
|
||||
# IMPORTANT: Never manually edit .env - edit .env.dev instead!
|
||||
|
||||
# ============================================================================
|
||||
# ORACLE DATABASE CONFIGURATION
|
||||
# ============================================================================
|
||||
# Single server: Use ORACLE_USER/HOST/PORT/SID
|
||||
# Multi-server: Use ORACLE_SERVERS JSON (ignores single server vars)
|
||||
# Passwords: secrets/{id}.oracle_pass
|
||||
# SSH tunnels: ssh-tunnels.json (separate file)
|
||||
|
||||
ORACLE_USER=CONTAFIN_ORACLE
|
||||
ORACLE_PASSWORD=SET_IN_SECRETS_FILE
|
||||
ORACLE_HOST=localhost
|
||||
ORACLE_PORT=1521
|
||||
ORACLE_SID=ROA
|
||||
|
||||
# Multi-server example (uncomment to use):
|
||||
# ORACLE_SERVERS='[{"id":"server1","name":"Server 1","host":"localhost","port":1521,"user":"USER","sid":"ROA"}]'
|
||||
|
||||
# ============================================================================
|
||||
# JWT AUTHENTICATION (REQUIRED - Shared by all modules)
|
||||
# ============================================================================
|
||||
# Used for JWT token generation and validation (shared/auth/jwt_handler.py)
|
||||
# Generate strong secret: python3 -c "import secrets; print(secrets.token_urlsafe(32))"
|
||||
|
||||
JWT_SECRET_KEY=GENERATE_STRONG_SECRET_IN_PRODUCTION
|
||||
JWT_ALGORITHM=HS256
|
||||
|
||||
# Token expiration settings (used by shared/auth/jwt_handler.py)
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=30
|
||||
REFRESH_TOKEN_EXPIRE_DAYS=7
|
||||
|
||||
# ============================================================================
|
||||
# SESSION SECURITY - EMAIL 2FA (REQUIRED for Telegram email login)
|
||||
# ============================================================================
|
||||
# Used by Telegram module for session token validation
|
||||
# Generate with: python3 -c "import secrets; print(secrets.token_urlsafe(32))"
|
||||
|
||||
AUTH_SESSION_SECRET=your-secure-random-secret-here-min-32-chars
|
||||
|
||||
# ============================================================================
|
||||
# SERVER CONFIGURATION
|
||||
# ============================================================================
|
||||
# Unified backend server settings
|
||||
|
||||
API_HOST=0.0.0.0
|
||||
API_PORT=8000
|
||||
DEBUG=false
|
||||
|
||||
# CORS Origins (comma-separated)
|
||||
CORS_ORIGINS=http://localhost:3000,http://localhost:5173
|
||||
|
||||
# ============================================================================
|
||||
# REPORTS MODULE - CACHE CONFIGURATION (OPTIONAL - defaults provided)
|
||||
# ============================================================================
|
||||
# Two-tier hybrid cache system (L1: in-memory LRU, L2: SQLite persistent)
|
||||
# Used by backend/modules/reports/cache/config.py
|
||||
|
||||
# Core Settings
|
||||
REPORTS_CACHE_ENABLED=True
|
||||
REPORTS_CACHE_TYPE=hybrid
|
||||
REPORTS_CACHE_SQLITE_PATH=./data/cache/roa2web_cache.db
|
||||
REPORTS_CACHE_MEMORY_MAX_SIZE=1000
|
||||
REPORTS_CACHE_DEFAULT_TTL=900
|
||||
|
||||
# TTL per Cache Type (seconds)
|
||||
REPORTS_CACHE_TTL_SCHEMA=86400
|
||||
REPORTS_CACHE_TTL_COMPANIES=1800
|
||||
REPORTS_CACHE_TTL_DASHBOARD_SUMMARY=1800
|
||||
REPORTS_CACHE_TTL_DASHBOARD_TRENDS=1800
|
||||
REPORTS_CACHE_TTL_INVOICES=600
|
||||
REPORTS_CACHE_TTL_INVOICES_SUMMARY=900
|
||||
REPORTS_CACHE_TTL_TREASURY=600
|
||||
|
||||
# Maintenance
|
||||
REPORTS_CACHE_CLEANUP_INTERVAL=3600
|
||||
|
||||
# Event-Based Invalidation (experimental)
|
||||
REPORTS_CACHE_AUTO_INVALIDATE=False
|
||||
REPORTS_CACHE_CHECK_INTERVAL=300
|
||||
|
||||
# Performance Tracking
|
||||
REPORTS_CACHE_TRACK_PERFORMANCE=True
|
||||
REPORTS_CACHE_BENCHMARK_ON_STARTUP=False
|
||||
|
||||
# ============================================================================
|
||||
# DATA ENTRY MODULE - CONFIGURATION
|
||||
# ============================================================================
|
||||
# Data Entry module settings (receipts, OCR, etc.)
|
||||
|
||||
# SQLite Database
|
||||
DATA_ENTRY_SQLITE_DATABASE_PATH=data/receipts/receipts.db
|
||||
|
||||
# File uploads
|
||||
DATA_ENTRY_UPLOAD_PATH=data/receipts/uploads
|
||||
DATA_ENTRY_MAX_UPLOAD_SIZE_MB=10
|
||||
|
||||
# ============================================================================
|
||||
# OCR ENGINE CONFIGURATION
|
||||
# ============================================================================
|
||||
# Control which OCR engines are loaded at startup.
|
||||
# Disabling engines saves memory but limits available OCR modes.
|
||||
|
||||
# Enable/disable PaddleOCR (set to 'false' to save ~800MB RAM)
|
||||
# When disabled: 'paddleocr' engine unavailable
|
||||
OCR_ENABLE_PADDLEOCR=false
|
||||
|
||||
# Enable/disable Tesseract (set to 'false' to save ~50MB RAM)
|
||||
# When disabled: 'tesseract' engine unavailable
|
||||
OCR_ENABLE_TESSERACT=false
|
||||
|
||||
# Default OCR engine when not specified in request
|
||||
# Options: tesseract, doctr, doctr_plus, paddleocr
|
||||
# Recommended: doctr_plus (2-tier sequential with early exit, ~7.5s avg)
|
||||
OCR_DEFAULT_ENGINE=doctr_plus
|
||||
|
||||
# Active OCR engines shown in frontend dropdown (comma-separated)
|
||||
# Options: tesseract, doctr, doctr_plus, paddleocr
|
||||
# doctr_plus: 73.3% perfect, 7.5s avg, 65% fast path (recommended)
|
||||
# doctr: 63.3% perfect, simpler but faster
|
||||
OCR_ACTIVE_ENGINES=tesseract,doctr,doctr_plus,paddleocr
|
||||
|
||||
# OCR Worker Pool Configuration
|
||||
# Number of parallel OCR workers (each loads ~1GB for docTR)
|
||||
# Recommended: 2 for 8GB RAM, 3 for 16GB RAM
|
||||
OCR_WORKERS=2
|
||||
|
||||
# Max tasks per worker before restart (0 = no restart, saves 40-60s warmup time)
|
||||
# Set to 0 for testing, 10-20 for production (prevents memory leaks)
|
||||
OCR_MAX_TASKS_PER_CHILD=0
|
||||
|
||||
# ============================================================================
|
||||
# TELEGRAM MODULE - BOT CONFIGURATION (REQUIRED for Telegram features)
|
||||
# ============================================================================
|
||||
# Obtain bot token from @BotFather on Telegram
|
||||
|
||||
TELEGRAM_BOT_TOKEN=your_bot_token_here
|
||||
|
||||
# ============================================================================
|
||||
# TELEGRAM MODULE - EMAIL AUTHENTICATION (SMTP) (REQUIRED for email 2FA)
|
||||
# ============================================================================
|
||||
# Required for email-based 2FA authentication flow
|
||||
# Users can login with email + password instead of web app linking
|
||||
|
||||
# SMTP Server Configuration
|
||||
TELEGRAM_SMTP_HOST=mail.romfast.ro
|
||||
TELEGRAM_SMTP_PORT=587
|
||||
TELEGRAM_SMTP_USER=ups@romfast.ro
|
||||
TELEGRAM_SMTP_PASSWORD=your_smtp_password_here
|
||||
TELEGRAM_SMTP_FROM_EMAIL=ups@romfast.ro
|
||||
TELEGRAM_SMTP_FROM_NAME=ROA2WEB
|
||||
TELEGRAM_SMTP_USE_TLS=true
|
||||
|
||||
# Email Retry Settings
|
||||
TELEGRAM_EMAIL_MAX_RETRIES=3
|
||||
TELEGRAM_EMAIL_RETRY_DELAY=2.0
|
||||
|
||||
# ============================================================================
|
||||
# TELEGRAM MODULE - DATABASE (SQLite for bot data)
|
||||
# ============================================================================
|
||||
# Separate SQLite database for Telegram bot auth codes and sessions
|
||||
|
||||
TELEGRAM_SQLITE_DATABASE_PATH=data/telegram/telegram.db
|
||||
@@ -1,212 +0,0 @@
|
||||
# Environment Configuration Guide
|
||||
|
||||
## Overview
|
||||
|
||||
The unified backend uses environment-specific configuration files that are automatically loaded by startup scripts.
|
||||
|
||||
**SECURITY**: All `.env*` files (except `.env*.example`) contain real credentials and are **NEVER committed to git**.
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
backend/
|
||||
├── .env.prod.example # Production template (COMMITTED - no credentials)
|
||||
├── .env.test.example # Test template (COMMITTED - no credentials)
|
||||
├── .env.prod.example # Production template (COMMITTED - no credentials)
|
||||
├── .env.example # Generic template (COMMITTED)
|
||||
├── .env.prod # Production config (IGNORED - real credentials)
|
||||
├── .env.test # Test config (IGNORED - real credentials)
|
||||
├── .env.prod # Production config (IGNORED - real credentials)
|
||||
└── .env # Active config (IGNORED - auto-generated)
|
||||
```
|
||||
|
||||
## First-Time Setup
|
||||
|
||||
### Production
|
||||
```bash
|
||||
# 1. Copy template
|
||||
cp backend/.env.prod.example backend/.env.prod
|
||||
|
||||
# 2. Edit with your credentials
|
||||
vim backend/.env.prod
|
||||
|
||||
# 3. Fill in:
|
||||
# - ORACLE_PASSWORD
|
||||
# - JWT_SECRET_KEY (generate with: python3 -c "import secrets; print(secrets.token_urlsafe(32))")
|
||||
# - AUTH_SESSION_SECRET (generate with: python3 -c "import secrets; print(secrets.token_urlsafe(32))")
|
||||
# - TELEGRAM_BOT_TOKEN (from @BotFather)
|
||||
# - SMTP_PASSWORD
|
||||
|
||||
# 4. Start
|
||||
./start.sh prod
|
||||
```
|
||||
|
||||
### Test
|
||||
```bash
|
||||
# Same process with .env.test
|
||||
cp backend/.env.test.example backend/.env.test
|
||||
vim backend/.env.test
|
||||
# Fill in TEST credentials (separate from dev!)
|
||||
./start.sh test
|
||||
```
|
||||
|
||||
### Production
|
||||
```bash
|
||||
# Same process with .env.prod
|
||||
cp backend/.env.prod.example backend/.env.prod
|
||||
vim backend/.env.prod
|
||||
# Fill in PRODUCTION credentials (generate NEW secrets!)
|
||||
./start-backend.sh start
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
### Production
|
||||
```bash
|
||||
./start.sh prod # Checks for .env.prod → copies to .env → starts backend
|
||||
```
|
||||
|
||||
### Test
|
||||
```bash
|
||||
./start.sh test # Checks for .env.test → copies to .env → starts backend
|
||||
```
|
||||
|
||||
### Production
|
||||
```bash
|
||||
# Manual setup (one-time)
|
||||
cp .env.prod.example .env.prod
|
||||
vim .env.prod # Fill in credentials
|
||||
# Then start
|
||||
./start-backend.sh start
|
||||
```
|
||||
|
||||
## Important Rules
|
||||
|
||||
### ✅ DO
|
||||
- Copy `.env.*.example` to `.env.*` and fill in real credentials
|
||||
- Edit `.env.prod` for production changes
|
||||
- Edit `.env.test` for test environment changes
|
||||
- Edit `.env.prod` for production
|
||||
- Generate **new** secrets for each environment
|
||||
- Keep `.env.prod`, `.env.test`, `.env.prod` **local only** (never commit!)
|
||||
|
||||
### ❌ DON'T
|
||||
- Don't commit `.env`, `.env.prod`, `.env.test`, or `.env.prod` (they're in .gitignore)
|
||||
- Don't manually edit `.env` (it's auto-generated!)
|
||||
- Don't use same secrets across environments
|
||||
- Don't share credentials via git (use secure channels)
|
||||
- Don't put real credentials in `.env*.example` files
|
||||
|
||||
## Environment Differences
|
||||
|
||||
| Setting | .env.prod | .env.test | .env.prod |
|
||||
|---------|----------|-----------|-----------|
|
||||
| Oracle SID | `ROA` | `roa` | `ROA` |
|
||||
| JWT Expire | 30 min | 480 min | 30 min |
|
||||
| DEBUG | `true` | `true` | `false` |
|
||||
| Cache DB | `roa2web_cache.db` | `roa2web_cache_test.db` | `roa2web_cache_prod.db` |
|
||||
| Receipts DB | `receipts_dev.db` | `receipts_test.db` | `receipts_prod.db` |
|
||||
| Telegram DB | `telegram.db` | `telegram_test.db` | `telegram_prod.db` |
|
||||
|
||||
## Security Notes
|
||||
|
||||
### Template Files (.env.*.example)
|
||||
These contain **placeholders only**:
|
||||
- ✅ Safe to commit to git
|
||||
- ✅ Shared across team
|
||||
- ✅ No real credentials
|
||||
- 📖 Used as reference for first-time setup
|
||||
|
||||
### Actual Config Files (.env.prod, .env.test, .env.prod)
|
||||
These contain **real credentials**:
|
||||
- ❌ **NEVER commit to git** (in .gitignore)
|
||||
- ❌ Never share via email/chat
|
||||
- ✅ Keep local only
|
||||
- ✅ Generate unique secrets per environment
|
||||
- 🔐 Share securely if needed (encrypted vault, 1Password, etc.)
|
||||
|
||||
### Active Config (.env)
|
||||
This is **auto-generated** and **ignored by git**:
|
||||
- ❌ Never commit to git
|
||||
- 🔄 Auto-overwritten by startup scripts
|
||||
- 📝 Edit source files (.env.prod, .env.test) instead
|
||||
|
||||
## Generating Secrets
|
||||
|
||||
For `JWT_SECRET_KEY` and `AUTH_SESSION_SECRET`:
|
||||
```bash
|
||||
python3 -c "import secrets; print(secrets.token_urlsafe(32))"
|
||||
```
|
||||
|
||||
Generate **different** secrets for dev, test, and production!
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### First Time Setup
|
||||
```bash
|
||||
# 1. Copy template
|
||||
cp backend/.env.prod.example backend/.env.prod
|
||||
|
||||
# 2. Fill credentials
|
||||
vim backend/.env.prod
|
||||
|
||||
# 3. Start
|
||||
./start.sh prod
|
||||
```
|
||||
|
||||
### Changing Configuration
|
||||
```bash
|
||||
# 1. Edit source file
|
||||
vim backend/.env.prod
|
||||
|
||||
# 2. Restart to apply
|
||||
./start.sh prod
|
||||
```
|
||||
|
||||
### Production Deployment
|
||||
```bash
|
||||
# 1. Copy template
|
||||
cp backend/.env.prod.example backend/.env.prod
|
||||
|
||||
# 2. Fill in PRODUCTION values
|
||||
vim backend/.env.prod
|
||||
|
||||
# 3. Generate NEW secrets
|
||||
python3 -c "import secrets; print(secrets.token_urlsafe(32))"
|
||||
|
||||
# 4. Start backend
|
||||
./start-backend.sh start
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Wrong database" error
|
||||
Check that you're using the correct startup script:
|
||||
- Production: `./start.sh prod` (uses `.env.prod`)
|
||||
- Test: `./start.sh test` (uses `.env.test`)
|
||||
|
||||
### ".env.prod not found" error
|
||||
First-time setup required:
|
||||
```bash
|
||||
cp backend/.env.prod.example backend/.env.prod
|
||||
vim backend/.env.prod # Fill in your credentials
|
||||
```
|
||||
|
||||
### Changes not taking effect
|
||||
The `.env` file is regenerated on each start. Edit the source file (`.env.prod` or `.env.test`) instead.
|
||||
|
||||
### Checking what will be committed
|
||||
```bash
|
||||
git status backend/.env*
|
||||
# Should show:
|
||||
# modified: .env.prod.example (if you changed template)
|
||||
# nothing else!
|
||||
```
|
||||
|
||||
## Team Sharing
|
||||
|
||||
**Templates only** are committed to git:
|
||||
- Share configuration structure via `.env*.example`
|
||||
- Each developer creates their own `.env.prod` from template
|
||||
- Never commit actual credentials
|
||||
- Use secure channels for sharing sensitive values (1Password, encrypted vault, etc.)
|
||||
@@ -1,102 +0,0 @@
|
||||
# Quick Environment Reference
|
||||
|
||||
## 🔒 SECURITY FIRST
|
||||
|
||||
**All `.env*` files (except `.env*.example`) contain real credentials and are NEVER committed to git!**
|
||||
|
||||
## 🚀 First-Time Setup
|
||||
|
||||
```bash
|
||||
# 1. Copy template with real credentials
|
||||
cp backend/.env.prod.example backend/.env.prod
|
||||
|
||||
# 2. Edit with YOUR credentials
|
||||
vim backend/.env.prod
|
||||
|
||||
# 3. Fill in the placeholders:
|
||||
# - ORACLE_PASSWORD
|
||||
# - JWT_SECRET_KEY
|
||||
# - AUTH_SESSION_SECRET
|
||||
# - TELEGRAM_BOT_TOKEN
|
||||
# - SMTP_PASSWORD
|
||||
|
||||
# 4. Start production
|
||||
./start.sh prod
|
||||
```
|
||||
|
||||
## 📋 Daily Usage
|
||||
|
||||
```bash
|
||||
# Production (uses .env.prod automatically)
|
||||
./start.sh prod
|
||||
|
||||
# Test Environment (uses .env.test automatically)
|
||||
./start.sh test
|
||||
|
||||
# Quick Restart (uses existing .env)
|
||||
./start-backend.sh restart
|
||||
```
|
||||
|
||||
## ✏️ Changing Configuration
|
||||
|
||||
```bash
|
||||
# 1. Edit the source file (NOT .env!)
|
||||
vim backend/.env.prod # Production
|
||||
vim backend/.env.test # Test
|
||||
|
||||
# 2. Restart to apply changes
|
||||
./start.sh prod
|
||||
```
|
||||
|
||||
## 📁 Which File to Edit?
|
||||
|
||||
| You Want To... | Edit This File |
|
||||
|----------------|----------------|
|
||||
| Change dev database password | `backend/.env.prod` |
|
||||
| Update test server settings | `backend/.env.test` |
|
||||
| Add new environment variable | Templates: `.env*.example` + your `.env.prod`/`.env.test` |
|
||||
| Create production config | Copy `.env.prod.example` to `.env.prod` and fill secrets |
|
||||
|
||||
## 🔑 Generating Secrets
|
||||
|
||||
```bash
|
||||
# For JWT_SECRET_KEY and AUTH_SESSION_SECRET
|
||||
python3 -c "import secrets; print(secrets.token_urlsafe(32))"
|
||||
```
|
||||
|
||||
**Generate DIFFERENT secrets for each environment (dev, test, prod)!**
|
||||
|
||||
## ⚠️ Important
|
||||
|
||||
- **Never edit** `backend/.env` directly (it's auto-generated!)
|
||||
- **Always edit** `backend/.env.prod` or `.env.test`
|
||||
- **Never commit** `.env`, `.env.prod`, `.env.test`, `.env.prod`
|
||||
- **Only commit** `.env*.example` (templates with placeholders)
|
||||
- Restart after changes for them to take effect
|
||||
|
||||
## 🛡️ Git Behavior
|
||||
|
||||
| File | Git Status | Contains |
|
||||
|------|-----------|----------|
|
||||
| `.env.prod.example` | ✅ Committed | Template (placeholders) |
|
||||
| `.env.test.example` | ✅ Committed | Template (placeholders) |
|
||||
| `.env.prod.example` | ✅ Committed | Template (placeholders) |
|
||||
| `.env.example` | ✅ Committed | Generic template |
|
||||
| `.env.prod` | ❌ Ignored | **Real dev credentials** |
|
||||
| `.env.test` | ❌ Ignored | **Real test credentials** |
|
||||
| `.env.prod` | ❌ Ignored | **Real prod credentials** |
|
||||
| `.env` | ❌ Ignored | Auto-generated (current) |
|
||||
|
||||
## ✅ Quick Check
|
||||
|
||||
```bash
|
||||
# See what git will commit
|
||||
git status backend/.env*
|
||||
|
||||
# Should show ONLY .env*.example files
|
||||
# If .env.prod or .env.test appear, they're NOT properly ignored!
|
||||
```
|
||||
|
||||
## 📖 More Info
|
||||
|
||||
See `backend/ENV-SETUP.md` for complete documentation.
|
||||
@@ -1,168 +0,0 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
cd /d "%~dp0"
|
||||
|
||||
REM Parse command line arguments for worker counts
|
||||
REM Usage: TEST-OCR-WINDOWS.bat [worker_counts...]
|
||||
REM Examples:
|
||||
REM TEST-OCR-WINDOWS.bat -> tests 1,2,3 workers (default)
|
||||
REM TEST-OCR-WINDOWS.bat 1 -> tests only 1 worker
|
||||
REM TEST-OCR-WINDOWS.bat 3 6 -> tests 3 and 6 workers
|
||||
REM TEST-OCR-WINDOWS.bat 1 2 3 4 5 6 -> tests all
|
||||
|
||||
set "WORKER_LIST=%*"
|
||||
if "%WORKER_LIST%"=="" set "WORKER_LIST=1 2 3"
|
||||
|
||||
echo.
|
||||
echo ==========================================
|
||||
echo OCR Benchmark - Windows (Workers: %WORKER_LIST%)
|
||||
echo ==========================================
|
||||
echo.
|
||||
|
||||
REM Check if Poppler is installed
|
||||
where pdftoppm >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
echo Checking for Poppler...
|
||||
if exist "E:\poppler" (
|
||||
for /r "E:\poppler" %%i in (pdftoppm.exe) do (
|
||||
set "POPPLER_BIN=%%~dpi"
|
||||
goto :found_poppler
|
||||
)
|
||||
)
|
||||
echo.
|
||||
echo ERROR: Poppler not found!
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
:found_poppler
|
||||
if defined POPPLER_BIN (
|
||||
echo Found Poppler at: %POPPLER_BIN%
|
||||
set "PATH=%POPPLER_BIN%;%PATH%"
|
||||
)
|
||||
|
||||
REM Check venv
|
||||
if not exist "venv-win\Scripts\python.exe" (
|
||||
echo ERROR: venv-win not found!
|
||||
echo Run: python -m venv venv-win
|
||||
echo Then: venv-win\Scripts\pip install -r requirements.txt
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
REM Set common environment
|
||||
set JWT_SECRET_KEY=generate_with_secrets_token_urlsafe_32
|
||||
set ORACLE_HOST=10.0.20.121
|
||||
set ORACLE_PORT=1521
|
||||
set ORACLE_USER=CONTAFIN_ORACLE
|
||||
set ORACLE_PASSWORD=ROMFASTSOFT
|
||||
set ORACLE_SERVICE_NAME=ROA
|
||||
set OCR_ENABLE_PADDLEOCR=false
|
||||
set OCR_ENABLE_TESSERACT=false
|
||||
set OCR_DEFAULT_ENGINE=hybrid-doctr
|
||||
set OCR_MAX_TASKS_PER_CHILD=0
|
||||
set LOG_LEVEL=WARNING
|
||||
|
||||
REM Results file with timestamp
|
||||
for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /value') do set datetime=%%I
|
||||
set RESULTS_FILE=ocr_benchmark_%datetime:~0,8%_%datetime:~8,4%.json
|
||||
|
||||
echo Results will be saved to: %RESULTS_FILE%
|
||||
echo.
|
||||
|
||||
REM Delete old results file if exists
|
||||
if exist "%RESULTS_FILE%" del "%RESULTS_FILE%"
|
||||
|
||||
REM Run tests with specified workers
|
||||
for %%W in (%WORKER_LIST%) do (
|
||||
call :run_test %%W
|
||||
)
|
||||
|
||||
goto :show_summary
|
||||
|
||||
:run_test
|
||||
set WORKERS=%1
|
||||
echo.
|
||||
echo ############################################################
|
||||
echo STARTING TEST WITH %WORKERS% WORKER(S)
|
||||
echo ############################################################
|
||||
echo.
|
||||
|
||||
REM Kill existing processes on port 8006
|
||||
echo Cleaning up old processes...
|
||||
for /f "tokens=5" %%a in ('netstat -ano ^| findstr :8006 ^| findstr LISTENING 2^>nul') do (
|
||||
taskkill /F /PID %%a >nul 2>&1
|
||||
)
|
||||
taskkill /F /FI "WINDOWTITLE eq ROA2WEB Backend*" >nul 2>&1
|
||||
timeout /t 3 >nul
|
||||
|
||||
REM Set workers count
|
||||
set OCR_WORKERS=%WORKERS%
|
||||
|
||||
echo Starting backend with %WORKERS% OCR worker(s)...
|
||||
|
||||
REM Start backend in a new minimized window with all OCR env vars
|
||||
start /min "ROA2WEB Backend %WORKERS% workers" cmd /c "set OCR_WORKERS=%WORKERS%&& set OCR_ENABLE_PADDLEOCR=false&& set OCR_ENABLE_TESSERACT=false&& set OCR_DEFAULT_ENGINE=hybrid-doctr&& set LOG_LEVEL=WARNING&& venv-win\Scripts\python.exe -m uvicorn main:app --host 0.0.0.0 --port 8006 --workers 1 2>&1"
|
||||
|
||||
REM Wait for backend to be ready
|
||||
echo Waiting for backend to start...
|
||||
set attempts=0
|
||||
:wait_loop
|
||||
timeout /t 3 >nul
|
||||
set /a attempts+=1
|
||||
curl -s http://localhost:8006/health >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
if !attempts! lss 40 (
|
||||
echo Waiting... !attempts!/40
|
||||
goto :wait_loop
|
||||
)
|
||||
echo ERROR: Backend failed to start!
|
||||
goto :eof
|
||||
)
|
||||
|
||||
echo Backend is ready!
|
||||
|
||||
REM Wait for OCR warmup
|
||||
echo Waiting for OCR worker warmup (30s)...
|
||||
timeout /t 30 >nul
|
||||
|
||||
echo.
|
||||
echo Running OCR test with %WORKERS% worker(s)...
|
||||
echo.
|
||||
|
||||
venv-win\Scripts\python.exe ..\tests\ocr-validation\test_receipts_parallel_windows.py --port 8006 --workers %WORKERS% --output %RESULTS_FILE%
|
||||
|
||||
REM Stop backend
|
||||
echo.
|
||||
echo Stopping backend...
|
||||
taskkill /F /FI "WINDOWTITLE eq ROA2WEB Backend*" >nul 2>&1
|
||||
for /f "tokens=5" %%a in ('netstat -ano ^| findstr :8006 ^| findstr LISTENING 2^>nul') do (
|
||||
taskkill /F /PID %%a >nul 2>&1
|
||||
)
|
||||
|
||||
REM Wait for memory to be released
|
||||
echo Releasing memory (10s)...
|
||||
timeout /t 10 >nul
|
||||
goto :eof
|
||||
|
||||
:show_summary
|
||||
echo.
|
||||
echo ############################################################
|
||||
echo ALL TESTS COMPLETE
|
||||
echo ############################################################
|
||||
echo.
|
||||
echo Results saved to: %RESULTS_FILE%
|
||||
echo.
|
||||
|
||||
REM Show summary from results file
|
||||
if exist "%RESULTS_FILE%" (
|
||||
echo BENCHMARK SUMMARY:
|
||||
echo ------------------
|
||||
venv-win\Scripts\python.exe -c "import json; data=json.load(open('%RESULTS_FILE%')); print(); [print(f\" {r['workers']} worker(s): {r['total_time']:.1f}s total, {r['avg_time']:.1f}s avg, {r.get('peak_memory_mb', 0):.0f}MB peak, {r['successful']}/{r['submitted']} success\") for r in data]"
|
||||
echo.
|
||||
)
|
||||
|
||||
echo Press any key to exit...
|
||||
pause >nul
|
||||
|
||||
endlocal
|
||||
@@ -1,296 +0,0 @@
|
||||
"""
|
||||
Unified Configuration for ROA2WEB Backend
|
||||
Consolidates settings from Reports, Data Entry, and Telegram modules
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
from pydantic_settings import BaseSettings
|
||||
from pydantic import BaseModel
|
||||
from functools import lru_cache
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OracleServerConfig(BaseModel):
|
||||
"""Configuration for a single Oracle server instance."""
|
||||
id: str # Unique identifier (e.g., "romfast", "client_a")
|
||||
name: str # Human-readable name (e.g., "Romfast - Producție")
|
||||
host: str = "localhost"
|
||||
port: int = 1521
|
||||
user: str
|
||||
password: str
|
||||
sid: Optional[str] = None
|
||||
service_name: Optional[str] = None
|
||||
|
||||
def get_dsn(self) -> str:
|
||||
"""Build DSN string for this server."""
|
||||
if self.service_name:
|
||||
return f"{self.host}:{self.port}/{self.service_name}"
|
||||
elif self.sid:
|
||||
return f"{self.host}:{self.port}:{self.sid}"
|
||||
else:
|
||||
return f"{self.host}:{self.port}/ROA"
|
||||
|
||||
|
||||
class UnifiedSettings(BaseSettings):
|
||||
"""Unified application settings for all modules."""
|
||||
|
||||
# ============================================================================
|
||||
# GENERAL APPLICATION SETTINGS
|
||||
# ============================================================================
|
||||
app_name: str = "ROA2WEB Unified Backend"
|
||||
app_version: str = "1.0.0"
|
||||
debug: bool = False
|
||||
api_host: str = "0.0.0.0"
|
||||
api_port: int = 8000
|
||||
|
||||
# ============================================================================
|
||||
# ORACLE DATABASE (Shared by all modules)
|
||||
# ============================================================================
|
||||
# Legacy single-server configuration (backward compatible)
|
||||
oracle_user: str = ""
|
||||
oracle_password: str = ""
|
||||
oracle_host: str = "localhost"
|
||||
oracle_port: int = 1526
|
||||
oracle_sid: str = "ROA"
|
||||
|
||||
# ============================================================================
|
||||
# MULTI-ORACLE SERVER CONFIGURATION (Optional)
|
||||
# ============================================================================
|
||||
# JSON array of server configs. If not set, uses legacy single-server config.
|
||||
# Example: ORACLE_SERVERS='[{"id": "romfast", "name": "Romfast", "host": "localhost", "port": 1521, "user": "USER", "password": "PASS", "sid": "ROA"}]'
|
||||
oracle_servers: Optional[str] = None # Raw JSON string from env
|
||||
|
||||
# Parsed server configurations (populated in model_post_init)
|
||||
_oracle_servers_parsed: List[OracleServerConfig] = []
|
||||
|
||||
def model_post_init(self, __context) -> None:
|
||||
"""Parse ORACLE_SERVERS JSON and build server list.
|
||||
|
||||
Oracle passwords are loaded from:
|
||||
1. secrets/{server_id}.oracle_pass file (preferred, more secure)
|
||||
2. password field in ORACLE_SERVERS JSON (fallback)
|
||||
"""
|
||||
servers = []
|
||||
secrets_dir = Path(__file__).parent / "secrets"
|
||||
|
||||
if self.oracle_servers:
|
||||
# Parse multi-server JSON configuration
|
||||
try:
|
||||
servers_data = json.loads(self.oracle_servers)
|
||||
if not isinstance(servers_data, list):
|
||||
raise ValueError("ORACLE_SERVERS must be a JSON array")
|
||||
|
||||
for server_data in servers_data:
|
||||
server_id = server_data.get("id", "default")
|
||||
|
||||
# Try to load password from secrets file
|
||||
pass_file = secrets_dir / f"{server_id}.oracle_pass"
|
||||
if pass_file.exists():
|
||||
server_data["password"] = pass_file.read_text().strip()
|
||||
logger.debug(f"Loaded Oracle password for '{server_id}' from {pass_file}")
|
||||
elif "password" not in server_data:
|
||||
logger.warning(f"No password found for server '{server_id}' - check secrets/{server_id}.oracle_pass")
|
||||
|
||||
servers.append(OracleServerConfig(**server_data))
|
||||
|
||||
logger.info(f"Loaded {len(servers)} Oracle servers from ORACLE_SERVERS config")
|
||||
for srv in servers:
|
||||
logger.info(f" - {srv.id}: {srv.name} ({srv.host}:{srv.port})")
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Failed to parse ORACLE_SERVERS JSON: {e}")
|
||||
raise ValueError(f"Invalid ORACLE_SERVERS JSON format: {e}")
|
||||
else:
|
||||
# Backward compatibility: build default server from legacy config
|
||||
if self.oracle_user:
|
||||
# Try to load password from secrets file
|
||||
password = self.oracle_password
|
||||
pass_file = secrets_dir / "default.oracle_pass"
|
||||
if pass_file.exists():
|
||||
password = pass_file.read_text().strip()
|
||||
logger.debug(f"Loaded Oracle password from {pass_file}")
|
||||
|
||||
default_server = OracleServerConfig(
|
||||
id="default",
|
||||
name="Default Server",
|
||||
host=self.oracle_host,
|
||||
port=self.oracle_port,
|
||||
user=self.oracle_user,
|
||||
password=password,
|
||||
sid=self.oracle_sid,
|
||||
)
|
||||
servers.append(default_server)
|
||||
logger.info("Using legacy single-server Oracle configuration (ORACLE_USER/HOST/etc)")
|
||||
logger.info(f" - default: {default_server.host}:{default_server.port}/{default_server.sid}")
|
||||
|
||||
object.__setattr__(self, '_oracle_servers_parsed', servers)
|
||||
|
||||
def get_oracle_servers(self) -> List[OracleServerConfig]:
|
||||
"""Get list of configured Oracle servers."""
|
||||
return self._oracle_servers_parsed
|
||||
|
||||
def get_oracle_server(self, server_id: str) -> Optional[OracleServerConfig]:
|
||||
"""Get a specific Oracle server by ID."""
|
||||
for server in self._oracle_servers_parsed:
|
||||
if server.id == server_id:
|
||||
return server
|
||||
return None
|
||||
|
||||
def get_default_oracle_server(self) -> Optional[OracleServerConfig]:
|
||||
"""Get the default Oracle server (first in list or 'default')."""
|
||||
if not self._oracle_servers_parsed:
|
||||
return None
|
||||
# Try to find server with id='default', otherwise return first
|
||||
for server in self._oracle_servers_parsed:
|
||||
if server.id == "default":
|
||||
return server
|
||||
return self._oracle_servers_parsed[0]
|
||||
|
||||
# ============================================================================
|
||||
# JWT AUTHENTICATION (Shared by all modules)
|
||||
# ============================================================================
|
||||
jwt_secret_key: str = "change-me-in-production"
|
||||
jwt_algorithm: str = "HS256"
|
||||
access_token_expire_minutes: int = 30
|
||||
refresh_token_expire_days: int = 7
|
||||
|
||||
# ============================================================================
|
||||
# SESSION SECURITY - EMAIL 2FA (Telegram module)
|
||||
# ============================================================================
|
||||
auth_session_secret: str = "change-me-in-production"
|
||||
|
||||
# ============================================================================
|
||||
# CORS
|
||||
# ============================================================================
|
||||
cors_origins: str = "http://localhost:3000,http://localhost:5173"
|
||||
|
||||
# ============================================================================
|
||||
# REPORTS MODULE - CACHE CONFIGURATION
|
||||
# ============================================================================
|
||||
reports_cache_enabled: bool = True
|
||||
reports_cache_type: str = "hybrid"
|
||||
reports_cache_sqlite_path: str = "./data/cache/roa2web_cache.db"
|
||||
reports_cache_memory_max_size: int = 1000
|
||||
reports_cache_default_ttl: int = 900
|
||||
|
||||
# Cache TTL per type (seconds)
|
||||
reports_cache_ttl_schema: int = 86400
|
||||
reports_cache_ttl_companies: int = 1800
|
||||
reports_cache_ttl_dashboard_summary: int = 1800
|
||||
reports_cache_ttl_dashboard_trends: int = 1800
|
||||
reports_cache_ttl_invoices: int = 600
|
||||
reports_cache_ttl_invoices_summary: int = 900
|
||||
reports_cache_ttl_treasury: int = 600
|
||||
|
||||
# Cache maintenance
|
||||
reports_cache_cleanup_interval: int = 3600
|
||||
reports_cache_auto_invalidate: bool = False
|
||||
reports_cache_check_interval: int = 300
|
||||
reports_cache_track_performance: bool = True
|
||||
reports_cache_benchmark_on_startup: bool = False
|
||||
|
||||
# ============================================================================
|
||||
# DATA ENTRY MODULE - CONFIGURATION
|
||||
# ============================================================================
|
||||
data_entry_sqlite_database_path: str = "data/receipts/receipts.db"
|
||||
data_entry_upload_path: str = "data/receipts/uploads"
|
||||
data_entry_max_upload_size_mb: int = 10
|
||||
data_entry_allowed_mime_types: List[str] = [
|
||||
"image/jpeg",
|
||||
"image/png",
|
||||
"image/gif",
|
||||
"image/webp",
|
||||
"application/pdf",
|
||||
]
|
||||
|
||||
# ============================================================================
|
||||
# TELEGRAM MODULE - BOT CONFIGURATION
|
||||
# ============================================================================
|
||||
telegram_bot_token: str = ""
|
||||
telegram_smtp_host: str = ""
|
||||
telegram_smtp_port: int = 587
|
||||
telegram_smtp_user: str = ""
|
||||
telegram_smtp_password: str = ""
|
||||
telegram_smtp_from_email: str = ""
|
||||
telegram_smtp_from_name: str = "ROA2WEB"
|
||||
telegram_smtp_use_tls: bool = True
|
||||
telegram_email_max_retries: int = 3
|
||||
telegram_email_retry_delay: float = 2.0
|
||||
telegram_sqlite_database_path: str = "data/telegram/telegram.db"
|
||||
|
||||
class Config:
|
||||
env_file = ".env"
|
||||
env_file_encoding = "utf-8"
|
||||
extra = "ignore"
|
||||
case_sensitive = False
|
||||
|
||||
# ============================================================================
|
||||
# COMPUTED PROPERTIES
|
||||
# ============================================================================
|
||||
|
||||
@property
|
||||
def oracle_dsn(self) -> str:
|
||||
"""Get Oracle DSN string."""
|
||||
return f"{self.oracle_host}:{self.oracle_port}/{self.oracle_sid}"
|
||||
|
||||
@property
|
||||
def cors_origins_list(self) -> List[str]:
|
||||
"""Get CORS origins as list."""
|
||||
return [origin.strip() for origin in self.cors_origins.split(",")]
|
||||
|
||||
# Data Entry properties
|
||||
@property
|
||||
def data_entry_database_url(self) -> str:
|
||||
"""Get SQLite database URL for async (Data Entry)."""
|
||||
# Resolve to absolute path for Windows/IIS compatibility
|
||||
abs_path = Path(self.data_entry_sqlite_database_path).resolve()
|
||||
return f"sqlite+aiosqlite:///{abs_path}"
|
||||
|
||||
@property
|
||||
def data_entry_sync_database_url(self) -> str:
|
||||
"""Get SQLite database URL for sync operations (Alembic)."""
|
||||
# Resolve to absolute path for Windows/IIS compatibility
|
||||
abs_path = Path(self.data_entry_sqlite_database_path).resolve()
|
||||
return f"sqlite:///{abs_path}"
|
||||
|
||||
@property
|
||||
def data_entry_upload_path_resolved(self) -> Path:
|
||||
"""Get resolved upload path."""
|
||||
path = Path(self.data_entry_upload_path)
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
@property
|
||||
def data_entry_max_upload_size_bytes(self) -> int:
|
||||
"""Get max upload size in bytes."""
|
||||
return self.data_entry_max_upload_size_mb * 1024 * 1024
|
||||
|
||||
# Reports cache properties
|
||||
@property
|
||||
def reports_cache_sqlite_path_resolved(self) -> Path:
|
||||
"""Get resolved cache SQLite path."""
|
||||
path = Path(self.reports_cache_sqlite_path)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
# Telegram properties
|
||||
@property
|
||||
def telegram_sqlite_path_resolved(self) -> Path:
|
||||
"""Get resolved Telegram SQLite path."""
|
||||
path = Path(self.telegram_sqlite_database_path)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
|
||||
@lru_cache()
|
||||
def get_settings() -> UnifiedSettings:
|
||||
"""Get cached settings instance."""
|
||||
return UnifiedSettings()
|
||||
|
||||
|
||||
# Convenience instance
|
||||
settings = get_settings()
|
||||
@@ -1,45 +0,0 @@
|
||||
# Backend Runtime Data
|
||||
|
||||
This directory contains runtime data generated by the unified backend.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
data/
|
||||
├── cache/ # Reports module cache (hybrid L1+L2)
|
||||
│ └── *.db # SQLite L2 cache database
|
||||
├── receipts/ # Data Entry module data
|
||||
│ ├── *.db # SQLite receipts database
|
||||
│ └── uploads/ # User-uploaded files (receipts, attachments)
|
||||
└── telegram/ # Telegram bot data
|
||||
└── *.db # SQLite bot auth/session database
|
||||
```
|
||||
|
||||
## Git Behavior
|
||||
|
||||
- **Ignored**: All `*.db` files and `uploads/` contents
|
||||
- **Committed**: Only `.gitkeep` files to preserve directory structure
|
||||
|
||||
## Environment-Specific Databases
|
||||
|
||||
Different environments use separate databases:
|
||||
|
||||
- **Development** (`.env.prod`):
|
||||
- Cache: `roa2web_cache.db`
|
||||
- Receipts: `receipts_dev.db`
|
||||
- Telegram: `telegram.db`
|
||||
|
||||
- **Test** (`.env.test`):
|
||||
- Cache: `roa2web_cache_test.db`
|
||||
- Receipts: `receipts_test.db`
|
||||
- Telegram: `telegram_test.db`
|
||||
|
||||
- **Production** (`.env.prod`):
|
||||
- Cache: `roa2web_cache_prod.db`
|
||||
- Receipts: `receipts_prod.db`
|
||||
- Telegram: `telegram_prod.db`
|
||||
|
||||
## Auto-Created
|
||||
|
||||
All databases and directories are created automatically on first run.
|
||||
No manual setup required.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 299 KiB |
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 323 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 299 KiB |
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user