use axum::extract::{Path, State};
use axum::http::{HeaderValue, StatusCode, header};
use axum::response::IntoResponse;
use crate::server::AppState;
pub async fn did_log(State(state): State<AppState>, Path(scid): Path<String>) -> impl IntoResponse {
if !is_valid_scid(&scid) {
return (StatusCode::NOT_FOUND, "did log not found").into_response();
}
let config = state.config.read().await;
match config.vtc_did.as_deref().and_then(extract_scid_from_did) {
Some(expected) if expected == scid => {}
_ => return (StatusCode::NOT_FOUND, "did log not found").into_response(),
}
let path = config
.store
.data_dir
.join("did")
.join(format!("{scid}.jsonl"));
drop(config);
let body = match tokio::fs::read(&path).await {
Ok(bytes) => bytes,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
return (StatusCode::NOT_FOUND, "did log not found").into_response();
}
Err(e) => {
tracing::error!(error = %e, path = %path.display(), "failed to read did log");
return (StatusCode::INTERNAL_SERVER_ERROR, "did log read failed").into_response();
}
};
(
StatusCode::OK,
[(
header::CONTENT_TYPE,
HeaderValue::from_static("application/jsonl"),
)],
body,
)
.into_response()
}
fn is_valid_scid(s: &str) -> bool {
!s.is_empty()
&& s.len() <= 128
&& s.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
}
fn extract_scid_from_did(did: &str) -> Option<String> {
let suffix = did.strip_prefix("did:webvh:")?;
let last = suffix.split(':').next_back()?;
if is_valid_scid(last) {
Some(last.to_string())
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn valid_scid_accepts_alphanumeric() {
assert!(is_valid_scid("abc123"));
assert!(is_valid_scid("zQmPLwUBtaqz3a"));
assert!(is_valid_scid("foo-bar_baz"));
}
#[test]
fn valid_scid_rejects_separators_and_empty() {
assert!(!is_valid_scid(""));
assert!(!is_valid_scid("foo/bar"));
assert!(!is_valid_scid("foo:bar"));
assert!(!is_valid_scid("../etc/passwd"));
}
#[test]
fn extract_scid_from_did_pulls_last_component() {
assert_eq!(
extract_scid_from_did("did:webvh:vtc.example.com:abc123").as_deref(),
Some("abc123")
);
assert_eq!(
extract_scid_from_did("did:webvh:vtc.example.com:v1:abc123").as_deref(),
Some("abc123")
);
}
#[test]
fn extract_scid_from_did_returns_none_for_non_webvh() {
assert!(extract_scid_from_did("did:key:z6Mk…").is_none());
assert!(extract_scid_from_did("not a did").is_none());
}
#[test]
fn extract_scid_from_did_returns_none_when_last_component_invalid() {
assert!(extract_scid_from_did("did:webvh:vtc.example.com:foo/bar").is_none());
}
}