1#![allow(unused_variables)]
2
3use crate::{State, permissions::PermissionGroup};
7use indexmap::IndexMap;
8use serde::Serialize;
9use std::{ops::Deref, sync::Arc};
10use utoipa::ToSchema;
11use utoipa_axum::router::OpenApiRouter;
12
13pub mod background_tasks;
14pub mod commands;
15pub mod distr;
16pub mod email_templates;
17pub mod manager;
18pub mod settings;
19pub mod shutdown_handlers;
20
21pub struct ExtensionRouteBuilder {
22 state: State,
23 pub global: Option<Box<OpenApiRouter<State>>>,
24 pub api_admin: Option<Box<OpenApiRouter<State>>>,
25 pub api_auth: Option<Box<OpenApiRouter<State>>>,
26 pub api_client: Option<Box<OpenApiRouter<State>>>,
27 pub api_client_servers_server: Option<Box<OpenApiRouter<State>>>,
28 pub api_remote: Option<Box<OpenApiRouter<State>>>,
29 pub api_remote_servers_server: Option<Box<OpenApiRouter<State>>>,
30}
31
32impl ExtensionRouteBuilder {
33 pub fn new(state: State) -> Self {
34 Self {
35 state,
36 global: None,
37 api_admin: None,
38 api_auth: None,
39 api_client: None,
40 api_client_servers_server: None,
41 api_remote: None,
42 api_remote_servers_server: None,
43 }
44 }
45
46 pub fn add_global_router(
48 mut self,
49 router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
50 ) -> Self {
51 self.global = Some(Box::new(router(self.global.map_or_else(
52 || OpenApiRouter::new().with_state(self.state.clone()),
53 |b| *b,
54 ))));
55
56 self
57 }
58
59 pub fn add_admin_api_router(
62 mut self,
63 router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
64 ) -> Self {
65 self.api_admin = Some(Box::new(router(self.api_admin.map_or_else(
66 || OpenApiRouter::new().with_state(self.state.clone()),
67 |b| *b,
68 ))));
69
70 self
71 }
72
73 pub fn add_auth_api_router(
75 mut self,
76 router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
77 ) -> Self {
78 self.api_auth = Some(Box::new(router(self.api_auth.map_or_else(
79 || OpenApiRouter::new().with_state(self.state.clone()),
80 |b| *b,
81 ))));
82
83 self
84 }
85
86 pub fn add_client_api_router(
89 mut self,
90 router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
91 ) -> Self {
92 self.api_client = Some(Box::new(router(self.api_client.map_or_else(
93 || OpenApiRouter::new().with_state(self.state.clone()),
94 |b| *b,
95 ))));
96
97 self
98 }
99
100 pub fn add_client_server_api_router(
103 mut self,
104 router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
105 ) -> Self {
106 self.api_client_servers_server = Some(Box::new(router(
107 self.api_client_servers_server.map_or_else(
108 || OpenApiRouter::new().with_state(self.state.clone()),
109 |b| *b,
110 ),
111 )));
112
113 self
114 }
115
116 pub fn add_remote_api_router(
119 mut self,
120 router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
121 ) -> Self {
122 self.api_remote = Some(Box::new(router(self.api_remote.map_or_else(
123 || OpenApiRouter::new().with_state(self.state.clone()),
124 |b| *b,
125 ))));
126
127 self
128 }
129
130 pub fn add_remote_server_api_router(
133 mut self,
134 router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
135 ) -> Self {
136 self.api_remote_servers_server = Some(Box::new(router(
137 self.api_remote_servers_server.map_or_else(
138 || OpenApiRouter::new().with_state(self.state.clone()),
139 |b| *b,
140 ),
141 )));
142
143 self
144 }
145}
146
147type RawPermissionMap = IndexMap<&'static str, PermissionGroup>;
148pub struct ExtensionPermissionsBuilder {
149 pub user_permissions: RawPermissionMap,
150 pub admin_permissions: RawPermissionMap,
151 pub server_permissions: RawPermissionMap,
152}
153
154pub struct PermissionsSnapshot {
155 user: std::collections::HashSet<String>,
156 admin: std::collections::HashSet<String>,
157 server: std::collections::HashSet<String>,
158}
159
160impl ExtensionPermissionsBuilder {
161 pub(crate) fn snapshot(&self) -> PermissionsSnapshot {
162 PermissionsSnapshot {
163 user: crate::permissions::flatten_permissions(&self.user_permissions),
164 admin: crate::permissions::flatten_permissions(&self.admin_permissions),
165 server: crate::permissions::flatten_permissions(&self.server_permissions),
166 }
167 }
168
169 pub(crate) fn contributions_since(
170 &self,
171 snapshot: &PermissionsSnapshot,
172 ) -> crate::settings::ExtensionPermissions {
173 fn added(
174 current: std::collections::HashSet<String>,
175 previous: &std::collections::HashSet<String>,
176 ) -> Vec<compact_str::CompactString> {
177 let mut added: Vec<compact_str::CompactString> = current
178 .difference(previous)
179 .map(|permission| permission.into())
180 .collect();
181 added.sort_unstable();
182
183 added
184 }
185
186 crate::settings::ExtensionPermissions {
187 user: added(
188 crate::permissions::flatten_permissions(&self.user_permissions),
189 &snapshot.user,
190 ),
191 admin: added(
192 crate::permissions::flatten_permissions(&self.admin_permissions),
193 &snapshot.admin,
194 ),
195 server: added(
196 crate::permissions::flatten_permissions(&self.server_permissions),
197 &snapshot.server,
198 ),
199 }
200 }
201
202 pub fn new(
203 user_permissions: RawPermissionMap,
204 admin_permissions: RawPermissionMap,
205 server_permissions: RawPermissionMap,
206 ) -> Self {
207 Self {
208 user_permissions,
209 admin_permissions,
210 server_permissions,
211 }
212 }
213
214 pub fn add_user_permission_group(
216 mut self,
217 group_name: &'static str,
218 group: PermissionGroup,
219 ) -> Self {
220 self.user_permissions.insert(group_name, group);
221
222 self
223 }
224
225 pub fn mutate_user_permission_group(
227 mut self,
228 group_name: &'static str,
229 mutation: impl FnOnce(&mut PermissionGroup),
230 ) -> Self {
231 if let Some(group) = self.user_permissions.get_mut(group_name) {
232 mutation(group);
233 }
234
235 self
236 }
237
238 pub fn add_admin_permission_group(
240 mut self,
241 group_name: &'static str,
242 group: PermissionGroup,
243 ) -> Self {
244 self.admin_permissions.insert(group_name, group);
245
246 self
247 }
248
249 pub fn mutate_admin_permission_group(
251 mut self,
252 group_name: &'static str,
253 mutation: impl FnOnce(&mut PermissionGroup),
254 ) -> Self {
255 if let Some(group) = self.admin_permissions.get_mut(group_name) {
256 mutation(group);
257 }
258
259 self
260 }
261
262 pub fn add_server_permission_group(
264 mut self,
265 group_name: &'static str,
266 group: PermissionGroup,
267 ) -> Self {
268 self.server_permissions.insert(group_name, group);
269
270 self
271 }
272
273 pub fn mutate_server_permission_group(
275 mut self,
276 group_name: &'static str,
277 mutation: impl FnOnce(&mut PermissionGroup),
278 ) -> Self {
279 if let Some(group) = self.server_permissions.get_mut(group_name) {
280 mutation(group);
281 }
282
283 self
284 }
285}
286
287pub struct ExtensionUpdateInfo {
288 pub version: semver::Version,
289 pub changes: Vec<compact_str::CompactString>,
290}
291
292pub type ExtensionCallValue = Box<dyn std::any::Any + Send + Sync>;
293
294#[async_trait::async_trait]
295pub trait Extension: Send + Sync {
296 async fn initialize(&mut self, state: State) {}
298
299 async fn initialize_cli(
301 &mut self,
302 env: Option<&Arc<crate::env::Env>>,
303 builder: commands::CliCommandGroupBuilder,
304 ) -> commands::CliCommandGroupBuilder {
305 builder
306 }
307
308 async fn initialize_router(
310 &mut self,
311 state: State,
312 builder: ExtensionRouteBuilder,
313 ) -> ExtensionRouteBuilder {
314 builder
315 }
316
317 async fn initialize_email_templates(
319 &mut self,
320 state: State,
321 builder: email_templates::ExtensionEmailTemplateBuilder,
322 ) -> email_templates::ExtensionEmailTemplateBuilder {
323 builder
324 }
325
326 async fn initialize_background_tasks(
328 &mut self,
329 state: State,
330 builder: background_tasks::BackgroundTaskBuilder,
331 ) -> background_tasks::BackgroundTaskBuilder {
332 builder
333 }
334
335 async fn initialize_shutdown_handlers(
337 &mut self,
338 state: State,
339 builder: shutdown_handlers::ShutdownHandlerBuilder,
340 ) -> shutdown_handlers::ShutdownHandlerBuilder {
341 builder
342 }
343
344 async fn initialize_permissions(
346 &mut self,
347 state: State,
348 builder: ExtensionPermissionsBuilder,
349 ) -> ExtensionPermissionsBuilder {
350 builder
351 }
352
353 async fn settings_deserializer(&self, state: State) -> settings::ExtensionSettingsDeserializer {
357 Arc::new(settings::EmptySettings)
358 }
359
360 async fn check_for_updates(
364 &self,
365 state: State,
366 current_version: &semver::Version,
367 ) -> Result<Option<ExtensionUpdateInfo>, anyhow::Error> {
368 Ok(None)
369 }
370
371 async fn process_call(
376 &self,
377 name: &str,
378 args: &[ExtensionCallValue],
379 ) -> Option<ExtensionCallValue> {
380 None
381 }
382
383 async fn process_call_owned(
390 &self,
391 name: &str,
392 args: Vec<ExtensionCallValue>,
393 ) -> Option<ExtensionCallValue> {
394 self.process_call(name, &args).await
395 }
396}
397
398#[derive(ToSchema, Serialize, Clone)]
399pub struct ConstructedExtension {
400 pub metadata_toml: distr::MetadataToml,
401 pub package_name: &'static str,
402 pub description: &'static str,
403 pub authors: &'static [&'static str],
404 #[schema(value_type = String)]
405 pub version: semver::Version,
406
407 #[serde(skip)]
408 #[schema(ignore)]
409 pub extension: Arc<dyn Extension>,
410}
411
412impl Deref for ConstructedExtension {
413 type Target = Arc<dyn Extension>;
414
415 fn deref(&self) -> &Self::Target {
416 &self.extension
417 }
418}
419
420#[derive(ToSchema, Serialize, Clone)]
421pub struct PendingExtension {
422 pub metadata_toml: distr::MetadataToml,
423 pub package_name: compact_str::CompactString,
424 pub description: compact_str::CompactString,
425 pub authors: Vec<compact_str::CompactString>,
426 #[schema(value_type = String)]
427 pub version: semver::Version,
428}