use affinidi_did_resolver_cache_sdk::DIDCacheClient;
use chrono::Utc;
use tracing::info;
use crate::auth::AuthClaims;
use crate::error::AppError;
use crate::store::KeyspaceHandle;
use crate::webvh_store;
use vta_sdk::protocols::did_management::servers::{
AddWebvhServerResultBody, ListWebvhServersResultBody, RemoveWebvhServerResultBody,
UpdateWebvhServerResultBody,
};
use vta_sdk::webvh::WebvhServerRecord;
pub async fn add_webvh_server(
webvh_ks: &KeyspaceHandle,
auth: &AuthClaims,
id: &str,
server_did: &str,
label: Option<String>,
did_resolver: &DIDCacheClient,
channel: &str,
) -> Result<AddWebvhServerResultBody, AppError> {
auth.require_super_admin()?;
if webvh_store::get_server(webvh_ks, id).await?.is_some() {
return Err(AppError::Conflict(format!(
"webvh server already exists: {id}"
)));
}
validate_server_did(did_resolver, server_did).await?;
let now = Utc::now();
let record = WebvhServerRecord {
id: id.to_string(),
did: server_did.to_string(),
label,
access_token: None,
access_expires_at: None,
refresh_token: None,
created_at: now,
updated_at: now,
};
webvh_store::store_server(webvh_ks, &record).await?;
info!(channel, id = %id, did = %server_did, "webvh server added");
Ok(record)
}
pub async fn list_webvh_servers(
webvh_ks: &KeyspaceHandle,
auth: &AuthClaims,
channel: &str,
) -> Result<ListWebvhServersResultBody, AppError> {
let servers = webvh_store::list_servers(webvh_ks).await?;
info!(channel, caller = %auth.did, count = servers.len(), "webvh servers listed");
Ok(ListWebvhServersResultBody { servers })
}
pub async fn update_webvh_server(
webvh_ks: &KeyspaceHandle,
auth: &AuthClaims,
id: &str,
label: Option<String>,
channel: &str,
) -> Result<UpdateWebvhServerResultBody, AppError> {
auth.require_super_admin()?;
let mut record = webvh_store::get_server(webvh_ks, id)
.await?
.ok_or_else(|| AppError::NotFound(format!("webvh server not found: {id}")))?;
if let Some(lbl) = label {
record.label = if lbl.is_empty() { None } else { Some(lbl) };
}
record.updated_at = Utc::now();
webvh_store::store_server(webvh_ks, &record).await?;
info!(channel, id = %id, "webvh server updated");
Ok(record)
}
pub async fn remove_webvh_server(
webvh_ks: &KeyspaceHandle,
auth: &AuthClaims,
id: &str,
channel: &str,
) -> Result<RemoveWebvhServerResultBody, AppError> {
auth.require_super_admin()?;
webvh_store::get_server(webvh_ks, id)
.await?
.ok_or_else(|| AppError::NotFound(format!("webvh server not found: {id}")))?;
webvh_store::delete_server(webvh_ks, id).await?;
info!(channel, id = %id, "webvh server removed");
Ok(RemoveWebvhServerResultBody {
id: id.to_string(),
removed: true,
})
}
pub(super) async fn validate_server_did(
did_resolver: &DIDCacheClient,
server_did: &str,
) -> Result<(), AppError> {
let resolved = did_resolver.resolve(server_did).await.map_err(|e| {
AppError::Validation(format!("failed to resolve server DID {server_did}: {e}"))
})?;
let has_supported_service = resolved.doc.service.iter().any(|svc| {
svc.type_
.iter()
.any(|t| t == "WebVHHostingService" || t == "DIDCommMessaging")
});
if !has_supported_service {
return Err(AppError::Validation(format!(
"server DID {server_did} has no WebVHHostingService or DIDCommMessaging service endpoint"
)));
}
Ok(())
}