From 1eef6078bfa3d0e23eec6a5e15a7ee02f5cfc5dd Mon Sep 17 00:00:00 2001 From: daimond113 <72147841+daimond113@users.noreply.github.com> Date: Mon, 13 Jan 2025 13:19:15 +0100 Subject: [PATCH] fix(registry): keep v0 api backwards compatible --- registry/src/endpoints/package_version.rs | 65 +++++++++++++--------- registry/src/endpoints/package_versions.rs | 42 ++++++++++++-- registry/src/main.rs | 36 +++++++++++- 3 files changed, 112 insertions(+), 31 deletions(-) diff --git a/registry/src/endpoints/package_version.rs b/registry/src/endpoints/package_version.rs index 838e93b..c5c9c8e 100644 --- a/registry/src/endpoints/package_version.rs +++ b/registry/src/endpoints/package_version.rs @@ -16,7 +16,7 @@ pub struct Query { doc: Option, } -pub async fn get_package_version( +pub async fn get_package_version_v0( request: HttpRequest, app_state: web::Data, path: web::Path<(PackageName, LatestOrSpecificVersion, AnyOrSpecificTarget)>, @@ -32,35 +32,48 @@ pub async fn get_package_version( return Ok(HttpResponse::NotFound().finish()); }; - // TODO: this is deprecated, since the introduction of the specific endpoints for readme, doc and archive. - // remove this when we drop 0.5 support. - { - if let Some(doc_name) = request_query.doc.as_deref() { - let Some(hash) = find_package_doc(&file, v_id, doc_name) else { - return Ok(HttpResponse::NotFound().finish()); - }; + if let Some(doc_name) = request_query.doc.as_deref() { + let Some(hash) = find_package_doc(&file, v_id, doc_name) else { + return Ok(HttpResponse::NotFound().finish()); + }; - return app_state.storage.get_doc(hash).await; - } + return app_state.storage.get_doc(hash).await; + } - let accept = request - .headers() - .get(ACCEPT) - .and_then(|accept| accept.to_str().ok()) - .and_then(|accept| match accept.to_lowercase().as_str() { - "text/plain" => Some(true), - "application/octet-stream" => Some(false), - _ => None, - }); + let accept = request + .headers() + .get(ACCEPT) + .and_then(|accept| accept.to_str().ok()) + .and_then(|accept| match accept.to_lowercase().as_str() { + "text/plain" => Some(true), + "application/octet-stream" => Some(false), + _ => None, + }); - if let Some(readme) = accept { - return if readme { - app_state.storage.get_readme(&name, v_id).await - } else { - app_state.storage.get_package(&name, v_id).await - }; - } + if let Some(readme) = accept { + return if readme { + app_state.storage.get_readme(&name, v_id).await + } else { + app_state.storage.get_package(&name, v_id).await + }; } Ok(HttpResponse::Ok().json(PackageResponse::new(&name, v_id, &file))) } + +pub async fn get_package_version( + app_state: web::Data, + path: web::Path<(PackageName, LatestOrSpecificVersion, AnyOrSpecificTarget)>, +) -> Result { + let (name, version, target) = path.into_inner(); + + let Some(file) = read_package(&app_state, &name, &*app_state.source.read().await).await? else { + return Ok(HttpResponse::NotFound().finish()); + }; + + let Some(v_id) = resolve_version_and_target(&file, version, target) else { + return Ok(HttpResponse::NotFound().finish()); + }; + + Ok(HttpResponse::Ok().json(PackageResponse::new(&name, v_id, &file))) +} diff --git a/registry/src/endpoints/package_versions.rs b/registry/src/endpoints/package_versions.rs index a792b3a..c516212 100644 --- a/registry/src/endpoints/package_versions.rs +++ b/registry/src/endpoints/package_versions.rs @@ -1,11 +1,45 @@ -use actix_web::{web, HttpResponse, Responder}; - use crate::{ error::RegistryError, - package::{read_package, PackageVersionsResponse}, + package::{read_package, PackageResponse, PackageVersionsResponse}, AppState, }; -use pesde::names::PackageName; +use actix_web::{web, HttpResponse, Responder}; +use pesde::{names::PackageName, source::ids::VersionId}; +use semver::Version; +use std::collections::{btree_map::Entry, BTreeMap}; + +pub async fn get_package_versions_v0( + app_state: web::Data, + path: web::Path, +) -> Result { + let name = path.into_inner(); + + let Some(file) = read_package(&app_state, &name, &*app_state.source.read().await).await? else { + return Ok(HttpResponse::NotFound().finish()); + }; + + let mut versions = BTreeMap::<&Version, &VersionId>::new(); + + for v_id in file.entries.keys() { + match versions.entry(v_id.version()) { + Entry::Vacant(entry) => { + entry.insert(v_id); + } + Entry::Occupied(mut entry) => { + if entry.get() < &v_id { + entry.insert(v_id); + } + } + } + } + + let responses = versions + .into_values() + .map(|v_id| PackageResponse::new(&name, v_id, &file)) + .collect::>(); + + Ok(HttpResponse::Ok().json(responses)) +} pub async fn get_package_versions( app_state: web::Data, diff --git a/registry/src/main.rs b/registry/src/main.rs index e2af10b..07f3d0f 100644 --- a/registry/src/main.rs +++ b/registry/src/main.rs @@ -150,6 +150,8 @@ async fn run() -> std::io::Result<()> { .finish() .unwrap(); + let publish_payload_config = PayloadConfig::new(config.max_archive_size); + HttpServer::new(move || { App::new() .wrap(sentry_actix::Sentry::with_transaction()) @@ -166,6 +168,38 @@ async fn run() -> std::io::Result<()> { ) .service( web::scope("/v0") + .route( + "/search", + web::get() + .to(endpoints::search::search_packages) + .wrap(from_fn(auth::read_mw)), + ) + .route( + "/packages/{name}", + web::get() + .to(endpoints::package_versions::get_package_versions_v0) + .wrap(from_fn(auth::read_mw)), + ) + .route( + "/packages/{name}/{version}/{target}", + web::get() + .to(endpoints::package_version::get_package_version_v0) + .wrap(from_fn(auth::read_mw)), + ) + .service( + web::scope("/packages") + .app_data(publish_payload_config.clone()) + .route( + "", + web::post() + .to(endpoints::publish_version::publish_package) + .wrap(Governor::new(&publish_governor_config)) + .wrap(from_fn(auth::write_mw)), + ), + ), + ) + .service( + web::scope("/v1") .route( "/search", web::get() @@ -216,7 +250,7 @@ async fn run() -> std::io::Result<()> { ) .service( web::scope("/packages") - .app_data(PayloadConfig::new(config.max_archive_size)) + .app_data(publish_payload_config.clone()) .route( "", web::post()