163 lines
4.7 KiB
Rust
163 lines
4.7 KiB
Rust
use crate::hook::HookService;
|
|
use actix_web::{App, HttpResponse, HttpServer, 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>,
|
|
pub config: AppConfig,
|
|
}
|
|
|
|
async fn robots(state: web::Data<HttpAppState>) -> HttpResponse {
|
|
let sitemap_url = state
|
|
.config
|
|
.git_http_domain()
|
|
.map(|d| format!("{}/sitemap.xml", d.trim_end_matches('/')))
|
|
.unwrap_or_default();
|
|
|
|
let body = if sitemap_url.is_empty() {
|
|
"User-agent: *\nDisallow: /\n".to_string()
|
|
} else {
|
|
format!("User-agent: *\nDisallow: /\n\nSitemap: {sitemap_url}\n")
|
|
};
|
|
|
|
HttpResponse::Ok()
|
|
.content_type("text/plain; charset=utf-8")
|
|
.body(body)
|
|
}
|
|
|
|
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("/robots.txt", web::get().to(robots))
|
|
.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 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,
|
|
config: config.clone(),
|
|
};
|
|
|
|
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(())
|
|
}
|