import os import re import traceback import gradio as gr from fastapi import FastAPI, Request from fastapi.responses import RedirectResponse, PlainTextResponse from dotenv import load_dotenv from slowapi import Limiter, _rate_limit_exceeded_handler from slowapi.util import get_remote_address from slowapi.errors import RateLimitExceeded from showcase_ui import build_showcase_app from admin_ui import build_admin_app load_dotenv() ADMIN_USER = os.getenv("ADMIN_USER", "admin") ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD", "admin") # Патч: відключаємо get_api_info щоб уникнути 500 try: import gradio.blocks as _gb _gb.Blocks.get_api_info = lambda self, *a, **kw: {"named_endpoints": {}, "unnamed_endpoints": {}} print("[OK] get_api_info patched") except Exception: traceback.print_exc() limiter = Limiter(key_func=get_remote_address, default_limits=["60/minute"]) print("[..] building showcase...") app_showcase = build_showcase_app() print("[OK] showcase built") print("[..] building admin...") app_admin = build_admin_app() print("[OK] admin built") app = FastAPI() app.state.limiter = limiter app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler) BLOCKED_PATHS = ( "/config.json", "/.env", "/utils.py", "/main.py", "/admin_ui.py", "/showcase_ui.py", "/.git", ) @app.middleware("http") async def security_middleware(request: Request, call_next): path = request.url.path # Нормалізуємо подвійні слеші: //figvam/ -> /figvam/ clean_path = re.sub(r'/{2,}', '/', path) if clean_path != path: return RedirectResponse(url=clean_path, status_code=301) path_lower = path.lower() for blocked in BLOCKED_PATHS: if path_lower.startswith(blocked): return PlainTextResponse("404 Not Found", status_code=404) if ".." in path or "%2e%2e" in path: return PlainTextResponse("400 Bad Request", status_code=400) try: response = await call_next(request) except Exception: traceback.print_exc() return PlainTextResponse("500 Internal Server Error", status_code=500) response.headers["X-Content-Type-Options"] = "nosniff" response.headers["X-Frame-Options"] = "SAMEORIGIN" return response @app.get("/figvam") def redirect_admin(): return RedirectResponse(url="/figvam/") app = gr.mount_gradio_app(app, app_admin, path="/figvam", auth=(ADMIN_USER, ADMIN_PASSWORD), auth_message="Unstop Admin") app = gr.mount_gradio_app(app, app_showcase, path="/")