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>;
148#[derive(Clone)]
149pub struct ExtensionPermissionsBuilder {
150 pub user_permissions: RawPermissionMap,
151 pub admin_permissions: RawPermissionMap,
152 pub server_permissions: RawPermissionMap,
153}
154
155pub struct PermissionsSnapshot {
156 user: std::collections::HashSet<String>,
157 admin: std::collections::HashSet<String>,
158 server: std::collections::HashSet<String>,
159}
160
161impl ExtensionPermissionsBuilder {
162 pub(crate) fn snapshot(&self) -> PermissionsSnapshot {
163 PermissionsSnapshot {
164 user: crate::permissions::flatten_permissions(&self.user_permissions),
165 admin: crate::permissions::flatten_permissions(&self.admin_permissions),
166 server: crate::permissions::flatten_permissions(&self.server_permissions),
167 }
168 }
169
170 pub(crate) fn contributions_since(
171 &self,
172 snapshot: &PermissionsSnapshot,
173 ) -> crate::settings::ExtensionPermissions {
174 fn added(
175 current: std::collections::HashSet<String>,
176 previous: &std::collections::HashSet<String>,
177 ) -> Vec<compact_str::CompactString> {
178 let mut added: Vec<compact_str::CompactString> = current
179 .difference(previous)
180 .map(|permission| permission.into())
181 .collect();
182 added.sort_unstable();
183
184 added
185 }
186
187 crate::settings::ExtensionPermissions {
188 user: added(
189 crate::permissions::flatten_permissions(&self.user_permissions),
190 &snapshot.user,
191 ),
192 admin: added(
193 crate::permissions::flatten_permissions(&self.admin_permissions),
194 &snapshot.admin,
195 ),
196 server: added(
197 crate::permissions::flatten_permissions(&self.server_permissions),
198 &snapshot.server,
199 ),
200 }
201 }
202
203 pub fn new(
204 user_permissions: RawPermissionMap,
205 admin_permissions: RawPermissionMap,
206 server_permissions: RawPermissionMap,
207 ) -> Self {
208 Self {
209 user_permissions,
210 admin_permissions,
211 server_permissions,
212 }
213 }
214
215 pub fn add_user_permission_group(
217 mut self,
218 group_name: &'static str,
219 group: PermissionGroup,
220 ) -> Self {
221 self.user_permissions.insert(group_name, group);
222
223 self
224 }
225
226 pub fn mutate_user_permission_group(
228 mut self,
229 group_name: &'static str,
230 mutation: impl FnOnce(&mut PermissionGroup),
231 ) -> Self {
232 if let Some(group) = self.user_permissions.get_mut(group_name) {
233 mutation(group);
234 }
235
236 self
237 }
238
239 pub fn add_admin_permission_group(
241 mut self,
242 group_name: &'static str,
243 group: PermissionGroup,
244 ) -> Self {
245 self.admin_permissions.insert(group_name, group);
246
247 self
248 }
249
250 pub fn mutate_admin_permission_group(
252 mut self,
253 group_name: &'static str,
254 mutation: impl FnOnce(&mut PermissionGroup),
255 ) -> Self {
256 if let Some(group) = self.admin_permissions.get_mut(group_name) {
257 mutation(group);
258 }
259
260 self
261 }
262
263 pub fn add_server_permission_group(
265 mut self,
266 group_name: &'static str,
267 group: PermissionGroup,
268 ) -> Self {
269 self.server_permissions.insert(group_name, group);
270
271 self
272 }
273
274 pub fn mutate_server_permission_group(
276 mut self,
277 group_name: &'static str,
278 mutation: impl FnOnce(&mut PermissionGroup),
279 ) -> Self {
280 if let Some(group) = self.server_permissions.get_mut(group_name) {
281 mutation(group);
282 }
283
284 self
285 }
286}
287
288pub struct ExtensionUpdateInfo {
289 pub version: semver::Version,
290 pub changes: Vec<compact_str::CompactString>,
291}
292
293pub type ExtensionCallValue = Box<dyn std::any::Any + Send + Sync>;
294
295#[async_trait::async_trait]
296pub trait Extension: Send + Sync {
297 async fn initialize(&mut self, state: State) {}
299
300 async fn initialize_cli(
302 &mut self,
303 env: Option<&Arc<crate::env::Env>>,
304 builder: commands::CliCommandGroupBuilder,
305 ) -> commands::CliCommandGroupBuilder {
306 builder
307 }
308
309 async fn initialize_router(
311 &mut self,
312 state: State,
313 builder: ExtensionRouteBuilder,
314 ) -> ExtensionRouteBuilder {
315 builder
316 }
317
318 async fn initialize_email_templates(
320 &mut self,
321 state: State,
322 builder: email_templates::ExtensionEmailTemplateBuilder,
323 ) -> email_templates::ExtensionEmailTemplateBuilder {
324 builder
325 }
326
327 async fn initialize_background_tasks(
329 &mut self,
330 state: State,
331 builder: background_tasks::BackgroundTaskBuilder,
332 ) -> background_tasks::BackgroundTaskBuilder {
333 builder
334 }
335
336 async fn initialize_shutdown_handlers(
338 &mut self,
339 state: State,
340 builder: shutdown_handlers::ShutdownHandlerBuilder,
341 ) -> shutdown_handlers::ShutdownHandlerBuilder {
342 builder
343 }
344
345 async fn initialize_permissions(
347 &mut self,
348 state: State,
349 builder: ExtensionPermissionsBuilder,
350 ) -> ExtensionPermissionsBuilder {
351 builder
352 }
353
354 async fn settings_deserializer(&self, state: State) -> settings::ExtensionSettingsDeserializer {
358 Arc::new(settings::EmptySettings)
359 }
360
361 async fn check_for_updates(
365 &self,
366 state: State,
367 current_version: &semver::Version,
368 ) -> Result<Option<ExtensionUpdateInfo>, anyhow::Error> {
369 Ok(None)
370 }
371
372 async fn process_call(
377 &self,
378 name: &str,
379 args: &[ExtensionCallValue],
380 ) -> Option<ExtensionCallValue> {
381 None
382 }
383}
384
385#[derive(ToSchema, Serialize, Clone)]
386pub struct ConstructedExtension {
387 pub metadata_toml: distr::MetadataToml,
388 pub package_name: &'static str,
389 pub description: &'static str,
390 pub authors: &'static [&'static str],
391 #[schema(value_type = String)]
392 pub version: semver::Version,
393
394 #[serde(skip)]
395 #[schema(ignore)]
396 pub extension: Arc<dyn Extension>,
397}
398
399impl Deref for ConstructedExtension {
400 type Target = Arc<dyn Extension>;
401
402 fn deref(&self) -> &Self::Target {
403 &self.extension
404 }
405}
406
407#[derive(ToSchema, Serialize, Clone)]
408pub struct PendingExtension {
409 pub metadata_toml: distr::MetadataToml,
410 pub package_name: compact_str::CompactString,
411 pub description: compact_str::CompactString,
412 pub authors: Vec<compact_str::CompactString>,
413 #[schema(value_type = String)]
414 pub version: semver::Version,
415}