Fire automation events across API routers
FlowDeck CI / lint (push) Failing after 1m12s
FlowDeck CI / test (push) Failing after 3h3m3s
FlowDeck CI / docker (push) Skipped

This commit is contained in:
2026-09-21 20:30:05 -04:00
parent 189ed5bbca
commit e0237e576f
8 changed files with 202 additions and 2 deletions
+64
View File
@@ -16,6 +16,7 @@ from fastapi import APIRouter, Header, HTTPException, Request
from fastapi.responses import JSONResponse, Response
from app.db import get_conn
from app.services.automations import fire_event as _fire_event
from app.services.api_v2_helpers import (
audit_log,
check_idempotency,
@@ -779,6 +780,10 @@ async def create_collection_page_v2(collection_id: int, request: Request, author
conn.commit()
row = conn.execute("SELECT * FROM collection_pages WHERE id=?", (pid,)).fetchone()
audit_log(user, "page.create", "collection_page", pid, title, request)
try:
await _fire_event("collection.page.created", {"page_id": pid, "collection_id": collection_id, "title": title})
except Exception:
pass
data = {"id": pid, "title": title, "status": "created", "page": row_to_dict(row)}
key = (request.headers.get("Idempotency-Key") or "").strip()
if key:
@@ -845,6 +850,10 @@ async def patch_page_v2(page_id: int, request: Request, authorization: str | Non
conn.execute("UPDATE collection_pages SET title=?, icon=?, position=?, parent_id=?, property_values_json=?, updated_at=CURRENT_TIMESTAMP WHERE id=?", (title, icon, pos, parent_id, json.dumps(stored), page_id))
conn.commit()
audit_log(user, "page.update", "collection_page", page_id, "", request)
try:
await _fire_event("collection.page.updated", {"page_id": page_id, "collection_id": row["collection_id"], "title": title})
except Exception:
pass
return {"id": page_id, "status": "updated"}
@router.delete("/pages/{page_id}")
@@ -860,6 +869,10 @@ async def delete_page_v2(page_id: int, request: Request, authorization: str | No
conn.execute("DELETE FROM collection_pages WHERE id=?", (page_id,))
conn.commit()
audit_log(user, "page.delete", "collection_page", page_id, "", request)
try:
await _fire_event("collection.page.deleted", {"page_id": page_id, "collection_id": row["collection_id"]})
except Exception:
pass
return {"id": page_id, "status": "deleted"}
@router.post("/pages/{page_id}/restore")
@@ -874,6 +887,10 @@ async def restore_page_v2(page_id: int, request: Request, authorization: str | N
if row and row["deleted_at"]:
conn.execute("UPDATE pages SET deleted_at=NULL WHERE id=?", (page_id,))
conn.commit()
try:
await _fire_event("page.restored", {"page_id": page_id})
except Exception:
pass
return {"id": page_id, "status": "restored"}
raise HTTPException(404, "Page not found or not deleted")
@@ -1153,6 +1170,10 @@ async def create_view_v2(collection_id: int, request: Request, authorization: st
vid = cur.lastrowid
conn.commit()
audit_log(user, "view.create", "view", vid, name, request)
try:
await _fire_event("collection.view.created", {"view_id": vid, "collection_id": collection_id, "name": name, "view_type": vtype})
except Exception:
pass
return {"id": vid, "name": name, "view_type": vtype, "status": "created"}
@router.patch("/views/{view_id}")
@@ -1315,6 +1336,10 @@ async def create_comment_v2(page_id: int, request: Request, authorization: str |
conn.commit()
row = conn.execute("SELECT * FROM comments WHERE id=?", (nid,)).fetchone()
audit_log(user, "comment.create", "comment", nid, text[:80], request)
try:
await _fire_event("comment.added", {"comment_id": nid, "page_id": page_id, "user_id": user["id"]})
except Exception:
pass
return {"id": nid, "status": "created", "comment": row_to_dict(row)}
@router.patch("/comments/{comment_id}")
@@ -1335,8 +1360,14 @@ async def patch_comment_v2(comment_id: int, request: Request, authorization: str
raise HTTPException(403, "Not your comment")
body_text = body.get("body", row["body"])
resolved = body.get("resolved", row["resolved"])
was_resolved = int(row["resolved"] or 0)
conn.execute("UPDATE comments SET body=?, resolved=?, updated_at=CURRENT_TIMESTAMP WHERE id=?", (body_text, int(bool(resolved)), comment_id))
conn.commit()
if int(bool(resolved)) and not was_resolved:
try:
await _fire_event("comment.resolved", {"comment_id": comment_id, "page_id": row["page_id"]})
except Exception:
pass
return {"id": comment_id, "status": "updated"}
@router.delete("/comments/{comment_id}")
@@ -1379,6 +1410,11 @@ async def create_mention_v2(page_id: int, request: Request, authorization: str |
except Exception:
pass
conn.commit()
if created:
try:
await _fire_event("mention.added", {"page_id": page_id, "user_ids": [u for u in targets if isinstance(u, int)], "count": created})
except Exception:
pass
return {"mentions": created, "status": "created"}
# ── Notifications ─────────────────────────────────────────────────────────
@@ -1472,6 +1508,10 @@ async def add_favorite_v2(request: Request, authorization: str | None = Header(d
conn.commit()
except Exception as e:
raise HTTPException(409, str(e)) from None
try:
await _fire_event("favorite.added", {"page_id": pid, "user_id": user["id"]})
except Exception:
pass
return {"page_id": pid, "status": "added"}
@router.delete("/favorites/{page_id}")
@@ -1483,6 +1523,10 @@ async def remove_favorite_v2(page_id: int, request: Request, authorization: str
with get_conn() as conn:
conn.execute("DELETE FROM favorites WHERE user_id=? AND page_id=?", (user["id"], page_id))
conn.commit()
try:
await _fire_event("favorite.removed", {"page_id": page_id, "user_id": user["id"]})
except Exception:
pass
return {"page_id": page_id, "status": "removed"}
@router.get("/tags")
@@ -1629,6 +1673,10 @@ async def create_share_v2(page_id: int, request: Request, authorization: str | N
conn.commit()
nid = cur.lastrowid
audit_log(user, "share.create", "share", nid, f"page={page_id}", request)
try:
await _fire_event("page.shared", {"page_id": page_id, "share_id": nid, "permission": perm})
except Exception:
pass
return {"id": nid, "page_id": page_id, "status": "shared"}
@router.patch("/shares/{share_id}")
@@ -1686,6 +1734,10 @@ async def publish_page_v2(page_id: int, request: Request, authorization: str | N
conn.execute("UPDATE pages SET is_published=1, publish_slug=?, is_shared=1 WHERE id=?", (slug, page_id))
conn.commit()
audit_log(user, "page.publish", "page", page_id, slug, request)
try:
await _fire_event("page.published", {"page_id": page_id, "slug": slug})
except Exception:
pass
return {"page_id": page_id, "slug": slug, "url": f"/p/{slug}", "status": "published"}
@router.delete("/pages/{page_id}/publish")
@@ -1697,6 +1749,10 @@ async def unpublish_page_v2(page_id: int, request: Request, authorization: str |
with get_conn() as conn:
conn.execute("UPDATE pages SET is_published=0 WHERE id=?", (page_id,))
conn.commit()
try:
await _fire_event("page.unpublished", {"page_id": page_id})
except Exception:
pass
return {"page_id": page_id, "status": "unpublished"}
# ── History ───────────────────────────────────────────────────────────────
@@ -1774,6 +1830,10 @@ async def create_sprint_v2(collection_id: int, request: Request, authorization:
cur = conn.execute("INSERT INTO sprints (collection_id, name, start_date, end_date, goal) VALUES (?, ?, ?, ?, ?)", (collection_id, name, start, end, body.get("goal") or ""))
sid = cur.lastrowid
conn.commit()
try:
await _fire_event("sprint.created", {"sprint_id": sid, "collection_id": collection_id, "name": name})
except Exception:
pass
return {"id": sid, "name": name, "status": "created"}
@router.patch("/sprints/{sprint_id}")
@@ -1797,6 +1857,10 @@ async def patch_sprint_v2(sprint_id: int, request: Request, authorization: str |
status = body.get("status", row["status"])
conn.execute("UPDATE sprints SET name=?, start_date=?, end_date=?, goal=?, status=? WHERE id=?", (name, start, end, goal, status, sprint_id))
conn.commit()
try:
await _fire_event("sprint.updated", {"sprint_id": sprint_id, "collection_id": row["collection_id"], "name": name, "status": status})
except Exception:
pass
return {"id": sprint_id, "status": "updated"}
@router.delete("/sprints/{sprint_id}")