httpd: Add /raw route to get file at canonical head
Instead of requiring to specify a commit to obtain a file through the /raw route, this commit allows to prefix `/head` to get the file at the current canonical head commit.
This commit is contained in:
parent
3ae7e305bd
commit
dff6df10fb
|
|
@ -7,6 +7,7 @@ use axum::response::IntoResponse;
|
|||
use axum::routing::get;
|
||||
use axum::Router;
|
||||
use hyper::HeaderMap;
|
||||
use radicle_surf::blob::{Blob, BlobRef};
|
||||
use tower_http::cors;
|
||||
|
||||
use radicle::prelude::RepoId;
|
||||
|
|
@ -94,8 +95,9 @@ static MIMES: &[(&str, &str)] = &[
|
|||
|
||||
pub fn router(profile: Arc<Profile>) -> Router {
|
||||
Router::new()
|
||||
.route("/:project/:sha/*path", get(file_by_path_handler))
|
||||
.route("/:project/blobs/:oid", get(file_by_oid_handler))
|
||||
.route("/:rid/:sha/*path", get(file_by_commit_handler))
|
||||
.route("/:rid/head/*path", get(file_by_canonical_head_handler))
|
||||
.route("/:rid/blobs/:oid", get(file_by_oid_handler))
|
||||
.with_state(profile)
|
||||
.layer(
|
||||
cors::CorsLayer::new()
|
||||
|
|
@ -106,7 +108,7 @@ pub fn router(profile: Arc<Profile>) -> Router {
|
|||
)
|
||||
}
|
||||
|
||||
async fn file_by_path_handler(
|
||||
async fn file_by_commit_handler(
|
||||
Path((rid, sha, path)): Path<(RepoId, Oid, String)>,
|
||||
State(profile): State<Arc<Profile>>,
|
||||
) -> impl IntoResponse {
|
||||
|
|
@ -118,10 +120,36 @@ async fn file_by_path_handler(
|
|||
return Err(Error::NotFound);
|
||||
}
|
||||
|
||||
let mut response_headers = HeaderMap::new();
|
||||
let repo: Repository = repo.backend.into();
|
||||
let blob = repo.blob(sha, &path)?;
|
||||
|
||||
blob_response(blob, path)
|
||||
}
|
||||
|
||||
async fn file_by_canonical_head_handler(
|
||||
Path((rid, path)): Path<(RepoId, String)>,
|
||||
State(profile): State<Arc<Profile>>,
|
||||
) -> impl IntoResponse {
|
||||
let storage = &profile.storage;
|
||||
let repo = storage.repository(rid)?;
|
||||
|
||||
// Don't allow downloading raw files for private repos.
|
||||
if repo.identity_doc()?.visibility.is_private() {
|
||||
return Err(Error::NotFound);
|
||||
}
|
||||
|
||||
let (_, sha) = repo.head()?;
|
||||
let repo: Repository = repo.backend.into();
|
||||
let blob = repo.blob(sha, &path)?;
|
||||
|
||||
blob_response(blob, path)
|
||||
}
|
||||
|
||||
fn blob_response(
|
||||
blob: Blob<BlobRef>,
|
||||
path: String,
|
||||
) -> Result<(StatusCode, HeaderMap, Vec<u8>), Error> {
|
||||
let mut response_headers = HeaderMap::new();
|
||||
if blob.size() > MAX_BLOB_SIZE {
|
||||
return Ok::<_, Error>((StatusCode::PAYLOAD_TOO_LARGE, response_headers, vec![]));
|
||||
}
|
||||
|
|
@ -171,8 +199,8 @@ async fn file_by_oid_handler(
|
|||
mod routes {
|
||||
use axum::http::StatusCode;
|
||||
|
||||
use crate::test::{self, get, HEAD, RID, RID_PRIVATE};
|
||||
use radicle::storage::{ReadRepository, ReadStorage};
|
||||
use crate::test::{self, get, RID, RID_PRIVATE};
|
||||
use radicle::storage::ReadStorage;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_file_handler() {
|
||||
|
|
@ -180,20 +208,18 @@ mod routes {
|
|||
let ctx = test::seed(tmp.path());
|
||||
let app = super::router(ctx.profile().to_owned());
|
||||
|
||||
let response = get(&app, format!("/{RID}/{HEAD}/dir1/README")).await;
|
||||
let response = get(&app, format!("/{RID}/head/dir1/README")).await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(response.body().await, "Hello World from dir1!\n");
|
||||
|
||||
// Make sure the repo exists in storage.
|
||||
let repo = ctx
|
||||
.profile()
|
||||
ctx.profile()
|
||||
.storage
|
||||
.repository(RID_PRIVATE.parse().unwrap())
|
||||
.unwrap();
|
||||
let (_, head) = repo.head().unwrap();
|
||||
|
||||
let response = get(&app, format!("/{RID_PRIVATE}/{head}/README")).await;
|
||||
let response = get(&app, format!("/{RID_PRIVATE}/head/README")).await;
|
||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue