use crate::orm::Db;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HealthStatus {
Ok,
Warn,
Error,
}
impl HealthStatus {
pub fn as_str(&self) -> &'static str {
match self {
HealthStatus::Ok => "ok",
HealthStatus::Warn => "warn",
HealthStatus::Error => "error",
}
}
}
#[derive(Debug, Clone)]
pub struct HealthCheck {
pub label: &'static str,
pub status: HealthStatus,
pub message: String,
}
pub(crate) async fn gather_checks(db: &Db) -> Vec<HealthCheck> {
let mut out: Vec<HealthCheck> = Vec::new();
let db_ok: bool = sqlx::query_scalar::<_, i32>("SELECT 1")
.fetch_one(db.pool())
.await
.map(|n| n == 1)
.unwrap_or(false);
out.push(HealthCheck {
label: "Postgres reachable",
status: if db_ok {
HealthStatus::Ok
} else {
HealthStatus::Error
},
message: if db_ok {
"Pool accepted `SELECT 1`.".to_string()
} else {
"Pool query failed. Check `DATABASE_URL` + network.".to_string()
},
});
let auth_exists: bool = sqlx::query_scalar(
"SELECT EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_name = 'rustio_users'
)",
)
.fetch_one(db.pool())
.await
.unwrap_or(false);
out.push(HealthCheck {
label: "Auth tables present",
status: if auth_exists {
HealthStatus::Ok
} else {
HealthStatus::Error
},
message: if auth_exists {
"rustio_users + rustio_sessions are in place.".to_string()
} else {
"Boot the app once or run `rustio-admin user create` to seed them.".to_string()
},
});
let admin_count: i64 = if auth_exists {
sqlx::query_scalar(
"SELECT COUNT(*) FROM rustio_users \
WHERE role IN ('administrator', 'developer') AND is_active = TRUE",
)
.fetch_one(db.pool())
.await
.unwrap_or(0)
} else {
0
};
out.push(HealthCheck {
label: "Active administrator",
status: if admin_count > 0 {
HealthStatus::Ok
} else {
HealthStatus::Error
},
message: if admin_count > 0 {
format!("{admin_count} active administrator(s) on file.")
} else {
"No active administrator. Run `rustio-admin user create --email … --role administrator`."
.to_string()
},
});
match std::env::var("RUSTIO_SECRET_KEY") {
Ok(v) if v.len() >= 43 => out.push(HealthCheck {
label: "RUSTIO_SECRET_KEY",
status: HealthStatus::Ok,
message: format!("Set ({} chars).", v.len()),
}),
Ok(v) if v.is_empty() => out.push(HealthCheck {
label: "RUSTIO_SECRET_KEY",
status: HealthStatus::Warn,
message: "Empty. MFA enrol + emergency-access URL signing will refuse.".to_string(),
}),
Ok(v) => out.push(HealthCheck {
label: "RUSTIO_SECRET_KEY",
status: HealthStatus::Error,
message: format!(
"Too short ({} chars; need ≥ 43 URL-safe-base64 chars = 32 raw bytes).",
v.len()
),
}),
Err(_) => out.push(HealthCheck {
label: "RUSTIO_SECRET_KEY",
status: HealthStatus::Warn,
message: "Not set. MFA enrol + emergency-access URL signing will refuse.".to_string(),
}),
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn status_as_str_is_snake_case_and_stable() {
assert_eq!(HealthStatus::Ok.as_str(), "ok");
assert_eq!(HealthStatus::Warn.as_str(), "warn");
assert_eq!(HealthStatus::Error.as_str(), "error");
}
}