2025-12-22 20:10:17 +01:00
|
|
|
use crate::errors::DepotError;
|
|
|
|
|
use crate::repo::DepotRepo;
|
2025-12-08 20:50:20 +01:00
|
|
|
use axum::{
|
2025-12-22 20:10:17 +01:00
|
|
|
extract::{Path, Request, State},
|
2025-12-09 20:23:00 +01:00
|
|
|
http::header,
|
2025-12-22 20:10:17 +01:00
|
|
|
response::{IntoResponse, Response},
|
2025-12-08 20:50:20 +01:00
|
|
|
};
|
2025-12-09 20:23:00 +01:00
|
|
|
use httpdate::fmt_http_date;
|
2025-12-22 20:10:17 +01:00
|
|
|
use std::fs;
|
|
|
|
|
use std::sync::Arc;
|
2025-12-09 20:23:00 +01:00
|
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
2025-12-22 20:10:17 +01:00
|
|
|
use tower::ServiceExt;
|
|
|
|
|
use tower_http::services::ServeFile;
|
2025-12-08 20:50:20 +01:00
|
|
|
|
|
|
|
|
pub async fn get_file(
|
|
|
|
|
State(repo): State<Arc<DepotRepo>>,
|
|
|
|
|
Path((publisher, _algo, digest)): Path<(String, String, String)>,
|
|
|
|
|
req: Request,
|
|
|
|
|
) -> Result<Response, DepotError> {
|
2025-12-22 20:10:17 +01:00
|
|
|
let path = repo.get_file_path(&publisher, &digest).ok_or_else(|| {
|
|
|
|
|
DepotError::Repo(libips::repository::RepositoryError::NotFound(
|
|
|
|
|
digest.clone(),
|
|
|
|
|
))
|
|
|
|
|
})?;
|
2025-12-08 20:50:20 +01:00
|
|
|
|
|
|
|
|
let service = ServeFile::new(path);
|
|
|
|
|
let result = service.oneshot(req).await;
|
2025-12-22 20:10:17 +01:00
|
|
|
|
2025-12-08 20:50:20 +01:00
|
|
|
match result {
|
2025-12-09 20:23:00 +01:00
|
|
|
Ok(mut res) => {
|
|
|
|
|
// Add caching headers
|
|
|
|
|
let max_age = repo.cache_max_age();
|
2025-12-22 20:10:17 +01:00
|
|
|
res.headers_mut().insert(
|
|
|
|
|
header::CACHE_CONTROL,
|
|
|
|
|
header::HeaderValue::from_str(&format!("public, max-age={}", max_age)).unwrap(),
|
|
|
|
|
);
|
2025-12-09 20:23:00 +01:00
|
|
|
// ETag from digest
|
2025-12-22 20:10:17 +01:00
|
|
|
res.headers_mut().insert(
|
|
|
|
|
header::ETAG,
|
|
|
|
|
header::HeaderValue::from_str(&format!("\"{}\"", digest)).unwrap(),
|
|
|
|
|
);
|
2025-12-09 20:23:00 +01:00
|
|
|
// Last-Modified from fs metadata
|
|
|
|
|
if let Some(body_path) = res.extensions().get::<std::path::PathBuf>().cloned() {
|
|
|
|
|
if let Ok(meta) = fs::metadata(&body_path) {
|
|
|
|
|
if let Ok(mtime) = meta.modified() {
|
|
|
|
|
let lm = fmt_http_date(mtime);
|
2025-12-22 20:10:17 +01:00
|
|
|
res.headers_mut().insert(
|
|
|
|
|
header::LAST_MODIFIED,
|
|
|
|
|
header::HeaderValue::from_str(&lm).unwrap(),
|
|
|
|
|
);
|
2025-12-09 20:23:00 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Fallback: use now if extension not present (should rarely happen)
|
|
|
|
|
if !res.headers().contains_key(header::LAST_MODIFIED) {
|
2025-12-22 20:10:17 +01:00
|
|
|
let now = SystemTime::now()
|
|
|
|
|
.duration_since(UNIX_EPOCH)
|
|
|
|
|
.ok()
|
|
|
|
|
.map(|_| SystemTime::now())
|
|
|
|
|
.unwrap_or_else(SystemTime::now);
|
2025-12-09 20:23:00 +01:00
|
|
|
let lm = fmt_http_date(now);
|
2025-12-22 20:10:17 +01:00
|
|
|
res.headers_mut().insert(
|
|
|
|
|
header::LAST_MODIFIED,
|
|
|
|
|
header::HeaderValue::from_str(&lm).unwrap(),
|
|
|
|
|
);
|
2025-12-09 20:23:00 +01:00
|
|
|
}
|
|
|
|
|
Ok(res.into_response())
|
2025-12-22 20:10:17 +01:00
|
|
|
}
|
2025-12-08 20:50:20 +01:00
|
|
|
Err(e) => Err(DepotError::Server(e.to_string())),
|
|
|
|
|
}
|
|
|
|
|
}
|