use serde_json::Value;
use tracing::warn;
use did_hosting_common::didcomm_types::{
MSG_HEALTH_PONG, MSG_REPLICA_DOMAIN_ASSIGN_ACK, MSG_REPLICA_DOMAIN_PURGE_ACK,
MSG_REPLICA_DOMAIN_UNASSIGN_ACK, MSG_REPLICA_DOMAIN_UPSERT_ACK, MSG_SERVER_REGISTER,
MSG_SERVER_REGISTER_ACK, MSG_STATS_ACK, MSG_STATS_SYNC, MSG_SYNC_BATCH_ACK,
MSG_SYNC_DELETE_ACK, MSG_SYNC_UPDATE_ACK,
};
use did_hosting_common::server::didcomm_profile::ObservedTransport;
use crate::registry;
use crate::server::AppState;
pub fn owns(type_uri: &str) -> bool {
matches!(
type_uri,
MSG_SERVER_REGISTER
| MSG_HEALTH_PONG
| MSG_STATS_SYNC
| MSG_SYNC_UPDATE_ACK
| MSG_SYNC_BATCH_ACK
| MSG_SYNC_DELETE_ACK
| MSG_REPLICA_DOMAIN_ASSIGN_ACK
| MSG_REPLICA_DOMAIN_UNASSIGN_ACK
| MSG_REPLICA_DOMAIN_PURGE_ACK
| MSG_REPLICA_DOMAIN_UPSERT_ACK
)
}
pub async fn dispatch(
state: &AppState,
sender: &str,
via: Option<ObservedTransport>,
doc: trust_tasks_rs::TrustTask<Value>,
) -> Option<Value> {
let response = dispatch_inner(state, sender, doc).await;
if let Some(via) = via {
registry::record_inbound_transport(
&state.registry_ks,
sender,
via,
crate::auth::session::now_epoch(),
)
.await;
}
response
}
async fn dispatch_inner(
state: &AppState,
sender: &str,
doc: trust_tasks_rs::TrustTask<Value>,
) -> Option<Value> {
match doc.type_uri.to_string().as_str() {
MSG_SERVER_REGISTER => {
let reply_id = uuid::Uuid::new_v4().to_string();
match crate::messaging::do_server_register(state, sender, &doc.payload).await {
Ok(ack) => {
let resp = doc.respond_with(reply_id, ack);
debug_assert_eq!(resp.type_uri.to_string(), MSG_SERVER_REGISTER_ACK);
Some(serde_json::to_value(&resp).expect("ack document serialises"))
}
Err(rej) => {
let err = doc.reject_with(
reply_id,
trust_tasks_rs::RejectReason::PermissionDenied {
reason: rej.comment.clone(),
},
);
warn!(
sender,
code = rej.code,
comment = %rej.comment,
"server registration rejected (trust task)"
);
Some(serde_json::to_value(&err).expect("error document serialises"))
}
}
}
MSG_STATS_SYNC => {
let reply_id = uuid::Uuid::new_v4().to_string();
match crate::messaging::do_stats_sync(state, sender, &doc.payload).await {
Ok(ack) => {
let resp = doc.respond_with(reply_id, ack);
debug_assert_eq!(resp.type_uri.to_string(), MSG_STATS_ACK);
Some(serde_json::to_value(&resp).expect("ack document serialises"))
}
Err(rej) => {
let err = doc.reject_with(
reply_id,
trust_tasks_rs::RejectReason::PermissionDenied {
reason: rej.comment.clone(),
},
);
Some(serde_json::to_value(&err).expect("error document serialises"))
}
}
}
MSG_HEALTH_PONG => {
crate::messaging::do_health_pong(state, sender, &doc.payload).await;
None
}
uri @ (MSG_SYNC_UPDATE_ACK | MSG_SYNC_BATCH_ACK | MSG_SYNC_DELETE_ACK) => {
crate::messaging::do_sync_ack(sender, uri, &doc.payload);
acknowledge_outbox(state, sender, &doc).await;
None
}
uri @ (MSG_REPLICA_DOMAIN_ASSIGN_ACK
| MSG_REPLICA_DOMAIN_UNASSIGN_ACK
| MSG_REPLICA_DOMAIN_PURGE_ACK) => {
crate::messaging::do_domain_ack(state, sender, uri, &doc.payload).await;
acknowledge_outbox(state, sender, &doc).await;
None
}
MSG_REPLICA_DOMAIN_UPSERT_ACK => {
acknowledge_outbox(state, sender, &doc).await;
None
}
other => {
warn!(type_uri = other, "trust_tasks_infra: unowned type URI");
None
}
}
}
pub(crate) async fn acknowledge_outbox(
state: &AppState,
sender: &str,
doc: &trust_tasks_rs::TrustTask<Value>,
) {
let thread = doc.thread_id.as_deref().or_else(|| {
doc.payload
.get("inResponseTo")
.and_then(|r| r.get("id"))
.and_then(Value::as_str)
});
let Some(thread) = thread else {
return;
};
if !crate::server_push::is_configured_watcher(state, sender)
&& !matches!(
crate::acl::check_acl(&state.acl_ks, sender).await,
Ok(crate::acl::Role::Service)
)
{
warn!(sender, "acknowledgement ignored: Service role required");
return;
}
match crate::outbox::acknowledge(&state.store, sender, thread).await {
Ok(true) => {
crate::registry::record_ack(
&state.registry_ks,
sender,
crate::auth::session::now_epoch(),
)
.await;
state.outbox_notify.notify_one();
}
Ok(false) => tracing::debug!(sender, thread, "acknowledgement for no awaited entry"),
Err(e) => warn!(sender, error = %e, "failed to settle acknowledged outbox entry"),
}
}