"""FlowDeck — Collections : data_api. Découpe A28 de l'ancien app/routers/collections.py (2 622 lignes, 53 routes) — un module par concern, contrat inchangé. """ from __future__ import annotations import json import logging from fastapi import APIRouter, Body, HTTPException, Request from app.db import get_conn from app.services.automations import fire_event, run_event_sync from app.services.property_types import ( apply_auto_properties, ) from ._common import ( _collection_properties, _current_user, _require_edit, _require_view, _session_user, _validate_meta_keys, _validate_page_properties, ) logger = logging.getLogger(__name__) router = APIRouter(tags=["collections"], prefix="/db") @router.get("/{collection_id}/api") def get_collection_api(request: Request, collection_id: int): """API: get a single collection with its pages.""" # v6.0.0: granular collection permissions — hide restricted collections. _require_view(collection_id, _session_user(request)) with get_conn() as conn: collection = conn.execute( "SELECT * FROM collections WHERE id=?", (collection_id,) ).fetchone() if not collection: raise HTTPException(status_code=404, detail="Collection not found") pages = conn.execute( "SELECT * FROM collection_pages WHERE collection_id=? ORDER BY position", (collection_id,), ).fetchall() views = conn.execute( "SELECT * FROM collection_views WHERE collection_id=? ORDER BY position", (collection_id,), ).fetchall() return { "collection": dict(collection), "pages": [dict(p) for p in pages], "views": [dict(v) for v in views], } @router.post("/{collection_id}/pages/api") def create_page_api(request: Request, collection_id: int, body: dict = Body(default={})): """API: create a page in a collection.""" # v6.0.0: granular collection permissions — viewer/commenter cannot create. _require_view(collection_id, _session_user(request)) _require_edit(collection_id, _session_user(request)) title = body.get("title", "").strip() if not title: raise HTTPException(status_code=400, detail="title is required") icon = body.get("icon", "📄") property_values = body.get("properties", {}) cover_url = body.get("cover_url", "") gitea_issue_id = body.get("gitea_issue_id") gitea_issue_number = body.get("gitea_issue_number") with get_conn() as conn: coll = conn.execute("SELECT id FROM collections WHERE id=?", (collection_id,)).fetchone() if not coll: raise HTTPException(status_code=404, detail="Collection not found") _validate_page_properties(conn, collection_id, property_values) _validate_meta_keys(conn, collection_id, property_values) apply_auto_properties( _collection_properties(conn, collection_id), property_values, _current_user(request), is_create=True, ) max_pos = conn.execute( "SELECT COALESCE(MAX(position), -1) + 1 FROM collection_pages WHERE collection_id=?", (collection_id,), ).fetchone()[0] cur = conn.execute( """INSERT INTO collection_pages (collection_id, title, icon, cover_url, position, gitea_issue_id, gitea_issue_number, property_values_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?)""", (collection_id, title, icon, cover_url, max_pos, gitea_issue_id, gitea_issue_number, json.dumps(property_values)), ) conn.commit() page_id = cur.lastrowid run_event_sync(fire_event("page.created", { "page_id": page_id, "collection_id": collection_id, "title": title, "icon": icon, "properties": property_values, })) run_event_sync(fire_event("collection.page.created", { "page_id": page_id, "collection_id": collection_id, "title": title, })) return {"id": page_id, "title": title, "status": "created"}