from contextlib import asynccontextmanager from fastapi import FastAPI from starlette.datastructures import Headers from starlette.middleware.sessions import SessionMiddleware from fastapi.middleware.cors import CORSMiddleware from starlette.middleware.base import BaseHTTPMiddleware class ForceHTTPSMiddleware(BaseHTTPMiddleware): async def dispatch(self, request, call_next): # บังคับให้ FastAPI มองว่า Request ที่เข้ามาเป็น HTTPS เสมอ # เพื่อให้ url_for() เจนลิงก์ CSS/JS เป็น https:// 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) # class RootPathStripMiddleware: # def __init__(self, app, prefix: str): # self.app = app # self.prefix = (prefix or "").rstrip("/") # async def __call__(self, scope, receive, send): # if scope["type"] in {"http", "websocket"} and self.prefix: # path = scope.get("path") or "" # new_scope = dict(scope) # new_scope["root_path"] = self.prefix # if path == self.prefix or path.startswith(self.prefix + "/"): # new_path = path[len(self.prefix) :] # new_scope["path"] = new_path if new_path else "/" # return await self.app(new_scope, receive, send) # return await self.app(scope, receive, send) 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 from fastapi.staticfiles import StaticFiles from sqladmin import Admin import os import sqladmin # รายชื่อ Origins ที่อนุญาตให้ยิง API มาหาเราได้ origins = [ "http://localhost:80400", # สำหรับตอนพัฒนา Frontend "https://ai.sriphat.com", # Domain หลักของคุณ "http://ai.sriphat.com", ] @asynccontextmanager async def lifespan(_: FastAPI): init_db() yield print(settings.ROOT_PATH, flush=True) 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) #if settings.ROOT_PATH: # app.add_middleware(RootPathStripMiddleware, prefix=settings.ROOT_PATH) app.add_middleware(ForceHTTPSMiddleware) app.add_middleware(SessionMiddleware, secret_key=settings.ADMIN_SECRET_KEY) app.add_middleware(ForwardedProtoMiddleware) 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") app.add_middleware( CORSMiddleware, allow_origins=origins, # หรือ ["*"] ถ้าต้องการอนุญาตทั้งหมด (ไม่แนะนำใน production) allow_credentials=True, # สำคัญมาก! ต้องเป็น True ถ้าหน้า Admin/API มีการใช้ Cookies/Sessions allow_methods=["*"], # อนุญาตทุก HTTP Method (GET, POST, PUT, DELETE, etc.) allow_headers=["*"], # อนุญาตทุก Headers ) mount_admin(app)