use axum::{
body::Bytes,
extract::{Path, Query, State},
http::{header, StatusCode},
response::{IntoResponse, Response},
Json,
};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use crate::db::{queries::object_store, DbPool};
use crate::error::AppResult;
#[derive(Debug, Deserialize)]
pub struct PutQuery {
#[serde(default)]
pub media_type: Option<String>,
}
#[derive(Debug, Serialize)]
pub struct PutResponse {
pub key: String,
pub digest: String,
pub bytes: usize,
}
pub async fn put(
State(pool): State<DbPool>,
Path(key): Path<String>,
Query(q): Query<PutQuery>,
body: Bytes,
) -> AppResult<Json<PutResponse>> {
let digest = hex::encode(Sha256::digest(&body));
let media_type = q
.media_type
.unwrap_or_else(|| "application/octet-stream".to_string());
object_store::put(&pool, &key, &digest, &media_type, &body).await?;
tracing::debug!(object_key = %key, digest = %digest, bytes = body.len(), "stored object");
Ok(Json(PutResponse {
key,
digest,
bytes: body.len(),
}))
}
pub async fn get(
State(pool): State<DbPool>,
Path(key): Path<String>,
) -> AppResult<Response> {
let Some(row) = object_store::get(&pool, &key).await? else {
return Ok((StatusCode::NOT_FOUND, format!("object {key} not found")).into_response());
};
Ok((
[
(header::CONTENT_TYPE, row.media_type),
(header::ETAG, format!("\"{}\"", row.digest)),
],
row.bytes,
)
.into_response())
}