"""어드민 API. 솔루션 API(:9800)와 프로세스·포트가 갈린다. 도메인 코드는 solution/backend 것을 PYTHONPATH 로 쓴다 — admin 전용 라우터가 0개라 (전부 place·fact) 새로 쓰면 같은 테이블을 두 벌 구현하는 것뿐이다. 경로 접두어가 아니라 포트를 가른 이유: 접두어는 같은 프로세스라 사장님이 닿는 서버에 내부 엔드포인트가 존재한다. 포트를 가르면 아예 없다. """ import time from fastapi import Depends, FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.gzip import GZipMiddleware from common.database.db_session_manager import DB_SESSION_MNG from common.logger import LOG from common.utils.gtime import GTime from config.server_configs import web_server_config from router.v1.validator.dependencies import RequireDeveloper import router.v1.auth.account import router.v1.fact.fact import router.v1.job.job import router.v1.local.local import router.v1.place.place import router.v1.site.review_admin import router.v1.site.site from contextlib import asynccontextmanager API_SERVER_START_TIME = GTime.UTCStr() @asynccontextmanager async def lifespan(app: FastAPI): # 크론은 :9800 담당. 여기서도 돌리면 같은 시각에 중복 실행된다. yield await DB_SESSION_MNG.dispose_all() app = FastAPI(title="Web4Ai Admin API", lifespan=lifespan) def _origins(*values: str) -> list[str]: seen: list[str] = [] for value in values: for origin in value.split(","): origin = origin.strip().rstrip("/") if origin and origin not in seen: seen.append(origin) return seen ALLOWED_ORIGINS = _origins(web_server_config.client_url) app.add_middleware( CORSMiddleware, allow_origins=ALLOWED_ORIGINS, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.add_middleware(GZipMiddleware, minimum_size=1000) @app.middleware("http") async def log_time(request: Request, call_next): start_time = time.time() response = await call_next(request) LOG.d(f"[admin] {response.status_code} {request.method} {request.url.path} - {time.time() - start_time:.4f}s") return response @app.get(path="/healthz") async def healthz(): return API_SERVER_START_TIME # auth 만 게이트 밖 — 로그인까지 막으면 아무도 못 들어온다. app.include_router(router.v1.auth.account.router) # 나머지 전부 role >= DEVELOPER. _gate = [Depends(RequireDeveloper)] app.include_router(router.v1.place.place.router, dependencies=_gate) app.include_router(router.v1.fact.fact.router, dependencies=_gate) app.include_router(router.v1.job.job.router, dependencies=_gate) app.include_router(router.v1.site.site.router, dependencies=_gate) app.include_router(router.v1.local.local.router, dependencies=_gate) app.include_router(router.v1.site.review_admin.router, dependencies=_gate)