""" 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()}