112 lines
4.3 KiB
Python
112 lines
4.3 KiB
Python
#!/usr/bin/env python3
|
|
import os
|
|
import logging
|
|
|
|
from fastapi import FastAPI, HTTPException, Request, status
|
|
from fastapi.staticfiles import StaticFiles
|
|
from fastapi.templating import Jinja2Templates
|
|
from starlette.middleware.sessions import SessionMiddleware
|
|
from starlette.config import Config
|
|
from starlette.middleware.trustedhost import TrustedHostMiddleware
|
|
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware
|
|
from pathlib import Path
|
|
|
|
from app.database import init_db
|
|
from app.config import settings
|
|
from app.utils.config_validator import check_all_configs
|
|
|
|
# Import both the traditional and new routers - we'll keep both available for compatibility
|
|
from app.frontend.router import router as frontend_router_original
|
|
from app.api.router import router as api_router_original
|
|
from app.auth import router as auth_router
|
|
|
|
# Import also the organized router modules
|
|
from app.frontend.router import router as frontend_router_organized
|
|
from app.api.router import router as api_router_organized
|
|
|
|
# Load configuration from .env for the session key
|
|
config = Config(".env")
|
|
SESSION_SECRET = config(
|
|
"SESSION_SECRET",
|
|
default="YOUR_DEFAULT_SESSION_SECRET_MUST_BE_32_CHARS_OR_MORE"
|
|
)
|
|
|
|
app = FastAPI(title="DocuNova")
|
|
|
|
# 1) Session Middleware (for request.session to work)
|
|
app.add_middleware(SessionMiddleware, secret_key=SESSION_SECRET)
|
|
|
|
# 2) Respect the X-Forwarded-* headers from Traefik
|
|
app.add_middleware(ProxyHeadersMiddleware, trusted_hosts="*")
|
|
|
|
# 3) (Optional but recommended) Restrict valid hosts:
|
|
app.add_middleware(TrustedHostMiddleware, allowed_hosts=[
|
|
settings.external_hostname,
|
|
"localhost",
|
|
"127.0.0.1"
|
|
])
|
|
|
|
# Mount the static folder for CSS/JS:
|
|
frontend_static_dir = Path(__file__).parent.parent / "frontend" / "static"
|
|
app.mount("/static", StaticFiles(directory=frontend_static_dir), name="static")
|
|
|
|
@app.on_event("startup")
|
|
def on_startup():
|
|
init_db() # Create tables if they don't exist
|
|
|
|
@app.on_event("startup")
|
|
async def startup_event():
|
|
"""Run startup tasks for the application"""
|
|
# Force settings dump to log for troubleshooting
|
|
from app.utils.config_validator import dump_all_settings
|
|
dump_all_settings()
|
|
|
|
# Validate configuration
|
|
config_issues = check_all_configs()
|
|
|
|
# Log overall status
|
|
has_issues = any(config_issues['email']) or any(len(issues) > 0 for provider, issues in config_issues['storage'].items())
|
|
if has_issues:
|
|
logging.warning("Application started with configuration issues - some features may be unavailable")
|
|
else:
|
|
logging.info("Application started with valid configuration")
|
|
|
|
logging.info("Router organization note: Using route handlers from main app directory for now")
|
|
logging.info("In the future, we'll transition fully to the frontend/ and api/ submodules")
|
|
|
|
# Custom 404 - we can still return the Jinja2 template, or the old static file:
|
|
@app.exception_handler(404)
|
|
async def custom_404_handler(request: Request, exc: HTTPException):
|
|
# Serve the 404 template directly
|
|
templates = Jinja2Templates(directory=str(frontend_static_dir.parent / "templates"))
|
|
return templates.TemplateResponse(
|
|
"404.html",
|
|
{"request": request},
|
|
status_code=status.HTTP_404_NOT_FOUND
|
|
)
|
|
|
|
@app.exception_handler(500)
|
|
async def custom_500_handler(request: Request, exc: Exception):
|
|
templates = Jinja2Templates(directory=str(frontend_static_dir.parent / "templates"))
|
|
# Option 1: Keep it simple, just show a funny 500 message:
|
|
return templates.TemplateResponse(
|
|
"500.html",
|
|
{"request": request, "exc": exc},
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
)
|
|
|
|
@app.get("/test-500")
|
|
def test_500():
|
|
raise RuntimeError("Testing forced 500 error!")
|
|
|
|
# Include the routers - for now we're using the original routers
|
|
# Later we can switch to the organized router structure
|
|
app.include_router(frontend_router_original)
|
|
app.include_router(auth_router)
|
|
app.include_router(api_router_original, prefix="/api")
|
|
|
|
# Note: These are the future, organized router modules that we'll eventually switch to
|
|
# For now they are disabled to avoid duplicate route conflicts
|
|
# app.include_router(frontend_router_organized)
|
|
# app.include_router(api_router_organized, prefix="/api")
|