use crate::hook::HookService; use actix_web::{App, HttpServer, web}; use config::AppConfig; use db::cache::AppCache; use db::database::AppDatabase; use slog::{Logger, error, info}; 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, pub logger: Logger, } pub fn git_http_cfg(cfg: &mut web::ServiceConfig) { cfg.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, logger: Logger) -> 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 http_client = Arc::new(reqwest::Client::new()); let hook = HookService::new( db.clone(), app_cache.clone(), redis_pool.clone(), logger.clone(), config.clone(), http_client, ); let _worker_cancel = hook.start_worker(); slog::info!(logger, "hook worker started"); let sync = crate::ssh::ReceiveSyncService::new(redis_pool.clone(), logger.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, logger: logger.clone(), }; let logger_startup = logger.clone(); info!(&logger_startup, "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 { error!(&logger, "HTTP server error: {}", e); } info!(&logger, "Git HTTP server stopped"); Ok(()) }