Files
nx9-url-shortener/src/web/system.rs
T
thakares b66703082f
Rust CI / Test & Quality Checks (push) Canceled after 0s
Rust CI / Build Docker Image (push) Canceled after 0s
Refactor responsive UI and build metadata
2026-08-10 17:46:49 +05:30

180 lines
5.2 KiB
Rust

use axum::{
extract::State,
http::{HeaderMap, StatusCode},
response::{IntoResponse, Json, Response},
};
use axum_extra::extract::CookieJar;
use serde::Serialize;
use crate::auth::{authenticate_admin_session, authenticate_api_key};
use crate::db::admin::get_user_count;
use crate::state::AppState;
use crate::utils::{get_db_file_info, get_memory_usage};
// Helper: authenticate system request via header or session cookie
fn authenticate_request(state: &AppState, jar: &CookieJar, headers: &HeaderMap) -> bool {
// 1. Try Authorization header
if let Some(auth_header) = headers.get("Authorization").and_then(|h| h.to_str().ok()) {
let admin_conn = state.admin_db.lock().unwrap();
let users_conn = state.users_db.lock().unwrap();
if let Ok(Some(_)) = authenticate_api_key(&admin_conn, &users_conn, auth_header) {
return true;
}
}
// 2. Try cookie session
let conn = state.users_db.lock().unwrap();
if let Ok(Some(_)) = authenticate_admin_session(&conn, jar) {
return true;
}
false
}
#[derive(Serialize)]
pub struct StatusResponse {
pub application: &'static str,
pub database: String,
pub queue_size: usize,
pub memory_usage: String,
pub uptime_seconds: u64,
pub version: &'static str,
pub git_commit: &'static str,
}
// GET /status
pub async fn status_endpoint(
State(state): State<AppState>,
jar: CookieJar,
headers: HeaderMap,
) -> Response {
if !authenticate_request(&state, &jar, &headers) {
// Return public basic status for container/load-balancer health checks
return (
StatusCode::OK,
Json(serde_json::json!({ "application": "Healthy" })),
)
.into_response();
}
let is_db_ok = {
let conn = state.admin_db.lock().unwrap();
get_user_count(&conn).is_ok()
};
let db_status = if is_db_ok {
format!(
"Connected (WAL Mode enabled). Files Info:\n{}",
get_db_file_info(&state.config.data_dir)
)
} else {
"Disconnected".to_string()
};
let uptime = state.start_time.elapsed().as_secs();
Json(StatusResponse {
application: "Healthy",
database: db_status,
queue_size: 0,
memory_usage: get_memory_usage(),
uptime_seconds: uptime,
version: crate::build_info::APP_VERSION,
git_commit: crate::build_info::GIT_COMMIT,
})
.into_response()
}
// GET /metrics
pub async fn metrics_endpoint(
State(state): State<AppState>,
jar: CookieJar,
headers: HeaderMap,
) -> Response {
if !authenticate_request(&state, &jar, &headers) {
return StatusCode::UNAUTHORIZED.into_response();
}
// 1. Gather stats from DBs
let (total_urls, active_links, dead_links) = {
let conn = state.content_db.lock().unwrap();
crate::db::content::get_url_counts(&conn).unwrap_or((0, 0, 0))
};
let total_pages = {
let conn = state.content_db.lock().unwrap();
crate::db::content::get_landing_page_count(&conn).unwrap_or(0)
};
let (total_clicks, total_page_views) = {
let conn = state.analytics_db.lock().unwrap();
(
crate::db::analytics::get_total_clicks(&conn).unwrap_or(0),
crate::db::analytics::get_total_page_views(&conn).unwrap_or(0),
)
};
// Calculate memory in bytes
let mut mem_bytes = 0;
if let Ok(statm) = std::fs::read_to_string("/proc/self/statm") {
if let Some(pages_str) = statm.split_whitespace().next() {
if let Ok(pages) = pages_str.parse::<u64>() {
mem_bytes = pages * 4096;
}
}
}
let uptime = state.start_time.elapsed().as_secs();
// 2. Format as Prometheus metrics text
let metrics_text = format!(
r#"# HELP bzod_urls_total Total number of registered short URLs
# TYPE bzod_urls_total gauge
bzod_urls_total {total_urls}
# HELP bzod_active_urls Total number of active/healthy short URLs
# TYPE bzod_active_urls gauge
bzod_active_urls {active_links}
# HELP bzod_dead_urls Total number of dead short URLs
# TYPE bzod_dead_urls gauge
bzod_dead_urls {dead_links}
# HELP bzod_pages_total Total number of registered landing pages
# TYPE bzod_pages_total gauge
bzod_pages_total {total_pages}
# HELP bzod_clicks_total Total number of URL clicks recorded
# TYPE bzod_clicks_total counter
bzod_clicks_total {total_clicks}
# HELP bzod_page_views_total Total number of page views recorded
# TYPE bzod_page_views_total counter
bzod_page_views_total {total_page_views}
# HELP bzod_memory_bytes Memory usage of the bzod process in bytes
# TYPE bzod_memory_bytes gauge
bzod_memory_bytes {mem_bytes}
# HELP bzod_uptime_seconds Uptime of the bzod process in seconds
# TYPE bzod_uptime_seconds counter
bzod_uptime_seconds {uptime}
"#,
total_urls = total_urls,
active_links = active_links,
dead_links = dead_links,
total_pages = total_pages,
total_clicks = total_clicks,
total_page_views = total_page_views,
mem_bytes = mem_bytes,
uptime = uptime
);
(
StatusCode::OK,
[("Content-Type", "text/plain; version=0.0.4; charset=utf-8")],
metrics_text,
)
.into_response()
}