feat: unify card runtime and event-driven web ui
This commit is contained in:
parent
0edf8c3fef
commit
4dfb7ca3cc
105 changed files with 17382 additions and 8505 deletions
108
routes/cards.py
Normal file
108
routes/cards.py
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from app_dependencies import get_runtime
|
||||
from card_store import (
|
||||
delete_card,
|
||||
load_card,
|
||||
load_cards,
|
||||
normalize_card_id,
|
||||
parse_iso_datetime,
|
||||
write_card,
|
||||
)
|
||||
from route_helpers import read_json_request
|
||||
from session_store import normalize_session_chat_id
|
||||
from web_runtime import WebAppRuntime
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/cards")
|
||||
async def get_cards(request: Request) -> JSONResponse:
|
||||
chat_id = normalize_session_chat_id(str(request.query_params.get("chat_id", "web") or "web"))
|
||||
if not chat_id:
|
||||
return JSONResponse({"error": "invalid chat id"}, status_code=400)
|
||||
return JSONResponse(load_cards(chat_id))
|
||||
|
||||
|
||||
@router.delete("/cards/{card_id}")
|
||||
async def delete_card_route(
|
||||
card_id: str,
|
||||
runtime: WebAppRuntime = Depends(get_runtime),
|
||||
) -> JSONResponse:
|
||||
if not normalize_card_id(card_id):
|
||||
return JSONResponse({"error": "invalid card id"}, status_code=400)
|
||||
card = load_card(card_id)
|
||||
delete_card(card_id)
|
||||
await runtime.publish_cards_changed(card.get("chat_id") if isinstance(card, dict) else None)
|
||||
return JSONResponse({"status": "ok"})
|
||||
|
||||
|
||||
@router.post("/cards/{card_id}/snooze")
|
||||
async def snooze_card(
|
||||
card_id: str,
|
||||
request: Request,
|
||||
runtime: WebAppRuntime = Depends(get_runtime),
|
||||
) -> JSONResponse:
|
||||
if not normalize_card_id(card_id):
|
||||
return JSONResponse({"error": "invalid card id"}, status_code=400)
|
||||
|
||||
try:
|
||||
payload = await read_json_request(request)
|
||||
except ValueError as exc:
|
||||
return JSONResponse({"error": str(exc)}, status_code=400)
|
||||
|
||||
until_raw = str(payload.get("until", "")).strip()
|
||||
until_dt = parse_iso_datetime(until_raw)
|
||||
if until_dt is None:
|
||||
return JSONResponse({"error": "until must be a valid ISO datetime"}, status_code=400)
|
||||
|
||||
card = load_card(card_id)
|
||||
if card is None:
|
||||
return JSONResponse({"error": "card not found"}, status_code=404)
|
||||
|
||||
card["snooze_until"] = until_dt.isoformat()
|
||||
card["updated_at"] = datetime.now(timezone.utc).isoformat()
|
||||
persisted = write_card(card)
|
||||
if persisted is None:
|
||||
return JSONResponse({"error": "failed to snooze card"}, status_code=500)
|
||||
await runtime.publish_cards_changed(persisted.get("chat_id"))
|
||||
return JSONResponse({"status": "ok", "card": persisted})
|
||||
|
||||
|
||||
@router.post("/cards/{card_id}/state")
|
||||
async def update_card_state(
|
||||
card_id: str,
|
||||
request: Request,
|
||||
runtime: WebAppRuntime = Depends(get_runtime),
|
||||
) -> JSONResponse:
|
||||
if not normalize_card_id(card_id):
|
||||
return JSONResponse({"error": "invalid card id"}, status_code=400)
|
||||
|
||||
try:
|
||||
payload = await read_json_request(request)
|
||||
except ValueError as exc:
|
||||
return JSONResponse({"error": str(exc)}, status_code=400)
|
||||
|
||||
template_state = payload.get("template_state")
|
||||
if not isinstance(template_state, dict):
|
||||
return JSONResponse({"error": "template_state must be an object"}, status_code=400)
|
||||
|
||||
card = load_card(card_id)
|
||||
if card is None:
|
||||
return JSONResponse({"error": "card not found"}, status_code=404)
|
||||
|
||||
if str(card.get("kind", "")) != "text":
|
||||
return JSONResponse({"error": "only text cards support template_state"}, status_code=400)
|
||||
|
||||
card["template_state"] = template_state
|
||||
card["updated_at"] = datetime.now(timezone.utc).isoformat()
|
||||
persisted = write_card(card)
|
||||
if persisted is None:
|
||||
return JSONResponse({"error": "failed to update card state"}, status_code=500)
|
||||
await runtime.publish_cards_changed(persisted.get("chat_id"))
|
||||
return JSONResponse({"status": "ok", "card": persisted})
|
||||
Loading…
Add table
Add a link
Reference in a new issue