gitdataai/libs/git/http/mod.rs
ZhenYi 10836730ed
Some checks are pending
CI / Rust Lint & Check (push) Waiting to run
CI / Rust Tests (push) Waiting to run
CI / Frontend Lint & Type Check (push) Waiting to run
CI / Frontend Build (push) Blocked by required conditions
feat: add health endpoints and Prometheus metrics to git-hook and email-worker
Health monitoring:
- gitserver: /health endpoint on port 8021 (DB + Redis ping)
- git-hook: hyper health server on port 8083 with /health
- email-worker: hyper health server on port 8084 with /health
- K8s probes updated to httpGet for all three services

Metrics (via /metrics endpoint):
- git-hook: hook_tasks_total/success/failed/locked/retried/exhausted,
  hook_sync_branches/tags_changed_total
- email: email_queued/consumed/sent/failed_total,
  email_validation_skipped/build_errors/send_attempts_total
2026-04-25 23:45:48 +08:00

144 lines
4.0 KiB
Rust

use crate::hook::HookService;
use actix_web::{App, HttpServer, HttpResponse, web};
use config::AppConfig;
use db::cache::AppCache;
use db::database::AppDatabase;
use sea_orm::ConnectionTrait;
use std::sync::Arc;
pub mod auth;
pub mod handler;
pub mod lfs;
pub mod lfs_routes;
pub mod rate_limit;
pub mod routes;
pub mod utils;
#[derive(Clone)]
pub struct HttpAppState {
pub db: AppDatabase,
pub cache: AppCache,
pub sync: crate::ssh::ReceiveSyncService,
pub rate_limiter: Arc<rate_limit::RateLimiter>,
}
async fn health(state: web::Data<HttpAppState>) -> HttpResponse {
let db_ok = state
.db
.query_one_raw(sea_orm::Statement::from_string(
sea_orm::DbBackend::Postgres,
"SELECT 1",
))
.await
.is_ok();
let cache_ok = state.cache.conn().await.is_ok();
if db_ok && cache_ok {
HttpResponse::Ok().json(serde_json::json!({
"status": "ok",
"db": "ok",
"cache": "ok",
}))
} else {
HttpResponse::ServiceUnavailable().json(serde_json::json!({
"status": "unhealthy",
"db": if db_ok { "ok" } else { "error" },
"cache": if cache_ok { "ok" } else { "error" },
}))
}
}
pub fn git_http_cfg(cfg: &mut web::ServiceConfig) {
cfg.route("/health", web::get().to(health))
.route(
"/{namespace}/{repo_name}.git/info/refs",
web::get().to(routes::info_refs),
)
.route(
"/{namespace}/{repo_name}.git/git-upload-pack",
web::post().to(routes::upload_pack),
)
.route(
"/{namespace}/{repo_name}.git/git-receive-pack",
web::post().to(routes::receive_pack),
)
.route(
"/{namespace}/{repo_name}.git/info/lfs/objects/batch",
web::post().to(lfs_routes::lfs_batch),
)
.route(
"/{namespace}/{repo_name}.git/info/lfs/objects/{oid}",
web::put().to(lfs_routes::lfs_upload),
)
.route(
"/{namespace}/{repo_name}.git/info/lfs/objects/{oid}",
web::get().to(lfs_routes::lfs_download),
)
.route(
"/{namespace}/{repo_name}.git/info/lfs/locks",
web::post().to(lfs_routes::lfs_lock_create),
)
.route(
"/{namespace}/{repo_name}.git/info/lfs/locks",
web::get().to(lfs_routes::lfs_lock_list),
)
.route(
"/{namespace}/{repo_name}.git/info/lfs/locks/{id}",
web::get().to(lfs_routes::lfs_lock_get),
)
.route(
"/{namespace}/{repo_name}.git/info/lfs/locks/{id}",
web::delete().to(lfs_routes::lfs_lock_delete),
);
}
pub async fn run_http(config: AppConfig) -> anyhow::Result<()> {
let (db, app_cache) = tokio::join!(AppDatabase::init(&config), AppCache::init(&config),);
let db = db?;
let app_cache = app_cache?;
let redis_pool = app_cache.redis_pool().clone();
let hook = HookService::new(
db.clone(),
app_cache.clone(),
redis_pool.clone(),
config.clone(),
);
let _worker_cancel = hook.start_worker();
tracing::info!("hook worker started");
let sync = crate::ssh::ReceiveSyncService::new(redis_pool.clone());
let rate_limiter = Arc::new(rate_limit::RateLimiter::new(
rate_limit::RateLimitConfig::default(),
));
let _cleanup = rate_limiter.clone().start_cleanup();
let state = HttpAppState {
db: db.clone(),
cache: app_cache.clone(),
sync,
rate_limiter,
};
tracing::info!("Starting git HTTP server on 0.0.0.0:8021");
let server = HttpServer::new(move || {
App::new()
.app_data(web::Data::new(state.clone()))
.configure(git_http_cfg)
})
.bind("0.0.0.0:8021")?
.run();
// Await the server. Actix-web handles Ctrl+C gracefully by default:
// workers finish in-flight requests then exit (graceful shutdown).
let result = server.await;
if let Err(e) = result {
tracing::error!("HTTP server error: {}", e);
}
tracing::info!("Git HTTP server stopped");
Ok(())
}