From 89abd5eee45ac31436cadfec5bde9e863b6ee749 Mon Sep 17 00:00:00 2001 From: vegard Date: Tue, 17 Mar 2026 19:00:08 +0100 Subject: [PATCH] =?UTF-8?q?Legg=20til=20create=5Falias-endepunkt=20og=20al?= =?UTF-8?q?ias-sp=C3=B8rring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Oppgave 8.1: Alias-noder med system-edge. Nytt endepunkt POST /intentions/create_alias oppretter en person-node og en alias-edge (system=true) fra brukerens hovednode. GET /query/aliases returnerer brukerens alias-noder. Alias-edgen er usynlig for traversering via eksisterende RLS-policy som filtrerer system-edges. Co-Authored-By: Claude Opus 4.6 --- maskinrommet/src/intentions.rs | 150 +++++++++++++++++++++++++++++++++ maskinrommet/src/main.rs | 2 + 2 files changed, 152 insertions(+) diff --git a/maskinrommet/src/intentions.rs b/maskinrommet/src/intentions.rs index 8f8bf12..cb6e596 100644 --- a/maskinrommet/src/intentions.rs +++ b/maskinrommet/src/intentions.rs @@ -1066,6 +1066,156 @@ async fn find_collection_for_node(db: &PgPool, node_id: Uuid) -> Result, +} + +#[derive(Serialize)] +pub struct CreateAliasResponse { + /// ID til den nye alias-noden. + pub alias_node_id: Uuid, + /// ID til alias-edgen (system=true, usynlig for traversering). + pub alias_edge_id: Uuid, +} + +/// POST /intentions/create_alias +/// +/// Oppretter en alias-node (node_kind='person') og en `alias`-edge +/// (system=true) fra brukerens hovednode til aliasnoden. Alias-edgen +/// er usynlig for traversering — RLS-policyen filtrerer system-edges. +/// +/// Bruksområde: en bruker kan ha flere identiteter (f.eks. Vegard +/// som seg selv og "Bjørn" som podcastvert). Oppgave 8.2 vil bruke +/// aliaset til å sette created_by kontekstbasert. +/// +/// Ref: docs/primitiver/edges.md (systemedges), docs/primitiver/nodes.md +pub async fn create_alias( + State(state): State, + user: AuthUser, + Json(req): Json, +) -> Result, (StatusCode, Json)> { + let title = req.title.trim().to_string(); + if title.is_empty() { + return Err(bad_request("Alias-tittel kan ikke være tom")); + } + + let metadata = req.metadata.unwrap_or_else(|| serde_json::json!({})); + + // -- Generer IDer -- + let alias_node_id = Uuid::now_v7(); + let alias_edge_id = Uuid::now_v7(); + + let alias_node_id_str = alias_node_id.to_string(); + let alias_edge_id_str = alias_edge_id.to_string(); + let user_node_id_str = user.node_id.to_string(); + let metadata_str = metadata.to_string(); + + // -- Skriv alias-node til STDB -- + state + .stdb + .create_node( + &alias_node_id_str, + "person", + &title, + "", + "hidden", + &metadata_str, + &user_node_id_str, + ) + .await + .map_err(|e| stdb_error("create_node (alias)", e))?; + + // -- Skriv alias-edge til STDB (system=true) -- + state + .stdb + .create_edge( + &alias_edge_id_str, + &user_node_id_str, + &alias_node_id_str, + "alias", + "{}", + true, + &user_node_id_str, + ) + .await + .map_err(|e| stdb_error("create_edge (alias)", e))?; + + tracing::info!( + alias_node_id = %alias_node_id, + alias_edge_id = %alias_edge_id, + user_node_id = %user.node_id, + title = %title, + "Alias opprettet i STDB" + ); + + // -- Spawn async PG-skriving: node + edge -- + let pg_db = state.db.clone(); + let pg_title = title.clone(); + let pg_metadata = metadata.clone(); + let pg_user_node_id = user.node_id; + tokio::spawn(async move { + // 1. Skriv alias-noden + let node_result = sqlx::query( + r#" + INSERT INTO nodes (id, node_kind, title, visibility, metadata, created_by) + VALUES ($1, 'person', $2, 'hidden'::visibility, $3, $4) + "#, + ) + .bind(alias_node_id) + .bind(&pg_title) + .bind(&pg_metadata) + .bind(pg_user_node_id) + .execute(&pg_db) + .await; + + match node_result { + Ok(_) => { + tracing::info!(alias_node_id = %alias_node_id, "Alias-node persistert til PostgreSQL"); + } + Err(e) => { + tracing::error!(alias_node_id = %alias_node_id, error = %e, "Kunne ikke persistere alias-node til PostgreSQL"); + return; + } + } + + // 2. Skriv alias-edge (system=true) + let edge_result = sqlx::query( + r#" + INSERT INTO edges (id, source_id, target_id, edge_type, metadata, system, created_by) + VALUES ($1, $2, $3, 'alias', '{}', true, $4) + "#, + ) + .bind(alias_edge_id) + .bind(pg_user_node_id) + .bind(alias_node_id) + .bind(pg_user_node_id) + .execute(&pg_db) + .await; + + match edge_result { + Ok(_) => { + tracing::info!(alias_edge_id = %alias_edge_id, "Alias-edge persistert til PostgreSQL"); + } + Err(e) => { + tracing::error!(alias_edge_id = %alias_edge_id, error = %e, "Kunne ikke persistere alias-edge til PostgreSQL"); + } + } + }); + + Ok(Json(CreateAliasResponse { + alias_node_id, + alias_edge_id, + })) +} + // ============================================================================= // Bakgrunns-PG-operasjoner // ============================================================================= diff --git a/maskinrommet/src/main.rs b/maskinrommet/src/main.rs index e180f25..440cecc 100644 --- a/maskinrommet/src/main.rs +++ b/maskinrommet/src/main.rs @@ -141,9 +141,11 @@ async fn main() { .route("/query/nodes", get(queries::query_nodes)) .route("/query/segments", get(queries::query_segments)) .route("/query/segments/srt", get(queries::export_srt)) + .route("/intentions/create_alias", post(intentions::create_alias)) .route("/intentions/update_segment", post(intentions::update_segment)) .route("/intentions/retranscribe", post(intentions::retranscribe)) .route("/intentions/resolve_retranscription", post(intentions::resolve_retranscription)) + .route("/query/aliases", get(queries::query_aliases)) .route("/query/transcription_versions", get(queries::query_transcription_versions)) .route("/query/segments_version", get(queries::query_segments_version)) .layer(TraceLayer::new_for_http())