From a6de2c3c149227919127a699c59409a01d533d4b Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Wed, 26 Jun 2024 21:52:02 +0200 Subject: [PATCH] refactor: move web code into own module --- server/src/main.rs | 13 +-- server/src/repo/mod.rs | 143 ------------------------- server/src/{ => web}/api/mod.rs | 0 server/src/{ => web}/api/pagination.rs | 0 server/src/web/mod.rs | 13 +++ server/src/web/repo.rs | 142 ++++++++++++++++++++++++ 6 files changed, 158 insertions(+), 153 deletions(-) rename server/src/{ => web}/api/mod.rs (100%) rename server/src/{ => web}/api/pagination.rs (100%) create mode 100644 server/src/web/mod.rs create mode 100644 server/src/web/repo.rs diff --git a/server/src/main.rs b/server/src/main.rs index 33865b5..337ba2e 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,18 +1,15 @@ -mod api; mod cli; mod config; pub mod db; mod error; mod repo; +mod web; pub use config::{Config, DbConfig, FsConfig}; pub use error::{Result, ServerError}; use std::{io, path::PathBuf}; -use axum::Router; -use tower_http::trace::TraceLayer; - use clap::Parser; use sea_orm_migration::MigratorTrait; use tokio::runtime; @@ -90,12 +87,8 @@ async fn run(global: Global) -> crate::Result<()> { .unwrap(); let listener = tokio::net::TcpListener::bind(domain).await?; - // build our application with a single route - let app = Router::new() - .nest("/api", crate::api::router()) - .merge(crate::repo::router(&global.config.api_key)) - .with_state(global) - .layer(TraceLayer::new_for_http()); + let app = web::router(global); + // run it with hyper on localhost:3000 Ok(axum::serve(listener, app.into_make_service()) .await diff --git a/server/src/repo/mod.rs b/server/src/repo/mod.rs index f48c0d7..16c368e 100644 --- a/server/src/repo/mod.rs +++ b/server/src/repo/mod.rs @@ -5,146 +5,3 @@ pub mod package; pub use actor::{RepoActor, RepoCommand, RepoSharedState}; pub use handle::Handle; - -use crate::FsConfig; - -use axum::{ - body::Body, - extract::{Path, State}, - http::{Request, StatusCode}, - response::IntoResponse, - routing::{delete, post}, - Router, -}; -use futures::TryStreamExt; -use tokio_util::io::StreamReader; -use tower::util::ServiceExt; -use tower_http::{services::ServeFile, validate_request::ValidateRequestHeaderLayer}; - -pub fn router(api_key: &str) -> Router { - Router::new() - .route( - "/:distro/:repo", - post(post_package_archive) - .delete(delete_repo) - .route_layer(ValidateRequestHeaderLayer::bearer(api_key)), - ) - .route( - "/:distro/:repo/:arch", - delete(delete_arch_repo).route_layer(ValidateRequestHeaderLayer::bearer(api_key)), - ) - // Routes added after the layer do not get that layer applied, so the GET requests will not - // be authorized - .route( - "/:distro/:repo/:arch/:filename", - delete(delete_package) - .route_layer(ValidateRequestHeaderLayer::bearer(api_key)) - .get(get_file), - ) -} - -/// Serve the package archive files and database archives. If files are requested for an -/// architecture that does not have any explicit packages, a repository containing only "any" files -/// is returned. -async fn get_file( - State(global): State, - Path((distro, repo, arch, file_name)): Path<(String, String, String, String)>, - req: Request, -) -> crate::Result { - if let Some(repo_id) = global.repo.get_repo(&distro, &repo).await? { - match global.config.fs { - FsConfig::Local { data_dir } => { - let repo_dir = data_dir.join("repos").join(repo_id.to_string()); - - let file_name = if file_name == format!("{}.db", repo) - || file_name == format!("{}.db.tar.gz", repo) - { - format!("{}.db.tar.gz", arch) - } else if file_name == format!("{}.files", repo) - || file_name == format!("{}.files.tar.gz", repo) - { - format!("{}.files.tar.gz", arch) - } else { - file_name - }; - - let path = repo_dir.join(file_name); - Ok(ServeFile::new(path).oneshot(req).await) - } - } - } else { - Err(StatusCode::NOT_FOUND.into()) - } -} - -async fn post_package_archive( - State(global): State, - Path((distro, repo)): Path<(String, String)>, - body: Body, -) -> crate::Result { - let repo_id = global.repo.get_or_create_repo(&distro, &repo).await?; - - let [tmp_path] = global.repo.random_file_paths(); - let mut tmp_file = tokio::fs::File::create(&tmp_path).await?; - let mut body = StreamReader::new(body.into_data_stream().map_err(std::io::Error::other)); - tokio::io::copy(&mut body, &mut tmp_file).await?; - - global.repo.queue_pkg(repo_id, tmp_path).await; - - Ok(StatusCode::ACCEPTED) -} - -async fn delete_repo( - State(global): State, - Path((distro, repo)): Path<(String, String)>, -) -> crate::Result { - if let Some(repo) = global.repo.get_repo(&distro, &repo).await? { - global.repo.remove_repo(repo).await?; - - tracing::info!("Removed repository {repo}"); - - Ok(StatusCode::OK) - } else { - Ok(StatusCode::NOT_FOUND) - } -} - -async fn delete_arch_repo( - State(global): State, - Path((distro, repo, arch)): Path<(String, String, String)>, -) -> crate::Result { - if let Some(repo) = global.repo.get_repo(&distro, &repo).await? { - global.repo.remove_repo_arch(repo, &arch).await?; - - tracing::info!("Removed architecture '{arch}' from repository {repo}"); - - Ok(StatusCode::OK) - } else { - Ok(StatusCode::NOT_FOUND) - } -} - -async fn delete_package( - State(global): State, - Path((distro, repo, arch, pkg_name)): Path<(String, String, String, String)>, -) -> crate::Result { - Ok(StatusCode::NOT_FOUND) - //if let Some(mgr) = global.mgr.get_mgr(&distro).await { - // let pkg_removed = mgr.remove_pkg(&repo, &arch, &pkg_name).await?; - // - // if pkg_removed { - // tracing::info!( - // "Removed package '{}' ({}) from repository '{}'", - // pkg_name, - // arch, - // repo - // ); - // - // Ok(StatusCode::OK) - // } else { - // Ok(StatusCode::NOT_FOUND) - // } - //} else { - // Ok(StatusCode::NOT_FOUND) - //} -} diff --git a/server/src/api/mod.rs b/server/src/web/api/mod.rs similarity index 100% rename from server/src/api/mod.rs rename to server/src/web/api/mod.rs diff --git a/server/src/api/pagination.rs b/server/src/web/api/pagination.rs similarity index 100% rename from server/src/api/pagination.rs rename to server/src/web/api/pagination.rs diff --git a/server/src/web/mod.rs b/server/src/web/mod.rs new file mode 100644 index 0000000..48e9cbb --- /dev/null +++ b/server/src/web/mod.rs @@ -0,0 +1,13 @@ +mod api; +mod repo; + +use axum::Router; +use tower_http::trace::TraceLayer; + +pub fn router(global: crate::Global) -> Router { + Router::new() + .nest("/api", api::router()) + .merge(repo::router(&global.config.api_key)) + .with_state(global) + .layer(TraceLayer::new_for_http()) +} diff --git a/server/src/web/repo.rs b/server/src/web/repo.rs new file mode 100644 index 0000000..d690895 --- /dev/null +++ b/server/src/web/repo.rs @@ -0,0 +1,142 @@ +use crate::FsConfig; + +use axum::{ + body::Body, + extract::{Path, State}, + http::{Request, StatusCode}, + response::IntoResponse, + routing::{delete, post}, + Router, +}; +use futures::TryStreamExt; +use tokio_util::io::StreamReader; +use tower::util::ServiceExt; +use tower_http::{services::ServeFile, validate_request::ValidateRequestHeaderLayer}; + +pub fn router(api_key: &str) -> Router { + Router::new() + .route( + "/:distro/:repo", + post(post_package_archive) + .delete(delete_repo) + .route_layer(ValidateRequestHeaderLayer::bearer(api_key)), + ) + .route( + "/:distro/:repo/:arch", + delete(delete_arch_repo).route_layer(ValidateRequestHeaderLayer::bearer(api_key)), + ) + // Routes added after the layer do not get that layer applied, so the GET requests will not + // be authorized + .route( + "/:distro/:repo/:arch/:filename", + delete(delete_package) + .route_layer(ValidateRequestHeaderLayer::bearer(api_key)) + .get(get_file), + ) +} + +/// Serve the package archive files and database archives. If files are requested for an +/// architecture that does not have any explicit packages, a repository containing only "any" files +/// is returned. +async fn get_file( + State(global): State, + Path((distro, repo, arch, file_name)): Path<(String, String, String, String)>, + req: Request, +) -> crate::Result { + if let Some(repo_id) = global.repo.get_repo(&distro, &repo).await? { + match global.config.fs { + FsConfig::Local { data_dir } => { + let repo_dir = data_dir.join("repos").join(repo_id.to_string()); + + let file_name = if file_name == format!("{}.db", repo) + || file_name == format!("{}.db.tar.gz", repo) + { + format!("{}.db.tar.gz", arch) + } else if file_name == format!("{}.files", repo) + || file_name == format!("{}.files.tar.gz", repo) + { + format!("{}.files.tar.gz", arch) + } else { + file_name + }; + + let path = repo_dir.join(file_name); + Ok(ServeFile::new(path).oneshot(req).await) + } + } + } else { + Err(StatusCode::NOT_FOUND.into()) + } +} + +async fn post_package_archive( + State(global): State, + Path((distro, repo)): Path<(String, String)>, + body: Body, +) -> crate::Result { + let repo_id = global.repo.get_or_create_repo(&distro, &repo).await?; + + let [tmp_path] = global.repo.random_file_paths(); + let mut tmp_file = tokio::fs::File::create(&tmp_path).await?; + let mut body = StreamReader::new(body.into_data_stream().map_err(std::io::Error::other)); + tokio::io::copy(&mut body, &mut tmp_file).await?; + + global.repo.queue_pkg(repo_id, tmp_path).await; + + Ok(StatusCode::ACCEPTED) +} + +async fn delete_repo( + State(global): State, + Path((distro, repo)): Path<(String, String)>, +) -> crate::Result { + if let Some(repo) = global.repo.get_repo(&distro, &repo).await? { + global.repo.remove_repo(repo).await?; + + tracing::info!("Removed repository {repo}"); + + Ok(StatusCode::OK) + } else { + Ok(StatusCode::NOT_FOUND) + } +} + +async fn delete_arch_repo( + State(global): State, + Path((distro, repo, arch)): Path<(String, String, String)>, +) -> crate::Result { + if let Some(repo) = global.repo.get_repo(&distro, &repo).await? { + global.repo.remove_repo_arch(repo, &arch).await?; + + tracing::info!("Removed architecture '{arch}' from repository {repo}"); + + Ok(StatusCode::OK) + } else { + Ok(StatusCode::NOT_FOUND) + } +} + +async fn delete_package( + State(global): State, + Path((distro, repo, arch, pkg_name)): Path<(String, String, String, String)>, +) -> crate::Result { + Ok(StatusCode::NOT_FOUND) + //if let Some(mgr) = global.mgr.get_mgr(&distro).await { + // let pkg_removed = mgr.remove_pkg(&repo, &arch, &pkg_name).await?; + // + // if pkg_removed { + // tracing::info!( + // "Removed package '{}' ({}) from repository '{}'", + // pkg_name, + // arch, + // repo + // ); + // + // Ok(StatusCode::OK) + // } else { + // Ok(StatusCode::NOT_FOUND) + // } + //} else { + // Ok(StatusCode::NOT_FOUND) + //} +}