Critical fixes: - C-01: Pass user_id to _mark_sent/_already_sent (ntfy crash) - C-02: Align frontend HTTP methods with backend routes (PATCH->PUT, DELETE->POST, fix reset-password/enforce-mfa/disable-mfa paths) - C-03: Add X-Requested-With to CORS allow_headers - C-04: Replace scalar_one_or_none with func.count for auth/status Warning fixes: - W-01: Batch audit log into same transaction in create_user, setup, register - W-02: Extract users array from UserListResponse wrapper in useAdminUsers - W-03: Update password hint from "8 chars" to "12 chars" in CreateUserDialog - W-04: Remove password input from reset flow, show returned temp password - W-06: Remove unused actor_alias variable in admin_dashboard - W-07: Resolve usernames in dashboard audit entries via JOIN, remove ip_address column from recent_logins (not tracked on User model) Suggestions applied: - S-01/S-06: Add extra="forbid" to all admin mutation schemas - S-04: Add ondelete="SET NULL" to audit_log.actor_user_id FK - S-05: Improve registration error message for better UX Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
84 lines
3.2 KiB
Python
84 lines
3.2 KiB
Python
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from contextlib import asynccontextmanager
|
|
|
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
|
|
|
from app.config import settings
|
|
from app.database import engine
|
|
from app.routers import auth, todos, events, calendars, reminders, projects, people, locations, settings as settings_router, dashboard, weather, event_templates
|
|
from app.routers import totp, admin
|
|
from app.jobs.notifications import run_notification_dispatch
|
|
|
|
# Import models so Alembic's autogenerate can discover them
|
|
from app.models import user as _user_model # noqa: F401
|
|
from app.models import session as _session_model # noqa: F401
|
|
from app.models import totp_usage as _totp_usage_model # noqa: F401
|
|
from app.models import backup_code as _backup_code_model # noqa: F401
|
|
from app.models import system_config as _system_config_model # noqa: F401
|
|
from app.models import audit_log as _audit_log_model # noqa: F401
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
scheduler = AsyncIOScheduler()
|
|
scheduler.add_job(
|
|
run_notification_dispatch,
|
|
"interval",
|
|
minutes=1,
|
|
id="ntfy_dispatch",
|
|
max_instances=1, # prevent overlap if a run takes longer than 60s
|
|
)
|
|
scheduler.start()
|
|
yield
|
|
scheduler.shutdown(wait=False)
|
|
await engine.dispose()
|
|
|
|
|
|
_is_dev = settings.ENVIRONMENT == "development"
|
|
|
|
app = FastAPI(
|
|
title="UMBRA API",
|
|
description="Backend API for UMBRA application",
|
|
version="1.0.0",
|
|
lifespan=lifespan,
|
|
docs_url="/docs" if _is_dev else None,
|
|
redoc_url="/redoc" if _is_dev else None,
|
|
openapi_url="/openapi.json" if _is_dev else None,
|
|
)
|
|
|
|
# CORS configuration
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=[o.strip() for o in settings.CORS_ORIGINS.split(",") if o.strip()],
|
|
allow_credentials=True,
|
|
allow_methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
|
|
allow_headers=["Content-Type", "Authorization", "Cookie", "X-Requested-With"],
|
|
)
|
|
|
|
# Include routers with /api prefix
|
|
app.include_router(auth.router, prefix="/api/auth", tags=["Authentication"])
|
|
app.include_router(todos.router, prefix="/api/todos", tags=["Todos"])
|
|
app.include_router(events.router, prefix="/api/events", tags=["Calendar Events"])
|
|
app.include_router(calendars.router, prefix="/api/calendars", tags=["Calendars"])
|
|
app.include_router(reminders.router, prefix="/api/reminders", tags=["Reminders"])
|
|
app.include_router(projects.router, prefix="/api/projects", tags=["Projects"])
|
|
app.include_router(people.router, prefix="/api/people", tags=["People"])
|
|
app.include_router(locations.router, prefix="/api/locations", tags=["Locations"])
|
|
app.include_router(settings_router.router, prefix="/api/settings", tags=["Settings"])
|
|
app.include_router(dashboard.router, prefix="/api", tags=["Dashboard"])
|
|
app.include_router(weather.router, prefix="/api/weather", tags=["Weather"])
|
|
app.include_router(event_templates.router, prefix="/api/event-templates", tags=["Event Templates"])
|
|
app.include_router(totp.router, prefix="/api/auth", tags=["TOTP MFA"])
|
|
app.include_router(admin.router, prefix="/api/admin", tags=["Admin"])
|
|
|
|
|
|
@app.get("/")
|
|
async def root():
|
|
return {"message": "UMBRA API is running"}
|
|
|
|
|
|
@app.get("/health")
|
|
async def health_check():
|
|
return {"status": "healthy"}
|