import logging import os from contextlib import asynccontextmanager from pathlib import Path from fastapi import FastAPI, Request from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles from slowapi import Limiter, _rate_limit_exceeded_handler from slowapi.errors import RateLimitExceeded from slowapi.util import get_remote_address from config import settings from database import Base, engine from routes.download import router as download_router from routes.upload import router as upload_router from utils.cleanup import start_scheduler logging.basicConfig( level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s — %(message)s", ) FRONTEND = Path(__file__).parent.parent / "frontend" @asynccontextmanager async def lifespan(app: FastAPI): os.makedirs(settings.storage_path, exist_ok=True) Base.metadata.create_all(bind=engine) scheduler = start_scheduler() yield scheduler.shutdown(wait=False) limiter = Limiter(key_func=get_remote_address) app = FastAPI( title="dropLake", lifespan=lifespan, docs_url=None, # no Swagger UI exposed redoc_url=None, openapi_url=None, ) app.state.limiter = limiter app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler) # API routes app.include_router(upload_router, prefix="/api") app.include_router(download_router, prefix="/api") # Static assets (CSS, JS) app.mount("/static", StaticFiles(directory=str(FRONTEND / "static")), name="static") @app.get("/") async def serve_index(): return FileResponse(str(FRONTEND / "index.html")) @app.get("/s/{share_id}") async def serve_share_page(share_id: str): # The JS on this page reads share_id from the URL path and the key from the fragment return FileResponse(str(FRONTEND / "s.html")) @app.get("/health") async def health(): return {"status": "ok"}