# ============================================================ # main.py # ------------------------------------------------------------ # Titan API Gateway — FastAPI application entry point. # # V2 Changes: # ★ APScheduler lifespan — auto-publishes Immersion story # every hour (00:05, 01:05, 02:05, … UTC) — 24 stories/day. # No manual cron job needed. # # Responsibilities: # 1. Create the FastAPI app instance with metadata. # 2. Configure CORS + SharedArrayBuffer security headers. # 3. Include all feature routers (Cinema, Immersion). # 4. Start/stop the Immersion story scheduler via lifespan. # 5. Provide a public /health endpoint for HF Space monitoring. # 6. Launch uvicorn when executed directly. # # Hugging Face Spaces notes: # - HF expects the app to bind on 0.0.0.0:7860. # - Set INTERNAL_BASE_URL=http://localhost:7860 in HF Secrets. # - Set TITAN_API_KEY in HF Secrets (same key as X-Titan-Key). # ============================================================ import logging import os from contextlib import asynccontextmanager import uvicorn from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import RedirectResponse from api.cinema import router as cinema_router from api.immersion import router as immersion_router from api.chat import router as chat_router # <--- (الإضافة الأولى هنا) from api.scheduler import start_scheduler, stop_scheduler # ============================================================ # Logging # ============================================================ logging.basicConfig( level=logging.INFO, format="%(asctime)s | %(levelname)-8s | %(name)s — %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) logger = logging.getLogger("titan.main") # Paths exempt from COEP/COOP headers — HF health checker hits these # from a different origin; require-corp would cause silent failures. _HEALTH_PATHS = {"/", "/health"} # ============================================================ # Lifespan — scheduler start/stop # ============================================================ @asynccontextmanager async def lifespan(app: FastAPI): """Start Immersion auto-scheduler on startup, stop on shutdown.""" logger.info("🚀 Titan Gateway starting up…") start_scheduler() yield logger.info("🛑 Titan Gateway shutting down…") stop_scheduler() # ============================================================ # FastAPI application # ============================================================ app = FastAPI( title="Titan API Gateway", description=( "Master API Gateway for Titan applications. " "Provides AI-powered transcription (Groq Whisper) and translation (Google Gemini/Gemma) " "services with built-in key rotation, rate-limit handling, and zero disk I/O. " "Titan Immersion stories are auto-published every 2 hours via APScheduler. " "All endpoints require the `X-Titan-Key` header for authentication." ), version="23.0.0", docs_url="/docs", redoc_url="/redoc", lifespan=lifespan, ) # ============================================================ # Security Headers Middleware # Required for FFmpeg.wasm SharedArrayBuffer support. # ============================================================ @app.middleware("http") async def add_security_headers(request: Request, call_next): response = await call_next(request) if request.url.path not in _HEALTH_PATHS: response.headers["Cross-Origin-Opener-Policy"] = "same-origin" response.headers["Cross-Origin-Embedder-Policy"] = "require-corp" return response # ============================================================ # CORS # ============================================================ ALLOWED_ORIGINS: list[str] = os.environ.get("CORS_ORIGINS", "*").split(",") app.add_middleware( CORSMiddleware, allow_origins=ALLOWED_ORIGINS, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], expose_headers=["X-Request-ID"], ) # ============================================================ # Routers # ============================================================ app.include_router(cinema_router) app.include_router(immersion_router) app.include_router(chat_router) # <--- (الإضافة التانية هنا) # ============================================================ # Health check (no auth — used by HF Spaces + load balancers) # ============================================================ @app.get( "/health", tags=["System"], summary="Gateway health check", ) async def health_check() -> dict: return { "status": "ok", "gateway": "Titan API Gateway", "version": "23.0.0", } # ============================================================ # Root → docs # ============================================================ # ============================================================ # Root → Health Check (To satisfy Hugging Face) # ============================================================ @app.get("/", tags=["System"]) async def root() -> dict: return { "status": "ok", "message": "Titan Gateway V23.0 is Alive and Ready!", "docs_url": "/docs", "health": "All systems operational" } # ============================================================ # Entry point # ============================================================ if __name__ == "__main__": uvicorn.run( "main:app", host="0.0.0.0", port=7860, reload=os.environ.get("RELOAD", "true").lower() == "true", log_level="info", workers=1, )