fix(registry): keep v0 api backwards compatible

This commit is contained in:
daimond113 2025-01-13 13:19:15 +01:00
parent 72c1c39401
commit 1eef6078bf
No known key found for this signature in database
GPG key ID: 3A8ECE51328B513C
3 changed files with 112 additions and 31 deletions

View file

@ -16,7 +16,7 @@ pub struct Query {
doc: Option<String>, doc: Option<String>,
} }
pub async fn get_package_version( pub async fn get_package_version_v0(
request: HttpRequest, request: HttpRequest,
app_state: web::Data<AppState>, app_state: web::Data<AppState>,
path: web::Path<(PackageName, LatestOrSpecificVersion, AnyOrSpecificTarget)>, path: web::Path<(PackageName, LatestOrSpecificVersion, AnyOrSpecificTarget)>,
@ -32,35 +32,48 @@ pub async fn get_package_version(
return Ok(HttpResponse::NotFound().finish()); return Ok(HttpResponse::NotFound().finish());
}; };
// TODO: this is deprecated, since the introduction of the specific endpoints for readme, doc and archive. if let Some(doc_name) = request_query.doc.as_deref() {
// remove this when we drop 0.5 support. 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 let accept = request
.headers() .headers()
.get(ACCEPT) .get(ACCEPT)
.and_then(|accept| accept.to_str().ok()) .and_then(|accept| accept.to_str().ok())
.and_then(|accept| match accept.to_lowercase().as_str() { .and_then(|accept| match accept.to_lowercase().as_str() {
"text/plain" => Some(true), "text/plain" => Some(true),
"application/octet-stream" => Some(false), "application/octet-stream" => Some(false),
_ => None, _ => None,
}); });
if let Some(readme) = accept { if let Some(readme) = accept {
return if readme { return if readme {
app_state.storage.get_readme(&name, v_id).await app_state.storage.get_readme(&name, v_id).await
} else { } else {
app_state.storage.get_package(&name, v_id).await app_state.storage.get_package(&name, v_id).await
}; };
}
} }
Ok(HttpResponse::Ok().json(PackageResponse::new(&name, v_id, &file))) Ok(HttpResponse::Ok().json(PackageResponse::new(&name, v_id, &file)))
} }
pub async fn get_package_version(
app_state: web::Data<AppState>,
path: web::Path<(PackageName, LatestOrSpecificVersion, AnyOrSpecificTarget)>,
) -> Result<HttpResponse, RegistryError> {
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)))
}

View file

@ -1,11 +1,45 @@
use actix_web::{web, HttpResponse, Responder};
use crate::{ use crate::{
error::RegistryError, error::RegistryError,
package::{read_package, PackageVersionsResponse}, package::{read_package, PackageResponse, PackageVersionsResponse},
AppState, 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<AppState>,
path: web::Path<PackageName>,
) -> Result<impl Responder, RegistryError> {
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::<Vec<_>>();
Ok(HttpResponse::Ok().json(responses))
}
pub async fn get_package_versions( pub async fn get_package_versions(
app_state: web::Data<AppState>, app_state: web::Data<AppState>,

View file

@ -150,6 +150,8 @@ async fn run() -> std::io::Result<()> {
.finish() .finish()
.unwrap(); .unwrap();
let publish_payload_config = PayloadConfig::new(config.max_archive_size);
HttpServer::new(move || { HttpServer::new(move || {
App::new() App::new()
.wrap(sentry_actix::Sentry::with_transaction()) .wrap(sentry_actix::Sentry::with_transaction())
@ -166,6 +168,38 @@ async fn run() -> std::io::Result<()> {
) )
.service( .service(
web::scope("/v0") 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( .route(
"/search", "/search",
web::get() web::get()
@ -216,7 +250,7 @@ async fn run() -> std::io::Result<()> {
) )
.service( .service(
web::scope("/packages") web::scope("/packages")
.app_data(PayloadConfig::new(config.max_archive_size)) .app_data(publish_payload_config.clone())
.route( .route(
"", "",
web::post() web::post()