Implement self-hosted document scanner and media processing tools as an alternative to CamScanner/ilovepdf without third-party uploads. Backend: Rust Axum gateway + worker pool with NATS JetStream queue Frontend: Next.js 16 + shadcn/ui + Tailwind v4 + Framer Motion Pipeline: Canny edge detection -> DLT homography warp -> Sauvola binarization -> Hough deskew -> Tesseract OCR -> searchable PDF Phase 1 (MVP) delivers: - Document scanner with perspective correction and OCR - Image compress/resize/convert tools - PDF merge/split/compress tools - Real-time WebSocket progress updates - Rate limiting, auto-cleanup, Prometheus metrics - Full CI/CD pipeline with Docker multi-stage build Co-Authored-By: Kilo <kilo@kilo.ai>
69 lines
1.8 KiB
Rust
69 lines
1.8 KiB
Rust
use std::sync::Arc;
|
|
|
|
use axum::{extract::State, Json};
|
|
use redis::AsyncCommands;
|
|
use serde::Serialize;
|
|
|
|
use crate::metrics::Metrics;
|
|
|
|
/// Shared application state accessible from all handlers.
|
|
pub struct AppState {
|
|
pub redis: redis::Client,
|
|
pub nats: async_nats::Client,
|
|
pub config: crate::config::AppConfig,
|
|
pub metrics: Metrics,
|
|
}
|
|
|
|
/// Health check response.
|
|
#[derive(Serialize)]
|
|
pub struct HealthResponse {
|
|
pub status: String,
|
|
pub version: String,
|
|
pub redis: String,
|
|
pub nats: String,
|
|
pub uptime_seconds: u64,
|
|
}
|
|
|
|
/// Handle GET /health
|
|
pub async fn health_handler(
|
|
State(state): State<Arc<AppState>>,
|
|
) -> Json<HealthResponse> {
|
|
let redis_status = {
|
|
match state.redis.get_multiplexed_async_connection().await {
|
|
Ok(mut conn) => match redis::cmd("PING").query_async::<String>(&mut conn).await {
|
|
Ok(_) => "connected".to_string(),
|
|
Err(_) => "error".to_string(),
|
|
},
|
|
Err(_) => "disconnected".to_string(),
|
|
}
|
|
};
|
|
|
|
let nats_status = if state
|
|
.nats
|
|
.publish("tools.health.check", b"ping".to_vec().into())
|
|
.await
|
|
.is_ok()
|
|
{
|
|
"connected".to_string()
|
|
} else {
|
|
"disconnected".to_string()
|
|
};
|
|
|
|
Json(HealthResponse {
|
|
status: "ok".to_string(),
|
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
|
redis: redis_status,
|
|
nats: nats_status,
|
|
uptime_seconds: std::time::SystemTime::now()
|
|
.duration_since(std::time::UNIX_EPOCH)
|
|
.map(|d| d.as_secs())
|
|
.unwrap_or(0),
|
|
})
|
|
}
|
|
|
|
/// Handle GET /metrics
|
|
pub async fn metrics_handler(
|
|
State(state): State<Arc<AppState>>,
|
|
) -> Result<String, (axum::http::StatusCode, axum::Json<serde_json::Value>)> {
|
|
Ok(state.metrics.format())
|
|
} |