cloudillo_action/native_hooks/conv.rs
1//! CONV (Conversation) action native hooks
2//!
3//! Handles conversation lifecycle:
4//! - on_create: Auto-creates SUBS with admin role for the creator
5//! - on_receive: Handles receiving federated conversation
6//! - Subtypes:
7//! - UPD: Update conversation settings (requires admin role)
8//! - DEL: Archive/delete conversation (requires admin role)
9
10use crate::hooks::{HookContext, HookResult};
11use crate::prelude::*;
12use crate::task::{create_action, CreateAction};
13use cloudillo_core::app::App;
14
15/// CONV on_create hook - Auto-subscribe creator as admin
16///
17/// Logic:
18/// - Creator creates their own SUBS with subject=action_id
19/// - Role is stored in x.role (extensible metadata), not in content JWT
20/// - If CONV has an audience (community), SUBS federates to the community
21/// - SUBS issuer is the creator (self-issued) ensuring proper ownership
22pub async fn on_create(app: App, context: HookContext) -> ClResult<HookResult> {
23 let tn_id = TnId(context.tenant_id as u32);
24
25 tracing::info!(
26 "CONV: Creating conversation {} by {}, audience={:?}",
27 context.action_id,
28 context.issuer,
29 context.audience
30 );
31
32 // Auto-create admin subscription for the creator
33 // Role is stored in x.role (server-side metadata, not in JWT)
34 let subs_action = CreateAction {
35 typ: "SUBS".into(),
36 // If CONV has audience (e.g., community), use that for federation
37 // Otherwise, use self as audience (personal conversation)
38 audience_tag: context
39 .audience
40 .clone()
41 .map(|a| a.into_boxed_str())
42 .or_else(|| Some(context.issuer.clone().into_boxed_str())),
43 subject: Some(context.action_id.clone().into()),
44 // x.role stores the subscription role (server-side, not in JWT)
45 x: Some(serde_json::json!({ "role": "admin" })),
46 ..Default::default()
47 };
48
49 match create_action(&app, tn_id, &context.issuer, subs_action).await {
50 Ok(subs_id) => {
51 tracing::info!(
52 "CONV: Auto-created admin SUBS {} for conversation {}",
53 subs_id,
54 context.action_id
55 );
56 }
57 Err(e) => {
58 tracing::error!(
59 "CONV: Failed to create admin SUBS for conversation {}: {}",
60 context.action_id,
61 e
62 );
63 // Don't fail the CONV creation - log and continue
64 }
65 }
66
67 Ok(HookResult::default())
68}
69
70/// CONV on_receive hook - Handle receiving shared conversation
71///
72/// Logic:
73/// - When a CONV is received (federated), we store it for reference
74/// - No special action needed as SUBS handles membership
75pub async fn on_receive(_app: App, context: HookContext) -> ClResult<HookResult> {
76 tracing::debug!("CONV: Received conversation {} from {}", context.action_id, context.issuer);
77
78 // CONV actions from remote are informational - the SUBS system handles access
79 Ok(HookResult::default())
80}
81
82// vim: ts=4