160 lines
5.3 KiB
Python
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"
|
|
) |