Initial commit: ROA2WEB - FastAPI + Vue.js + Telegram Bot

Modern ERP Reports Application with microservices architecture

Tech Stack:
- Backend: FastAPI + python-oracledb (Oracle DB integration)
- Frontend: Vue.js 3 + PrimeVue + Vite
- Telegram Bot: python-telegram-bot + SQLite
- Infrastructure: Shared database pool, JWT authentication, SSH tunnel

Features:
- FastAPI backend with async Oracle connection pool
- Vue.js 3 responsive frontend with PrimeVue components
- Telegram bot alternative interface
- Microservices architecture with shared components
- Complete deployment support (Linux Docker + Windows IIS)
- Comprehensive testing (Playwright E2E + pytest)

Repository Structure:
- reports-app/ - Main application (backend, frontend, telegram-bot)
- shared/ - Shared components (database pool, auth, utils)
- deployment/ - Deployment scripts (Linux & Windows)
- docs/ - Project documentation
- security/ - Security scanning and git hooks
This commit is contained in:
2025-10-25 14:55:08 +03:00
commit 6b13ffa183
237 changed files with 70035 additions and 0 deletions

View File

@@ -0,0 +1,222 @@
"""
ROA Reports API - FastAPI Backend
Aplicația principală pentru rapoarte facturi și încasări
"""
from fastapi import FastAPI, Depends
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
import sys
import os
from datetime import datetime
from dotenv import load_dotenv
# Încărcare environment variables din .env
load_dotenv()
# Configurare TNS_ADMIN pentru Oracle
tns_path = os.path.join(os.path.dirname(__file__), '../../../../app')
os.environ['TNS_ADMIN'] = tns_path
# Adăugare path pentru shared modules
sys.path.append(os.path.join(os.path.dirname(__file__), '../../../shared'))
from database.oracle_pool import oracle_pool
from auth.middleware import AuthenticationMiddleware
# from auth.routes import create_auth_router # Fixed inline
# Import routere locale
from app.routers import invoices, dashboard, treasury, companies, telegram
# Auth endpoints pentru test
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from datetime import datetime, timedelta
import jwt
import logging
logger = logging.getLogger(__name__)
# JWT Setup
JWT_SECRET = os.getenv("JWT_SECRET_KEY", "test-secret-key")
JWT_ALGORITHM = "HS256"
JWT_EXPIRE_MINUTES = 30
class LoginRequest(BaseModel):
username: str
password: str
class LoginResponse(BaseModel):
access_token: str
refresh_token: str # Added refresh token
token_type: str
user: dict
def create_auth_router():
"""Create authentication router for testing"""
auth_router = APIRouter(tags=["authentication"])
@auth_router.post("/login", response_model=LoginResponse)
async def login(credentials: LoginRequest):
"""Autentificare utilizator prin Oracle database"""
try:
async with oracle_pool.get_connection() as connection:
with connection.cursor() as cursor:
# Call verificautilizator procedure using SELECT
cursor.execute("""
SELECT pack_drepturi.verificautilizator(:username, :password)
FROM DUAL
""", {
'username': credentials.username.upper(),
'password': credentials.password
})
result = cursor.fetchone()
verification_result = result[0] if result else -1
# Check if authentication was successful
if verification_result == -1:
raise HTTPException(status_code=401, detail="Invalid username or password")
# Get user companies - first get user ID from UTILIZATORI
cursor.execute("""
SELECT ID_UTIL, UTILIZATOR
FROM UTILIZATORI
WHERE UPPER(UTILIZATOR) = :username
""", {'username': credentials.username.upper()})
user_row = cursor.fetchone()
if not user_row:
raise HTTPException(status_code=401, detail="User not found in system")
user_id = user_row[0]
# Now get companies using the correct query structure
cursor.execute("""
SELECT A.ID_FIRMA, A.FIRMA
FROM V_NOM_FIRME A
WHERE A.ID_FIRMA IN (
SELECT ID_FIRMA
FROM VDEF_UTIL_FIRME
WHERE ID_PROGRAM = 2
AND ID_UTIL = :user_id
)
ORDER BY A.FIRMA
""", {'user_id': user_id})
companies_result = cursor.fetchall()
if not companies_result:
# Don't fail login if no companies - let frontend show message
companies = []
else:
companies = [str(row[0]) for row in companies_result]
# Create JWT token with all required fields
now = datetime.utcnow()
expire = now + timedelta(minutes=JWT_EXPIRE_MINUTES)
token_data = {
"username": credentials.username, # Changed from "sub" to "username"
"user_id": user_id, # Include user_id from database
"companies": companies,
"permissions": ["read", "reports"], # Default permissions
"exp": expire,
"iat": now, # Added issued at time
"type": "access" # Added token type
}
access_token = jwt.encode(token_data, JWT_SECRET, algorithm=JWT_ALGORITHM)
# Create refresh token
refresh_expire = now + timedelta(days=7)
refresh_token_data = {
"username": credentials.username,
"user_id": user_id,
"companies": companies,
"permissions": ["read", "reports"],
"exp": refresh_expire,
"iat": now,
"type": "refresh"
}
refresh_token = jwt.encode(refresh_token_data, JWT_SECRET, algorithm=JWT_ALGORITHM)
return LoginResponse(
access_token=access_token,
refresh_token=refresh_token, # Include refresh token
token_type="bearer",
user={
"username": credentials.username,
"user_id": user_id, # Include user_id
"companies": companies,
"permissions": ["read", "reports"] # Include permissions
}
)
except Exception as e:
logger.error(f"Login error: {str(e)}")
raise HTTPException(status_code=500, detail="Internal authentication error")
return auth_router
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Lifecycle events pentru aplicație"""
# Startup
await oracle_pool.initialize()
print("[ROA Reports API] Started successfully")
yield
# Shutdown
await oracle_pool.close_pool()
print("[ROA Reports API] Stopped")
app = FastAPI(
title="ROA Reports API",
description="API pentru rapoarte ERP - facturi, încasări și alte rapoarte financiare",
version="1.0.0",
lifespan=lifespan
)
# CORS pentru frontend Vue.js
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Allow all origins for production deployment
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Authentication middleware
print("[MAIN DEBUG] Adding AuthenticationMiddleware")
app.add_middleware(
AuthenticationMiddleware,
excluded_paths=[
"/", "/docs", "/health", "/api/auth/login", "/redoc", "/openapi.json",
"/api/telegram/auth/verify-user", # Public endpoint for Telegram bot
"/api/telegram/auth/refresh-token", # Public endpoint for token refresh
"/api/telegram/health" # Health check for Telegram router
]
)
print("[MAIN DEBUG] AuthenticationMiddleware added - FRESH RESTART - AUTH FIX APPLIED")
# Include routere with /api prefix
auth_router = create_auth_router()
app.include_router(auth_router, prefix="/api/auth", tags=["authentication"])
app.include_router(companies.router, prefix="/api/companies", tags=["companies"])
app.include_router(invoices.router, prefix="/api/invoices", tags=["invoices"])
app.include_router(dashboard.router, prefix="/api/dashboard", tags=["dashboard"])
app.include_router(treasury.router, prefix="/api/treasury", tags=["treasury"])
app.include_router(telegram.router, prefix="/api/telegram", tags=["telegram"])
@app.get("/")
async def root():
print("[MAIN DEBUG] Root endpoint accessed")
return {"message": "ROA Reports API", "version": "1.0.0", "status": "running"}
@app.get("/health")
async def health_check():
# Test database connection
try:
async with oracle_pool.get_connection() as conn:
with conn.cursor() as cursor:
cursor.execute("SELECT 1 FROM DUAL")
return {"api": "healthy", "database": "connected", "timestamp": datetime.utcnow().isoformat()}
except Exception as e:
return {"api": "healthy", "database": f"error: {str(e)}", "timestamp": datetime.utcnow().isoformat()}