Files
takeyourmeds/server/app.py
T
Frank Schwenk 88bdf6f34b feat: overhaul with modular API, agent endpoints, migrations, backup and tests
- Split monolithic app.py into server/api/ routers
- Add Pydantic schemas for input validation
- Add /agent/* API with separate AGENT_TOKEN auth
- Replace static schema.sql with versioned migrations (datenerhaltend)
- Add tools/backup_db.py for SQLite backups
- Add Dockerfile HEALTHCHECK and compose.yml AGENT_TOKEN env
- Add pytest tests for user and agent API
- Update README with API docs and deployment safety notes
- Bump version to 1.2.0
2026-09-21 12:46:44 +02:00

83 lines
2.4 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", 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")