feat: unify card runtime and event-driven web ui
Some checks failed
CI / Backend Checks (push) Failing after 36s
CI / Frontend Checks (push) Failing after 40s

This commit is contained in:
kacper 2026-04-06 15:42:53 -04:00
parent 0edf8c3fef
commit 4dfb7ca3cc
105 changed files with 17382 additions and 8505 deletions

92
routes/templates.py Normal file
View file

@ -0,0 +1,92 @@
from __future__ import annotations
import json
import shutil
from datetime import datetime, timezone
from fastapi import APIRouter, Request
from fastapi.responses import JSONResponse
from card_store import (
list_templates,
normalize_template_key,
read_template_meta,
sync_templates_context_file,
template_dir,
template_html_path,
template_meta_path,
)
from route_helpers import read_json_request
router = APIRouter()
@router.get("/templates")
async def get_templates() -> JSONResponse:
return JSONResponse(list_templates())
@router.post("/templates")
async def save_template(request: Request) -> JSONResponse:
try:
payload = await read_json_request(request)
except ValueError as exc:
return JSONResponse({"error": str(exc)}, status_code=400)
key = normalize_template_key(str(payload.get("key", "")))
title = str(payload.get("title", "")).strip()
content = str(payload.get("content", "")).strip()
notes = str(payload.get("notes", "")).strip()
example_state = payload.get("example_state", {})
if not isinstance(example_state, dict):
example_state = {}
if not key:
return JSONResponse({"error": "template key is required"}, status_code=400)
if not content:
return JSONResponse({"error": "template content is required"}, status_code=400)
current_template_dir = template_dir(key)
current_template_dir.mkdir(parents=True, exist_ok=True)
now = datetime.now(timezone.utc).isoformat()
existing_meta = read_template_meta(key)
created_at = str(existing_meta.get("created_at") or now)
template_html_path(key).write_text(content, encoding="utf-8")
template_meta_path(key).write_text(
json.dumps(
{
"key": key,
"title": title,
"notes": notes,
"example_state": example_state,
"created_at": created_at,
"updated_at": now,
},
indent=2,
ensure_ascii=False,
)
+ "\n",
encoding="utf-8",
)
sync_templates_context_file()
return JSONResponse(
{
"status": "ok",
"id": key,
"key": key,
"example_state": example_state,
"file_url": f"/card-templates/{key}/template.html",
}
)
@router.delete("/templates/{template_key}")
async def delete_template(template_key: str) -> JSONResponse:
key = normalize_template_key(template_key)
if not key:
return JSONResponse({"error": "invalid template key"}, status_code=400)
shutil.rmtree(template_dir(key), ignore_errors=True)
sync_templates_context_file()
return JSONResponse({"status": "ok", "key": key})