use axum::{
extract::State,
http::StatusCode,
response::Json,
};
use serde_json::{json, Value};
use tracing::{info, warn};
use crate::state::AppState;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
static SHUTDOWN_REQUESTED: AtomicBool = AtomicBool::new(false);
pub async fn shutdown(
State(_state): State<AppState>,
) -> Result<Json<Value>, StatusCode> {
info!("Graceful shutdown requested via admin endpoint");
SHUTDOWN_REQUESTED.store(true, Ordering::Relaxed);
tokio::spawn(async {
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
warn!("Initiating graceful shutdown...");
std::process::exit(0);
});
Ok(Json(json!({
"status": "ok",
"message": "Graceful shutdown initiated",
"timestamp": chrono::Utc::now().to_rfc3339()
})))
}
pub async fn system_status(
State(state): State<AppState>,
) -> Result<Json<Value>, StatusCode> {
let uptime_seconds = state.start_time.elapsed().as_secs();
let shutdown_requested = SHUTDOWN_REQUESTED.load(Ordering::Relaxed);
Ok(Json(json!({
"status": "ok",
"server": {
"name": "minifly-api",
"version": env!("CARGO_PKG_VERSION"),
"uptime_seconds": uptime_seconds,
"uptime_human": format_duration(uptime_seconds),
"shutdown_requested": shutdown_requested
},
"configuration": {
"port": state.config.port,
"data_directory": state.config.data_dir,
"log_level": "info" },
"timestamp": chrono::Utc::now().to_rfc3339()
})))
}
pub fn is_shutdown_requested() -> bool {
SHUTDOWN_REQUESTED.load(Ordering::Relaxed)
}
fn format_duration(seconds: u64) -> String {
let hours = seconds / 3600;
let minutes = (seconds % 3600) / 60;
let seconds = seconds % 60;
if hours > 0 {
format!("{}h {}m {}s", hours, minutes, seconds)
} else if minutes > 0 {
format!("{}m {}s", minutes, seconds)
} else {
format!("{}s", seconds)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_duration() {
assert_eq!(format_duration(45), "45s");
assert_eq!(format_duration(90), "1m 30s");
assert_eq!(format_duration(3665), "1h 1m 5s");
assert_eq!(format_duration(7200), "2h 0m 0s");
}
}