Skip to main content

haste_server/auth_n/tenant/routes/
branding.rs

1use crate::{services::ServerState, static_assets::StaticFile, tenants::read_tenant};
2use axum::{
3    body::Body,
4    extract::{Path, State},
5    http::{HeaderValue, header::CONTENT_TYPE},
6    response::{IntoResponse, Response},
7};
8use haste_fhir_operation_error::OperationOutcomeError;
9use haste_fhir_search::SearchEngine;
10use haste_fhir_terminology::FHIRTerminology;
11use haste_jwt::TenantId;
12use haste_repository::Repository;
13use std::sync::Arc;
14
15pub async fn logo<
16    Repo: Repository + Send + Sync + 'static,
17    Search: SearchEngine + Send + Sync + 'static,
18    Terminology: FHIRTerminology + Send + Sync + 'static,
19>(
20    Path(tenant_id): Path<TenantId>,
21    State(state): State<Arc<ServerState<Repo, Search, Terminology>>>,
22) -> Result<Response, OperationOutcomeError> {
23    let tenant = read_tenant(&state, &tenant_id).await?;
24    let Some(data) = tenant.logo_data.filter(|data| !data.is_empty()) else {
25        return Ok(StaticFile("img/logo.svg".to_string()).into_response());
26    };
27
28    let content_type = tenant
29        .logo_content_type
30        .as_deref()
31        .filter(|content_type| content_type.starts_with("image/"))
32        .ok_or_else(|| {
33            OperationOutcomeError::error(
34                haste_fhir_model::r4::generated::terminology::IssueType::invalid(),
35                "Tenant logo has an invalid image content type.".to_string(),
36            )
37        })?;
38
39    let mut response = Response::new(Body::from(data));
40    response.headers_mut().insert(
41        CONTENT_TYPE,
42        HeaderValue::from_str(content_type).map_err(|_| {
43            OperationOutcomeError::error(
44                haste_fhir_model::r4::generated::terminology::IssueType::invalid(),
45                "Tenant logo has an invalid content type header.".to_string(),
46            )
47        })?,
48    );
49    Ok(response)
50}