kanidmd_core/https/
manifest.rs
1use axum::http::header::CONTENT_TYPE;
3use axum::http::HeaderValue;
4use axum::response::{IntoResponse, Response};
5use serde::{Deserialize, Serialize};
6use serde_with::skip_serializing_none;
7
8use crate::https::extractors::DomainInfo;
9
10const MIME_TYPE_MANIFEST: &str = "application/manifest+json;charset=utf-8";
12
13#[skip_serializing_none]
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct Manifest {
18 name: String,
19 short_name: String,
20 start_url: String,
21 #[serde(rename = "display")]
22 display_mode: DisplayMode,
23 background_color: String,
24 description: Option<String>,
25 #[serde(rename = "dir")]
26 direction: Direction,
27 orientation: Option<String>,
29 lang: Option<String>,
31 scope: Option<String>,
32 theme_color: String,
34 prefer_related_applications: Option<bool>,
35 icons: Vec<ManifestIcon>,
38 related_applications: Option<Vec<String>>,
41 }
43
44#[derive(Clone, Debug, Serialize, Deserialize)]
45struct ManifestIcon {
46 src: String,
47 #[serde(rename = "type")]
48 mime_type: String,
49 sizes: String,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 purpose: Option<String>,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
55enum Direction {
56 #[serde(rename = "ltr")]
58 Ltr,
59 #[serde(rename = "rtl")]
61 Rtl,
62 #[serde(rename = "auto")]
66 Auto,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
73enum DisplayMode {
74 #[serde(rename = "full-screen")]
77 FullScreen,
78 #[serde(rename = "standalone")]
84 Standalone,
85 #[serde(rename = "minimal-ui")]
89 MinimalUi,
90 #[serde(rename = "browser")]
93 Browser,
94}
95
96pub fn manifest_data(host_req: Option<&str>, domain_display_name: String) -> Manifest {
97 let icons = vec![
98 ManifestIcon {
99 sizes: String::from("512x512"),
100 src: String::from("/pkg/img/logo-square.svg"),
101 mime_type: String::from("image/svg+xml"),
102 purpose: None,
103 },
104 ManifestIcon {
105 sizes: String::from("512x512"),
106 src: String::from("/pkg/img/logo-512.png"),
107 mime_type: String::from("image/png"),
108 purpose: Some(String::from("maskable")),
109 },
110 ManifestIcon {
111 sizes: String::from("192x192"),
112 src: String::from("/pkg/img/logo-192.png"),
113 mime_type: String::from("image/png"),
114 purpose: Some(String::from("maskable")),
115 },
116 ManifestIcon {
117 sizes: String::from("256x156"),
118 src: String::from("/pkg/img/logo-256.png"),
119 mime_type: String::from("image/png"),
120 purpose: Some(String::from("maskable")),
121 },
122 ];
123
124 let start_url = match host_req {
125 Some(value) => format!("https://{}/", value),
126 None => String::from("/"),
127 };
128
129 Manifest {
130 short_name: "Kanidm".to_string(),
131 name: domain_display_name,
132 start_url,
133 display_mode: DisplayMode::MinimalUi,
134 description: None,
135 orientation: None,
136 lang: Some("en".to_string()),
137 theme_color: "white".to_string(),
138 background_color: "white".to_string(),
139 direction: Direction::Auto,
140 scope: None,
141 prefer_related_applications: None,
142 icons,
143 related_applications: None,
144 }
145}
146
147pub(crate) async fn manifest(DomainInfo(domain_info): DomainInfo) -> impl IntoResponse {
149 let domain_display_name = domain_info.display_name().to_string();
150 let manifest_string =
152 serde_json::to_string_pretty(&manifest_data(None, domain_display_name)).unwrap_or_default();
153 let mut res = Response::new(manifest_string);
154
155 res.headers_mut()
156 .insert(CONTENT_TYPE, HeaderValue::from_static(MIME_TYPE_MANIFEST));
157
158 res
159}