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:
222
reports-app/backend/app/main.py
Normal file
222
reports-app/backend/app/main.py
Normal 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()}
|
||||
Reference in New Issue
Block a user