"""Relations app action endpoints.""" from __future__ import annotations from quart import Blueprint, g, jsonify, request from shared.infrastructure.actions import ACTION_HEADER def register() -> Blueprint: bp = Blueprint("actions", __name__, url_prefix="/internal/actions") @bp.before_request async def _require_action_header(): if not request.headers.get(ACTION_HEADER): return jsonify({"error": "forbidden"}), 403 from shared.infrastructure.internal_auth import validate_internal_request if not validate_internal_request(): return jsonify({"error": "forbidden"}), 403 _handlers: dict[str, object] = {} @bp.post("/") async def handle_action(action_name: str): handler = _handlers.get(action_name) if handler is None: return jsonify({"error": "unknown action"}), 404 try: result = await handler() return jsonify(result) except Exception as exc: import logging logging.getLogger(__name__).exception("Action %s failed", action_name) return jsonify({"error": str(exc)}), 500 # --- attach-child --- async def _attach_child(): """Create or revive a ContainerRelation.""" from shared.services.relationships import attach_child data = await request.get_json(force=True) rel = await attach_child( g.s, parent_type=data["parent_type"], parent_id=data["parent_id"], child_type=data["child_type"], child_id=data["child_id"], label=data.get("label"), sort_order=data.get("sort_order"), ) return { "id": rel.id, "parent_type": rel.parent_type, "parent_id": rel.parent_id, "child_type": rel.child_type, "child_id": rel.child_id, "sort_order": rel.sort_order, } _handlers["attach-child"] = _attach_child # --- detach-child --- async def _detach_child(): """Soft-delete a ContainerRelation.""" from shared.services.relationships import detach_child data = await request.get_json(force=True) deleted = await detach_child( g.s, parent_type=data["parent_type"], parent_id=data["parent_id"], child_type=data["child_type"], child_id=data["child_id"], ) return {"deleted": deleted} _handlers["detach-child"] = _detach_child return bp