use crate::http::HttpAppState; use crate::http::auth::authorize_repo_access; use crate::http::handler::GitHttpHandler; use crate::http::utils::get_repo_model; use crate::ssh::RepoReceiveSyncTask; use actix_web::{Error, HttpRequest, HttpResponse, web}; use std::path::PathBuf; use std::time::Duration; use tokio::time::timeout; pub async fn info_refs( req: HttpRequest, path: web::Path<(String, String)>, state: web::Data, ) -> Result { let ip = extract_ip(&req); if !state.rate_limiter.is_ip_read_allowed(&ip).await { return Err(actix_web::error::ErrorTooManyRequests( "Rate limit exceeded", )); } let service_param = req .query_string() .split('&') .find(|s| s.starts_with("service=")) .and_then(|s| s.strip_prefix("service=")) .ok_or_else(|| actix_web::error::ErrorBadRequest("Missing service parameter"))?; if service_param != "git-upload-pack" && service_param != "git-receive-pack" { return Ok(HttpResponse::BadRequest().body("Invalid service")); } let path_inner = path.into_inner(); let model = get_repo_model(&path_inner.0, &path_inner.1, &state.db).await?; let is_write = service_param == "git-receive-pack"; authorize_repo_access(&req, &state.db, &model, is_write).await?; let storage_path = PathBuf::from(&model.storage_path); let handler = GitHttpHandler::new(storage_path, model, state.db.clone()); handler.info_refs(service_param).await } pub async fn upload_pack( req: HttpRequest, path: web::Path<(String, String)>, payload: web::Payload, state: web::Data, ) -> Result { let ip = extract_ip(&req); if !state.rate_limiter.is_ip_read_allowed(&ip).await { return Err(actix_web::error::ErrorTooManyRequests( "Rate limit exceeded", )); } let path_inner = path.into_inner(); let model = get_repo_model(&path_inner.0, &path_inner.1, &state.db).await?; authorize_repo_access(&req, &state.db, &model, false).await?; let storage_path = PathBuf::from(&model.storage_path); let handler = GitHttpHandler::new(storage_path, model, state.db.clone()); handler.upload_pack(payload).await } pub async fn receive_pack( req: HttpRequest, path: web::Path<(String, String)>, payload: web::Payload, state: web::Data, ) -> Result { let ip = extract_ip(&req); if !state.rate_limiter.is_ip_write_allowed(&ip).await { return Err(actix_web::error::ErrorTooManyRequests( "Rate limit exceeded", )); } let path_inner = path.into_inner(); let model = get_repo_model(&path_inner.0, &path_inner.1, &state.db).await?; authorize_repo_access(&req, &state.db, &model, true).await?; let storage_path = PathBuf::from(&model.storage_path); let handler = GitHttpHandler::new(storage_path, model.clone(), state.db.clone()); let result = handler.receive_pack(payload).await; let _ = tokio::spawn({ let sync = state.sync.clone(); let repo_uid = model.id; async move { let _ = timeout( Duration::from_secs(5), sync.send(RepoReceiveSyncTask { repo_uid }), ) .await; } }); result } fn extract_ip(req: &HttpRequest) -> String { req.connection_info() .realip_remote_addr() .unwrap_or("unknown") .to_string() }