feat(01-03): API routers (calendar, generate, export, settings) e wiring main.py

- schemas/settings.py: Settings pydantic model con api_key, llm_model, nicchie_attive, tono
- routers/calendar.py: POST /api/calendar/generate, GET /api/calendar/formats
- routers/generate.py: POST /api/generate/bulk (202 + job_id), GET /job/{job_id}/status (polling), GET /job/{job_id}, POST /single
- routers/export.py: GET /api/export/{job_id}/csv (originale), POST /api/export/{job_id}/csv (modifiche inline)
- routers/settings.py: GET /api/settings/status (api_key_configured), GET /api/settings, PUT /api/settings
- main.py: include_router x4 PRIMA di SPAStaticFiles, copia prompt default al primo avvio
This commit is contained in:
Michele
2026-03-08 02:14:17 +01:00
parent 083621afd3
commit e06edde4ef
6 changed files with 694 additions and 1 deletions

View File

@@ -5,6 +5,7 @@ It is passed only via Uvicorn's --root-path flag at runtime to avoid the
double-path bug (Pitfall #4).
"""
import shutil
from contextlib import asynccontextmanager
from pathlib import Path
@@ -13,6 +14,7 @@ from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from backend.config import CAMPAIGNS_PATH, CONFIG_PATH, OUTPUTS_PATH, PROMPTS_PATH
from backend.routers import calendar, export, generate, settings
# ---------------------------------------------------------------------------
@@ -35,11 +37,27 @@ class SPAStaticFiles(StaticFiles):
# Startup / shutdown lifecycle
# ---------------------------------------------------------------------------
# Directory dei prompt di default (inclusa nel source)
_DEFAULT_PROMPTS_DIR = Path(__file__).parent / "data" / "prompts"
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Create required data directories on startup if they do not exist."""
"""Create required data directories on startup if they do not exist.
Also copies default prompts to PROMPTS_PATH on first run (when empty).
"""
# Crea directory dati
for directory in (PROMPTS_PATH, OUTPUTS_PATH, CAMPAIGNS_PATH, CONFIG_PATH):
directory.mkdir(parents=True, exist_ok=True)
# Copia prompt default al primo avvio (se PROMPTS_PATH è vuota)
if _DEFAULT_PROMPTS_DIR.exists() and not any(PROMPTS_PATH.glob("*.txt")):
for prompt_file in _DEFAULT_PROMPTS_DIR.glob("*.txt"):
dest = PROMPTS_PATH / prompt_file.name
if not dest.exists():
shutil.copy2(prompt_file, dest)
yield
# Nothing to clean up on shutdown
@@ -68,6 +86,13 @@ async def health() -> dict:
return {"status": "ok"}
# Include all API routers — ORDER MATTERS (registered before SPA catch-all)
app.include_router(calendar.router)
app.include_router(generate.router)
app.include_router(export.router)
app.include_router(settings.router)
# ---------------------------------------------------------------------------
# SPA catch-all mount (MUST be last — catches everything not matched above)
# ---------------------------------------------------------------------------