85 lines
3.1 KiB
Python
85 lines
3.1 KiB
Python
"""Endpoints des références croisées des notes — Fusion #215 (ACC-1.3).
|
|
|
|
Création par collage d'une URL profonde d'une autre application
|
|
(Flowdeck, TikTak, Kanbante, ObsiGate) — zéro couplage serveur :
|
|
l'URL est reconnue par les formes figées du contrat
|
|
``dracodev-commons/contrats/liens-profonds.md`` (voir
|
|
:mod:`backend.external_refs`), la référence est stockée chez la note
|
|
propriétaire et affichée dans sa section « Liens ».
|
|
"""
|
|
|
|
import logging
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
|
|
from backend.auth.middleware import check_vault_access, require_auth
|
|
from backend.external_refs import add_ref, delete_ref, get_ref, list_refs, parse_deep_link
|
|
from backend.schemas import ExternalRef, ExternalRefCreateRequest, ExternalRefsResponse
|
|
|
|
logger = logging.getLogger("obsigate")
|
|
|
|
router = APIRouter(tags=["ExternalRefs"])
|
|
|
|
_FORMES_ACCEPTEES = (
|
|
"URL non reconnue — formes acceptées : TikTak ?task= ou ?list=, "
|
|
"ObsiGate #file=, Flowdeck /pages/ ou /collections/, "
|
|
"issue Gitea (carte Kanbante)"
|
|
)
|
|
|
|
|
|
@router.get("/api/refs", response_model=ExternalRefsResponse)
|
|
async def api_list_refs(
|
|
vault: str = Query(...),
|
|
path: str = Query(...),
|
|
current_user=Depends(require_auth),
|
|
):
|
|
"""Références croisées posées sur une note."""
|
|
if not check_vault_access(vault, current_user):
|
|
raise HTTPException(status_code=403, detail="Accès au vault refusé")
|
|
username = current_user.get("username") or ""
|
|
return {"refs": list_refs(username, vault, path)}
|
|
|
|
|
|
@router.post("/api/refs", response_model=ExternalRef, status_code=201)
|
|
async def api_add_ref(
|
|
payload: ExternalRefCreateRequest,
|
|
current_user=Depends(require_auth),
|
|
):
|
|
"""Crée une référence sur une note à partir d'une URL profonde collée."""
|
|
if not check_vault_access(payload.vault, current_user):
|
|
raise HTTPException(status_code=403, detail="Accès au vault refusé")
|
|
parsed = parse_deep_link(payload.url)
|
|
if parsed is None:
|
|
raise HTTPException(status_code=422, detail=_FORMES_ACCEPTEES)
|
|
target_app, target_type, target_id, default_label = parsed
|
|
label = (payload.label or "").strip() or default_label
|
|
username = current_user.get("username") or ""
|
|
return add_ref(
|
|
username,
|
|
payload.vault,
|
|
payload.path,
|
|
target_app,
|
|
target_type,
|
|
target_id,
|
|
payload.url.strip(),
|
|
label,
|
|
)
|
|
|
|
|
|
@router.delete("/api/refs/{ref_id}", response_model=ExternalRef)
|
|
async def api_delete_ref(
|
|
ref_id: str,
|
|
current_user=Depends(require_auth),
|
|
):
|
|
"""Retire une référence d'une note."""
|
|
username = current_user.get("username") or ""
|
|
ref = get_ref(username, ref_id)
|
|
if ref is None:
|
|
raise HTTPException(status_code=404, detail="Lien introuvable")
|
|
if not check_vault_access(ref["owner_vault"], current_user):
|
|
raise HTTPException(status_code=403, detail="Accès au vault refusé")
|
|
removed = delete_ref(username, ref_id)
|
|
if removed is None: # course improbable : disparue entre get et delete
|
|
raise HTTPException(status_code=404, detail="Lien introuvable")
|
|
return removed
|