Legg til create_alias-endepunkt og alias-spørring
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 <noreply@anthropic.com>
This commit is contained in:
parent
b687c717c6
commit
89abd5eee4
2 changed files with 152 additions and 0 deletions
|
|
@ -1066,6 +1066,156 @@ async fn find_collection_for_node(db: &PgPool, node_id: Uuid) -> Result<Option<U
|
|||
Ok(row)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// create_alias
|
||||
// =============================================================================
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CreateAliasRequest {
|
||||
/// Visningsnavn for aliaset (f.eks. "Bjørn" for en podcastvert-identitet).
|
||||
pub title: String,
|
||||
/// Valgfri metadata (f.eks. display_name, bio, avatar).
|
||||
pub metadata: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[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<AppState>,
|
||||
user: AuthUser,
|
||||
Json(req): Json<CreateAliasRequest>,
|
||||
) -> Result<Json<CreateAliasResponse>, (StatusCode, Json<ErrorResponse>)> {
|
||||
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
|
||||
// =============================================================================
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue