89 lines
2.6 KiB
Python
89 lines
2.6 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import logging
|
|
import pathlib
|
|
from contextlib import asynccontextmanager
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.responses import JSONResponse
|
|
from fastapi.staticfiles import StaticFiles
|
|
|
|
from server.ai import generate_daily_motivation, generate_roast_of_the_day
|
|
from server.api import agent, auth, config, content, health, intake, push, stats
|
|
from server.config_loader import load_meds_config
|
|
from server.db import ensure_schema, get_db
|
|
from server.scheduler import start_scheduler
|
|
from server.settings import settings
|
|
from server.static_cache import CacheControlMiddleware
|
|
|
|
NO_STORE = {"Cache-Control": "no-store"}
|
|
|
|
|
|
def _configure_logging() -> None:
|
|
fmt = logging.Formatter("%(levelname)s: %(message)s")
|
|
server_log = logging.getLogger("server")
|
|
if not server_log.handlers:
|
|
handler = logging.StreamHandler()
|
|
handler.setFormatter(fmt)
|
|
server_log.addHandler(handler)
|
|
server_log.setLevel(logging.INFO)
|
|
server_log.propagate = False
|
|
|
|
|
|
_configure_logging()
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
async def _ensure_today_ai_texts() -> None:
|
|
config = load_meds_config()
|
|
db = await get_db()
|
|
try:
|
|
await generate_daily_motivation(db, config.timezone)
|
|
await generate_roast_of_the_day(db, config.timezone)
|
|
except Exception:
|
|
logger.exception("Startup AI text generation failed")
|
|
finally:
|
|
await db.close()
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
db = await get_db()
|
|
await ensure_schema(db)
|
|
await db.close()
|
|
start_scheduler()
|
|
asyncio.create_task(_ensure_today_ai_texts())
|
|
yield
|
|
|
|
|
|
app = FastAPI(
|
|
title="TakeYourMeds",
|
|
description="Persönliche Medikamenten-PWA mit Push-Erinnerungen, Einnahme-Logging und Agent-API.",
|
|
version=settings.app_version,
|
|
lifespan=lifespan,
|
|
)
|
|
app.add_middleware(CacheControlMiddleware)
|
|
|
|
# API routers
|
|
app.include_router(auth.router)
|
|
app.include_router(config.router)
|
|
app.include_router(intake.router)
|
|
app.include_router(push.router)
|
|
app.include_router(content.router)
|
|
app.include_router(stats.router)
|
|
app.include_router(health.router)
|
|
app.include_router(agent.router)
|
|
|
|
|
|
# Backwards-compatible VAPID endpoint used by older clients
|
|
@app.get("/api/vapid-public-key")
|
|
async def vapid_public_key_legacy() -> JSONResponse:
|
|
return JSONResponse({"key": settings.vapid_public_key}, headers=NO_STORE)
|
|
|
|
|
|
# Static files — must be after API routes
|
|
PUBLIC = pathlib.Path(settings.public_dir)
|
|
if PUBLIC.exists():
|
|
app.mount("/", StaticFiles(directory=str(PUBLIC), html=True), name="static")
|