use axum::Router; use axum::extract::{Path, Query, State}; use axum::http::header; use axum::response::{IntoResponse, Response}; use axum::routing::get; use serde::Deserialize; use uuid::Uuid; use crate::error::ApiError; use crate::extractors::AuthenticatedUser; use crate::state::AppState; pub fn router() -> Router { Router::new().route("/attachments/{id}", get(get_attachment)) } /// Größen-/Format-Parameter für das gerenderte Vorschaubild. Alle optional /// mit sinnvollen Defaults — die App kann pro Anwendungsfall (Thumbnail vs. /// Vollbild) abweichende Werte anfragen. #[derive(Debug, Deserialize)] pub struct PreviewQuery { #[serde(default = "default_dimension")] pub w: u32, #[serde(default = "default_dimension")] pub h: u32, #[serde(default = "default_quality")] pub q: u32, #[serde(default = "default_ext")] pub ext: String, #[serde(default = "default_page")] pub page: String, } fn default_dimension() -> u32 { 1024 } fn default_quality() -> u32 { 85 } fn default_ext() -> String { "jpeg".to_string() } fn default_page() -> String { "1".to_string() } /// Liefert ein gerendertes Vorschaubild des Attachments (Bytes), geladen /// aus DOCUframe. Auflösung/Format über Query-Parameter steuerbar /// (`?w=&h=&q=&ext=&page=`). #[utoipa::path( get, path = "/attachments/{id}", tag = "attachments", params( ("id" = Uuid, Path, description = "Attachment-Id (unsere UUID)"), ("w" = Option, Query, description = "Breite in Pixeln (Default 1024)"), ("h" = Option, Query, description = "Höhe in Pixeln (Default 1024)"), ("q" = Option, Query, description = "Qualität 0–100 (Default 85)"), ("ext" = Option, Query, description = "png|jpeg|jpg|webp|tiff (Default jpeg)"), ("page" = Option, Query, description = "Seitennummer (Default 1)"), ), responses( (status = 200, description = "Vorschaubild (Bytes)", content_type = "image/jpeg"), (status = 401, description = "Authentifizierung fehlgeschlagen"), (status = 404, description = "Attachment nicht gefunden") ), security(("bearer_auth" = [])) )] pub async fn get_attachment( State(state): State, AuthenticatedUser(claims): AuthenticatedUser, Path(id): Path, Query(query): Query, ) -> Result { tracing::info!(actor = claims.personalnummer, %id, "attachment.preview"); // DOCUframe-Parameterschema: width_height_quality_extension. let parameters = format!("{}_{}_{}_{}", query.w, query.h, query.q, query.ext); let preview = state .get_attachment_preview .execute(id, parameters, query.page) .await?; Ok(( [(header::CONTENT_TYPE, preview.content_type)], preview.bytes, ) .into_response()) }