mod manager; mod package; pub use manager::RepoGroupManager; use axum::extract::{BodyStream, Path, State}; use axum::http::StatusCode; use axum::routing::{delete, get_service, post}; use axum::Router; use futures::StreamExt; use std::sync::Arc; use tokio::{fs, io::AsyncWriteExt}; use tower_http::services::ServeDir; use uuid::Uuid; pub fn router(global: &crate::Global) -> Router { // Try to serve packages by default, and try the database files instead if not found let serve_repos = get_service( ServeDir::new(&global.config.pkg_dir).fallback(ServeDir::new(&global.config.repo_dir)), ); Router::new() .route( "/:repo", post(post_package_archive) .delete(delete_repo) .get(serve_repos.clone()), ) .route("/:repo/:arch", delete(delete_arch_repo)) .fallback(serve_repos) .with_state(global.clone()) } async fn post_package_archive( State(global): State, Path(repo): Path, mut body: BodyStream, ) -> crate::Result<()> { // We first stream the uploaded file to disk let uuid: uuid::fmt::Simple = Uuid::new_v4().into(); let path = global.config.pkg_dir.join(uuid.to_string()); let mut f = fs::File::create(&path).await?; while let Some(chunk) = body.next().await { f.write_all(&chunk?).await?; } let clone = Arc::clone(&global.repo_manager); tokio::task::spawn_blocking(move || clone.write().unwrap().add_pkg_from_path(&repo, &path)) .await??; Ok(()) } async fn delete_repo( State(global): State, Path(repo): Path, ) -> crate::Result { let clone = Arc::clone(&global.repo_manager); let repo_removed = tokio::task::spawn_blocking(move || clone.write().unwrap().remove_repo(&repo)).await??; if repo_removed { Ok(StatusCode::OK) } else { Ok(StatusCode::NOT_FOUND) } } async fn delete_arch_repo( State(global): State, Path((repo, arch)): Path<(String, String)>, ) -> crate::Result { let clone = Arc::clone(&global.repo_manager); let repo_removed = tokio::task::spawn_blocking(move || clone.write().unwrap().remove_arch_repo(&repo, &arch)) .await??; if repo_removed { Ok(StatusCode::OK) } else { Ok(StatusCode::NOT_FOUND) } }