Portal FastAPI + 5 endpoints REST para Bootcamp Agentic AI con watsonx Orchestrate (FactorIT). Single container, Coolify-ready. - Landing brandeado FIT con formulario de registro (honeypot anti-bot) - Tokens itsdangerous para descargas (24h expiry) - 5 endpoints API: historical/available procedures, member-insights, schedule, generate-report (Jinja2 + Plotly) - SQLite con upsert-on-email para leads + log de descargas - Admin endpoints (HTTP Basic): leads.json, leads.csv, stats - 23 tests pytest pasando - Dockerfile listo para Coolify con volúmenes persistentes (/app/leads.db, /app/app/data/reports_output, /app/material) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
43 lines
1.1 KiB
Python
43 lines
1.1 KiB
Python
import csv
|
||
import io
|
||
|
||
from fastapi import APIRouter, Depends, Query
|
||
from fastapi.responses import Response
|
||
|
||
from app.db import list_leads, stats
|
||
from app.security import require_admin
|
||
|
||
router = APIRouter(prefix="/admin", tags=["admin"])
|
||
|
||
|
||
@router.get("/leads.json")
|
||
def admin_leads_json(
|
||
_user: str = Depends(require_admin),
|
||
limit: int = Query(100, ge=1, le=1000),
|
||
offset: int = Query(0, ge=0),
|
||
):
|
||
return list_leads(limit=limit, offset=offset)
|
||
|
||
|
||
@router.get("/leads.csv")
|
||
def admin_leads_csv(_user: str = Depends(require_admin)):
|
||
rows = list_leads(limit=10_000, offset=0)
|
||
buf = io.StringIO()
|
||
if rows:
|
||
writer = csv.DictWriter(buf, fieldnames=list(rows[0].keys()))
|
||
writer.writeheader()
|
||
writer.writerows(rows)
|
||
else:
|
||
buf.write("(no leads)\n")
|
||
payload = "" + buf.getvalue()
|
||
return Response(
|
||
content=payload,
|
||
media_type="text/csv; charset=utf-8",
|
||
headers={"Content-Disposition": 'attachment; filename="leads.csv"'},
|
||
)
|
||
|
||
|
||
@router.get("/stats")
|
||
def admin_stats(_user: str = Depends(require_admin)):
|
||
return stats()
|