"""Character library REST API — backed by SQLite via core.database. Mirrors the IndexedDB API in characters-library.js so the JS swap is mechanical: GET /api/characters → { characters: [...] } GET /api/characters/{id} → record | 404 PUT /api/characters/{id} → upsert, returns record DELETE /api/characters/{id} → { ok: true } POST /api/characters/migrate → bulk import from JS (one-time migration) """ from __future__ import annotations from fastapi import APIRouter, HTTPException, Request from core.database import char_get_all, char_get, char_put, char_delete router = APIRouter() @router.get("/api/characters") async def characters_list(): return {"characters": char_get_all()} @router.get("/api/characters/{char_id:path}") async def characters_get(char_id: str): rec = char_get(char_id) if rec is None: raise HTTPException(404, "Character not found") return rec @router.put("/api/characters/{char_id:path}") async def characters_put(char_id: str, request: Request): try: body = await request.json() except Exception: raise HTTPException(400, "Invalid JSON") body["id"] = char_id return char_put(body) @router.delete("/api/characters/{char_id:path}") async def characters_delete(char_id: str): char_delete(char_id) return {"ok": True} @router.post("/api/characters/migrate") async def characters_migrate(request: Request): """Accept a batch of character records from the browser's IndexedDB dump.""" try: body = await request.json() except Exception: raise HTTPException(400, "Invalid JSON") records = body if isinstance(body, list) else body.get("characters", []) n = 0 for rec in records: if rec.get("id"): char_put(rec) n += 1 return {"imported": n}