refactor: move web code into own module
parent
412d1e65f1
commit
a6de2c3c14
|
@ -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
|
||||
|
|
|
@ -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<crate::Global> {
|
||||
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<crate::Global>,
|
||||
Path((distro, repo, arch, file_name)): Path<(String, String, String, String)>,
|
||||
req: Request<Body>,
|
||||
) -> crate::Result<impl IntoResponse> {
|
||||
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<crate::Global>,
|
||||
Path((distro, repo)): Path<(String, String)>,
|
||||
body: Body,
|
||||
) -> crate::Result<StatusCode> {
|
||||
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<crate::Global>,
|
||||
Path((distro, repo)): Path<(String, String)>,
|
||||
) -> crate::Result<StatusCode> {
|
||||
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<crate::Global>,
|
||||
Path((distro, repo, arch)): Path<(String, String, String)>,
|
||||
) -> crate::Result<StatusCode> {
|
||||
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<crate::Global>,
|
||||
Path((distro, repo, arch, pkg_name)): Path<(String, String, String, String)>,
|
||||
) -> crate::Result<StatusCode> {
|
||||
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)
|
||||
//}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
|
@ -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<crate::Global> {
|
||||
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<crate::Global>,
|
||||
Path((distro, repo, arch, file_name)): Path<(String, String, String, String)>,
|
||||
req: Request<Body>,
|
||||
) -> crate::Result<impl IntoResponse> {
|
||||
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<crate::Global>,
|
||||
Path((distro, repo)): Path<(String, String)>,
|
||||
body: Body,
|
||||
) -> crate::Result<StatusCode> {
|
||||
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<crate::Global>,
|
||||
Path((distro, repo)): Path<(String, String)>,
|
||||
) -> crate::Result<StatusCode> {
|
||||
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<crate::Global>,
|
||||
Path((distro, repo, arch)): Path<(String, String, String)>,
|
||||
) -> crate::Result<StatusCode> {
|
||||
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<crate::Global>,
|
||||
Path((distro, repo, arch, pkg_name)): Path<(String, String, String, String)>,
|
||||
) -> crate::Result<StatusCode> {
|
||||
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)
|
||||
//}
|
||||
}
|
Loading…
Reference in New Issue