kpi_analysis/kpi_analysis/main.py
2025-11-25 20:38:15 +07:00

160 lines
5.3 KiB
Python

"""
KPI Analysis Application
Main FastAPI application entry point
"""
from fastapi import FastAPI, Request, HTTPException, Depends
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from fastapi.middleware.cors import CORSMiddleware
from starlette.middleware.base import BaseHTTPMiddleware
from pathlib import Path
import uvicorn
import os
import logging
# Import application modules
from app.api import routes
from config.settings import settings
from app.core.database import init_db
# Initialize FastAPI app
app = FastAPI(
title="KPI Analysis Dashboard",
description="Comprehensive KPI Analysis and Reporting System",
version="1.0.0",
docs_url="/docs",
redoc_url="/redoc"
)
# Add CORS middleware with configurable origins
app.add_middleware(
CORSMiddleware,
allow_origins=settings.effective_cors_origins,
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allow_headers=["*"],
)
# Mount static files
static_dir = Path(__file__).parent / "static"
app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
# Initialize templates
from pathlib import Path
templates_dir = Path(__file__).parent / "templates"
templates = Jinja2Templates(directory=str(templates_dir))
# Include API routes
app.include_router(routes.router, prefix="/api")
# Authentication dependency
security = HTTPBearer()
class CookieCleanupMiddleware(BaseHTTPMiddleware):
"""Middleware to clear conflicting cookies from other applications"""
async def dispatch(self, request: Request, call_next):
# List of cookies that might conflict with Odoo or other applications
conflicting_cookies = [
'session_id', 'frontend_lang', '_ga', '_ga_NMT50XL57M',
'cids', 'tz', 'irispid', 'session', 'csrftoken'
]
# Check if any conflicting cookies are present
has_conflicts = any(cookie in request.cookies for cookie in conflicting_cookies)
if has_conflicts and request.url.path.startswith('/api/auth/'):
logger = logging.getLogger(__name__)
logger.info(f"Cleaning conflicting cookies for request to {request.url.path}")
logger.info(f"Found conflicting cookies: {[c for c in conflicting_cookies if c in request.cookies]}")
response = await call_next(request)
# For authentication endpoints, set headers to clear conflicting cookies
if (has_conflicts and request.url.path.startswith('/api/auth/')):
if not isinstance(response, JSONResponse):
response = JSONResponse(content={"status": "processing"})
# Clear conflicting cookies
for cookie_name in conflicting_cookies:
response.set_cookie(
key=cookie_name,
value="",
max_age=0,
expires=0,
path="/",
domain=None,
secure=False,
httponly=False,
samesite="lax"
)
return response
# Add cookie cleanup middleware
app.add_middleware(CookieCleanupMiddleware)
async def require_authentication(credentials: HTTPAuthorizationCredentials = Depends(security)):
"""Require valid authentication token"""
from config.settings import settings
import jwt
try:
payload = jwt.decode(
credentials.credentials,
settings.secret_key,
algorithms=["HS256"]
)
return payload
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Token has expired")
except jwt.JWTError:
raise HTTPException(status_code=401, detail="Invalid token")
async def get_current_user(payload: dict = Depends(require_authentication)):
"""Get current authenticated user"""
return payload
@app.on_event("startup")
async def startup_event():
"""Initialize application on startup"""
await init_db()
print("🚀 KPI Analysis Application Started")
print(f"📊 Dashboard available at: http://localhost:8000")
print(f"📚 API Documentation at: http://localhost:8000/docs")
@app.get("/", response_class=HTMLResponse)
async def root(request: Request):
"""Redirect to login page"""
return RedirectResponse(url="/login", status_code=302)
@app.get("/login", response_class=HTMLResponse)
async def login_page(request: Request):
"""Public login page"""
return templates.TemplateResponse("login.html", {"request": request})
@app.get("/dashboard", response_class=HTMLResponse)
async def dashboard(request: Request):
"""Main dashboard page - authentication handled via frontend token"""
return templates.TemplateResponse("dashboard.html", {"request": request})
@app.get("/health")
async def health_check():
"""Health check endpoint"""
return {
"status": "healthy",
"service": "KPI Analysis Dashboard",
"version": "1.0.0"
}
if __name__ == "__main__":
uvicorn.run(
"kpi_analysis.main:app",
host="0.0.0.0",
port=8000,
reload=True,
log_level="info"
)