Files
nx9-url-shortener/src/web/admin/health.rs
T
thakares f49698bb5c
Rust CI / Test & Quality Checks (push) Canceled after 0s
Rust CI / Build Docker Image (push) Canceled after 0s
Release v0.6.0
2026-08-09 17:17:57 +05:30

170 lines
5.6 KiB
Rust

use super::*;
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct JobHistoryRow {
pub id: String,
pub job_name: String,
pub status: String,
pub started_at: String,
pub finished_at: Option<String>,
pub error_message: Option<String>,
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct HealthCheckRow {
pub id: String,
pub object_type: String,
pub object_id: String,
pub checked_at: String,
pub status_code: Option<i64>,
pub error_message: Option<String>,
pub is_healthy: i64,
}
// GET /admin/health
pub async fn health_get(
State(state): State<AppState>,
jar: CookieJar,
Query(params): Query<HashMap<String, String>>,
) -> Response {
let (user, session_id) = match require_auth(&state, &jar).await {
Ok(u) => u,
Err(redir) => return redir.into_response(),
};
let mut db_reports = vec![];
if let Ok(r) =
crate::db::sqlite::collect_health_report(&state.admin_db.lock().unwrap(), "admin")
{
db_reports.push(r);
}
if let Ok(r) =
crate::db::sqlite::collect_health_report(&state.system_db.lock().unwrap(), "system")
{
db_reports.push(r);
}
if let Ok(r) =
crate::db::sqlite::collect_health_report(&state.users_db.lock().unwrap(), "users")
{
db_reports.push(r);
}
let system_db_path = state.config.data_dir.join("admin").join("system.db");
let users_db_path = state.config.data_dir.join("admin").join("users.db");
let admin_db_path = state.config.data_dir.join("admin").join("admin.db");
let system_db_size = format_size(
std::fs::metadata(&system_db_path)
.map(|m| m.len())
.unwrap_or(0),
);
let users_db_size = format_size(
std::fs::metadata(&users_db_path)
.map(|m| m.len())
.unwrap_or(0),
);
let admin_db_size = format_size(
std::fs::metadata(&admin_db_path)
.map(|m| m.len())
.unwrap_or(0),
);
let users_dir = state.config.data_dir.join("users");
let tenants_db_size = format_size(get_dir_size(&users_dir).unwrap_or(0));
let total_data_size = format_size(get_dir_size(&state.config.data_dir).unwrap_or(0));
let job_history = {
let conn = state.system_db.lock().unwrap();
let mut stmt = conn.prepare("SELECT id, job_name, status, started_at, finished_at, error_message FROM job_history ORDER BY started_at DESC LIMIT 20;").unwrap();
let rows = stmt
.query_map([], |row| {
Ok(JobHistoryRow {
id: row.get(0)?,
job_name: row.get(1)?,
status: row.get(2)?,
started_at: row.get(3)?,
finished_at: row.get(4)?,
error_message: row.get(5)?,
})
})
.unwrap();
rows.filter_map(|r| r.ok()).collect()
};
let health_checks = {
let conn = state.system_db.lock().unwrap();
let mut stmt = conn.prepare("SELECT id, object_type, object_id, checked_at, status_code, error_message, is_healthy FROM health_checks ORDER BY checked_at DESC LIMIT 20;").unwrap();
let rows = stmt
.query_map([], |row| {
Ok(HealthCheckRow {
id: row.get(0)?,
object_type: row.get(1)?,
object_id: row.get(2)?,
checked_at: row.get(3)?,
status_code: row.get(4)?,
error_message: row.get(5)?,
is_healthy: row.get(6)?,
})
})
.unwrap();
rows.filter_map(|r| r.ok()).collect()
};
let csrf_token = generate_csrf_token(&session_id);
let (registry_errors, registry_warnings) = {
let system_conn = state.system_db.lock().unwrap();
let users_conn = state.users_db.lock().unwrap();
match crate::services::registry_validator::RegistryValidator::scan(
&system_conn,
&users_conn,
&state.config.data_dir,
None,
) {
Ok(issues) => {
let mut errors = Vec::new();
let mut warnings = Vec::new();
for issue in issues {
use crate::services::registry_validator::RegistryIssueType;
match issue.issue_type {
RegistryIssueType::StaleReservation
| RegistryIssueType::TenantAdminHasIsolatedContent => {
warnings.push(format!(
"Warning for slug {}: {}",
issue.slug, issue.description
));
}
_ => {
errors.push(format!(
"Error for slug {}: {}",
issue.slug, issue.description
));
}
}
}
(errors, warnings)
}
Err(e) => (vec![format!("Failed to run registry scan: {}", e)], vec![]),
}
};
let template = crate::templates::HealthTemplate {
admin_username: user.username,
db_reports,
total_data_size,
system_db_size,
users_db_size,
admin_db_size,
tenants_db_size,
job_history,
health_checks,
registry_errors,
registry_warnings,
csrf_token,
success: params.get("success").cloned(),
error: params.get("error").cloned(),
};
template.into_response()
}