import hashlib from contextlib import asynccontextmanager from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel from database import add_feedback, check_rate_limit, get_stats, init_db class FeedbackIn(BaseModel): url: str choice: str timestamp: str RATE_LIMIT = 5 RATE_WINDOW = 3600 @asynccontextmanager async def lifespan(app: FastAPI): await init_db() yield app = FastAPI(lifespan=lifespan) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) def get_client_ip(request: Request) -> str: cf_ip = request.headers.get("cf-connecting-ip") if cf_ip: return cf_ip forwarded = request.headers.get("x-forwarded-for") if forwarded: return forwarded.split(",")[0].strip() return request.client.host or "unknown" @app.post("/api/feedback") async def receive_feedback(body: FeedbackIn, request: Request): ip = get_client_ip(request) ip_hash = hashlib.sha256(ip.encode()).hexdigest() if not await check_rate_limit(ip_hash, RATE_LIMIT, RATE_WINDOW): await add_feedback(ip_hash, body.url, body.choice, body.timestamp) return {"ok": True} @app.get("/api/feedback/stats") async def stats(): return await get_stats() @app.get("/api/feedback/health") async def health(): return {"status": "ok"} if __name__ == "__main__": import uvicorn uvicorn.run("main:app", host="0.0.0.0", port=8005)