97 lines
3.0 KiB
Python
97 lines
3.0 KiB
Python
from contextlib import asynccontextmanager
|
|
import logging
|
|
import os
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.staticfiles import StaticFiles
|
|
from starlette.datastructures import Headers
|
|
from starlette.middleware.base import BaseHTTPMiddleware
|
|
from starlette.middleware.sessions import SessionMiddleware
|
|
import sqladmin
|
|
|
|
from app.admin import mount_admin
|
|
from app.api.v1.routes import router as v1_router
|
|
from app.core.config import settings
|
|
from app.db.init_db import init_db
|
|
|
|
# Configure logging for better error visibility
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
)
|
|
logging.getLogger("uvicorn.error").setLevel(logging.DEBUG)
|
|
logging.getLogger("uvicorn.access").setLevel(logging.INFO)
|
|
logging.getLogger("sqladmin").setLevel(logging.DEBUG)
|
|
logging.getLogger("sqlalchemy.engine").setLevel(logging.INFO)
|
|
|
|
|
|
class ForceHTTPSMiddleware(BaseHTTPMiddleware):
|
|
async def dispatch(self, request, call_next):
|
|
request.scope["scheme"] = "https"
|
|
response = await call_next(request)
|
|
return response
|
|
|
|
|
|
class ForwardedProtoMiddleware:
|
|
def __init__(self, app):
|
|
self.app = app
|
|
|
|
async def __call__(self, scope, receive, send):
|
|
if scope["type"] in {"http", "websocket"}:
|
|
headers = Headers(scope=scope)
|
|
forwarded_proto = headers.get("x-forwarded-proto")
|
|
if forwarded_proto:
|
|
proto = forwarded_proto.split(",", 1)[0].strip()
|
|
if proto:
|
|
new_scope = dict(scope)
|
|
new_scope["scheme"] = proto
|
|
return await self.app(new_scope, receive, send)
|
|
|
|
return await self.app(scope, receive, send)
|
|
|
|
|
|
origins = [
|
|
"http://localhost:8040",
|
|
"https://ai.sriphat.com",
|
|
"http://ai.sriphat.com",
|
|
]
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(_: FastAPI):
|
|
init_db()
|
|
yield
|
|
|
|
|
|
sqladmin_dir = os.path.dirname(sqladmin.__file__)
|
|
statics_path = os.path.join(sqladmin_dir, "statics")
|
|
|
|
app = FastAPI(title=settings.APP_NAME, root_path=settings.ROOT_PATH, lifespan=lifespan)
|
|
|
|
# Add exception handler to log all errors with traceback
|
|
@app.exception_handler(Exception)
|
|
async def global_exception_handler(request, exc):
|
|
import traceback
|
|
logging.error(f"Unhandled exception: {exc}")
|
|
logging.error(traceback.format_exc())
|
|
from starlette.responses import JSONResponse
|
|
return JSONResponse(
|
|
status_code=500,
|
|
content={"detail": "Internal server error", "error": str(exc)}
|
|
)
|
|
app.add_middleware(ForceHTTPSMiddleware)
|
|
app.add_middleware(SessionMiddleware, secret_key=settings.ADMIN_SECRET_KEY)
|
|
app.add_middleware(ForwardedProtoMiddleware)
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=origins,
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
app.include_router(v1_router)
|
|
app.mount("/admin/statics", StaticFiles(directory=statics_path), name="admin_statics")
|
|
app.mount("/apiservice/admin/statics", StaticFiles(directory=statics_path), name="proxy_admin_statics")
|
|
mount_admin(app)
|