use crate::server::config::SecretsConfig;
use crate::server::secret_store::{self, SecretStore};
use std::path::Path;
const GREEN: &str = "\x1b[32m";
const RED: &str = "\x1b[31m";
const YELLOW: &str = "\x1b[33m";
const CYAN: &str = "\x1b[36m";
const DIM: &str = "\x1b[2m";
const BOLD: &str = "\x1b[1m";
const RESET: &str = "\x1b[0m";
pub fn pass(msg: &str) {
eprintln!(" {GREEN}[PASS]{RESET} {msg}");
}
pub fn fail(msg: &str) {
eprintln!(" {RED}[FAIL]{RESET} {msg}");
}
pub fn warn_msg(msg: &str) {
eprintln!(" {YELLOW}[WARN]{RESET} {msg}");
}
pub fn info_msg(msg: &str) {
eprintln!(" {CYAN}[INFO]{RESET} {msg}");
}
pub fn skip(msg: &str) {
eprintln!(" {DIM}[----]{RESET} {msg}");
}
pub fn feature_on(name: &str) {
eprintln!(" {GREEN}[ON ]{RESET} {name}");
}
pub fn feature_off(name: &str) {
eprintln!(" {DIM}[OFF]{RESET} {name}");
}
pub fn section(title: &str) {
eprintln!();
eprintln!(" {BOLD}{title}{RESET}");
}
pub fn header(service: &str, version: &str) {
eprintln!();
eprintln!(" {BOLD}Health Check — {service} v{version}{RESET}");
eprintln!(" {}", "=".repeat(40));
}
pub fn print_feature(name: &str, enabled: bool) {
if enabled {
feature_on(name);
} else {
feature_off(name);
}
}
pub fn check_config_loaded(path: &Path) {
pass(&format!("Config loaded from: {}", path.display()));
}
pub fn check_value(label: &str, value: &Option<String>) {
match value {
Some(v) => pass(&format!("{label}: {v}")),
None => skip(&format!("{label}: (not configured)")),
}
}
pub fn active_secrets_backend(secrets: &SecretsConfig) -> &'static str {
secret_store::backend_label(secrets)
}
pub async fn check_secrets(secrets_config: &SecretsConfig, config_path: &Path) {
let backend_name = active_secrets_backend(secrets_config);
let store: Box<dyn SecretStore> =
match secret_store::create_secret_store(secrets_config, config_path) {
Ok(s) => s,
Err(e) => {
fail(&format!("Secret store creation failed: {e}"));
return;
}
};
if secret_store::is_plaintext_backend(secrets_config) {
warn_msg(&format!("Secret store backend: {backend_name} (INSECURE)"));
} else {
pass(&format!("Secret store backend: {backend_name}"));
}
match store.get().await {
Ok(Some(_)) => pass("Secrets loaded (signing_key, key_agreement_key, jwt_signing_key)"),
Ok(None) => fail("No secrets found — run setup first"),
Err(e) => fail(&format!("Failed to load secrets: {e}")),
}
}
pub async fn check_store(
store_config: &crate::server::config::StoreConfig,
) -> Option<crate::server::store::Store> {
let backend_name = active_store_backend();
match crate::server::store::Store::open(store_config).await {
Ok(store) => {
pass(&format!(
"Store opened ({backend_name} @ {})",
store_config.data_dir.display()
));
Some(store)
}
Err(e) => {
let msg = e.to_string();
if msg.contains("lock") || msg.contains("Lock") {
warn_msg(&format!("Store locked (is the server running?): {msg}"));
} else {
fail(&format!("Store open failed: {msg}"));
}
None
}
}
}
pub fn active_store_backend() -> &'static str {
if cfg!(feature = "store-fjall") {
"fjall"
} else if cfg!(feature = "store-redis") {
"redis"
} else if cfg!(feature = "store-dynamodb") {
"dynamodb"
} else if cfg!(feature = "store-firestore") {
"firestore"
} else if cfg!(feature = "store-cosmosdb") {
"cosmosdb"
} else {
"unknown"
}
}
pub async fn check_did_resolution(label: &str, did: &str) -> bool {
use affinidi_did_resolver_cache_sdk::{DIDCacheClient, config::DIDCacheConfigBuilder};
let client = match DIDCacheClient::new(DIDCacheConfigBuilder::default().build()).await {
Ok(c) => c,
Err(e) => {
fail(&format!("{label}: DID resolver init failed: {e}"));
return false;
}
};
match client.resolve(did).await {
Ok(_) => {
pass(&format!("{label}: {did}"));
true
}
Err(e) => {
fail(&format!("{label}: {did} — {e}"));
false
}
}
}
pub async fn own_did_served_locally(
dids_ks: &crate::server::store::KeyspaceHandle,
expected_did_id: Option<&str>,
) -> bool {
let Some(expected) = expected_did_id else {
return true;
};
let record = match dids_ks
.get::<crate::did_ops::DidRecord>(crate::did_ops::did_key(".well-known"))
.await
{
Ok(r) => r,
Err(_) => return false,
};
let Some(record) = record else {
return true;
};
if record.disabled || record.deleted_at.is_some() {
return false;
}
if record.did_id.as_deref() != Some(expected) {
return false;
}
matches!(
dids_ks
.get_raw(crate::did_ops::content_log_key(".well-known"))
.await,
Ok(Some(_))
)
}
pub async fn check_url_reachable(label: &str, url: &str) -> bool {
let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(5))
.build()
.unwrap();
match client.get(url).send().await {
Ok(resp) => {
let status = resp.status();
if status.is_success() {
pass(&format!("{label}: {url}"));
} else {
warn_msg(&format!("{label}: {url} (HTTP {status})"));
}
true
}
Err(e) => {
fail(&format!("{label}: {url} — {e}"));
false
}
}
}
#[cfg(all(test, feature = "store-fjall"))]
mod own_did_served_locally_tests {
use super::*;
use crate::did_ops::{DidRecord, content_log_key, did_key};
use crate::server::config::StoreConfig;
use crate::server::store::{KS_DIDS, Store};
async fn dids_ks() -> (tempfile::TempDir, crate::server::store::KeyspaceHandle) {
let dir = tempfile::tempdir().expect("tempdir");
let store = Store::open(&StoreConfig {
data_dir: dir.path().to_path_buf(),
..StoreConfig::default()
})
.await
.expect("open store");
let ks = store.keyspace(KS_DIDS).expect("dids keyspace");
(dir, ks)
}
fn record(did_id: &str) -> DidRecord {
DidRecord {
owner: "system".into(),
mnemonic: ".well-known".into(),
created_at: 0,
updated_at: 0,
version_count: 1,
did_id: Some(did_id.into()),
content_size: 2,
disabled: false,
deleted_at: None,
method: "webvh".into(),
domain: String::new(),
services: None,
agent_names: Vec::new(),
}
}
#[tokio::test]
async fn healthy_when_nothing_expected() {
let (_dir, ks) = dids_ks().await;
assert!(own_did_served_locally(&ks, None).await);
}
#[tokio::test]
async fn healthy_when_well_known_absent() {
let (_dir, ks) = dids_ks().await;
assert!(own_did_served_locally(&ks, Some("did:webvh:abc:host.example")).await);
}
#[tokio::test]
async fn healthy_when_matching_and_content_present() {
let (_dir, ks) = dids_ks().await;
ks.insert(
did_key(".well-known"),
&record("did:webvh:abc:host.example"),
)
.await
.unwrap();
ks.insert_raw(content_log_key(".well-known"), b"{}".to_vec())
.await
.unwrap();
assert!(own_did_served_locally(&ks, Some("did:webvh:abc:host.example")).await);
}
#[tokio::test]
async fn unhealthy_when_did_id_mismatches() {
let (_dir, ks) = dids_ks().await;
ks.insert(
did_key(".well-known"),
&record("did:webvh:stale:old-host.example"),
)
.await
.unwrap();
ks.insert_raw(content_log_key(".well-known"), b"{}".to_vec())
.await
.unwrap();
assert!(!own_did_served_locally(&ks, Some("did:webvh:abc:new-host.example")).await);
}
#[tokio::test]
async fn unhealthy_when_disabled() {
let (_dir, ks) = dids_ks().await;
let mut r = record("did:webvh:abc:host.example");
r.disabled = true;
ks.insert(did_key(".well-known"), &r).await.unwrap();
ks.insert_raw(content_log_key(".well-known"), b"{}".to_vec())
.await
.unwrap();
assert!(!own_did_served_locally(&ks, Some("did:webvh:abc:host.example")).await);
}
#[tokio::test]
async fn unhealthy_when_content_missing() {
let (_dir, ks) = dids_ks().await;
ks.insert(
did_key(".well-known"),
&record("did:webvh:abc:host.example"),
)
.await
.unwrap();
assert!(!own_did_served_locally(&ks, Some("did:webvh:abc:host.example")).await);
}
}