feat: barre d'apps commune et références croisées des notes (#215)
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user