use axum::body::Bytes; use axum::extract::{Path, Query, State}; use axum::http::header; use axum::http::{HeaderValue, StatusCode}; use axum::response::{IntoResponse, Response}; use jwt::extractor::AuthUser; use uuid::Uuid; use crate::error::RepoResult; use crate::features::RepoService; use crate::features::archive::ArchiveFormatQuery; #[utoipa::path( get, path = "/repo/{channel_id}/{repo_id}/archive/{archive_format}/{ref_name}", tag = "repo", params(ArchiveFormatQuery), responses((status = 200, description = "Download an archive")), security(("bearer_auth" = [])) )] pub(crate) async fn handler( State(svc): State, AuthUser { claims }: AuthUser, Path((_, repo_id, archive_format, ref_name)): Path<(Uuid, Uuid, String, String)>, Query(q): Query, ) -> RepoResult { let bytes = svc .get_archive(claims.sub, repo_id, &archive_format, &ref_name, q) .await?; let mime = match archive_format.as_str() { "zipball" => "application/zip", "tarball" | "bundle" => "application/x-tar", _ => "application/octet-stream", }; let resp = ( StatusCode::OK, [ (header::CONTENT_TYPE, HeaderValue::from_static(mime)), ( header::CONTENT_DISPOSITION, HeaderValue::from_str(&format!( "attachment; filename=\"{ref_name}.{archive_format}\"" )) .unwrap_or(HeaderValue::from_static("attachment")), ), ], Bytes::from(bytes), ) .into_response(); Ok(resp) }