use axum::Extension;
use axum::Json;
use axum::extract::RawQuery;
use axum::http::HeaderMap;
use axum::response::{IntoResponse, Response};
use super::audit_view::{AuditRow, project};
use super::common::{AdminError, AdminState, verify_and_authorize_admin_only};
const LXM: &str = "tools.cairn.admin.getAuditLog";
pub(super) async fn handler(
Extension(state): Extension<AdminState>,
headers: HeaderMap,
RawQuery(query): RawQuery,
) -> Response {
if let Err(e) = verify_and_authorize_admin_only(&state, &headers, LXM).await {
return e.into_response();
}
let id = match parse_id(query.as_deref().unwrap_or("")) {
Ok(n) => n,
Err(e) => return e.into_response(),
};
let row = sqlx::query_as!(
AuditRow,
r#"SELECT
id AS "id!: i64",
created_at AS "created_at!: i64",
action AS "action!: String",
actor_did AS "actor_did!: String",
target,
target_cid,
outcome AS "outcome!: String",
reason,
prev_hash,
row_hash
FROM audit_log WHERE id = ?1"#,
id,
)
.fetch_optional(&state.pool)
.await;
match row {
Ok(Some(r)) => match project(r) {
Ok(entry) => Json(entry).into_response(),
Err(_) => AdminError::Internal.into_response(),
},
Ok(None) => AdminError::AuditEntryNotFound.into_response(),
Err(_) => AdminError::Internal.into_response(),
}
}
fn parse_id(raw: &str) -> Result<i64, AdminError> {
for (k, v) in form_urlencoded::parse(raw.as_bytes()) {
if k.as_ref() == "id" {
return v
.parse::<i64>()
.map_err(|_| AdminError::InvalidRequest("id must be integer"));
}
}
Err(AdminError::InvalidRequest("id required"))
}