157 lines
5.2 KiB
Rust
157 lines
5.2 KiB
Rust
use crate::{db, 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? {
|
|
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 if let Some(m) = global.pkg_filename_re.captures(&file_name) {
|
|
// SAFETY: these unwraps cannot fail if the RegEx matched successfully
|
|
db::query::package::by_fields(
|
|
&global.db,
|
|
repo_id,
|
|
m.get(1).unwrap().as_str(),
|
|
m.get(2).unwrap().as_str(),
|
|
m.get(3).unwrap().as_str(),
|
|
m.get(4).unwrap().as_str(),
|
|
)
|
|
.await?
|
|
.ok_or(StatusCode::NOT_FOUND)?
|
|
.id
|
|
.to_string()
|
|
} else {
|
|
return Err(StatusCode::NOT_FOUND.into());
|
|
};
|
|
|
|
match global.config.fs {
|
|
FsConfig::Local { data_dir } => {
|
|
let path = data_dir
|
|
.join("repos")
|
|
.join(repo_id.to_string())
|
|
.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)
|
|
//}
|
|
}
|