shared/extensions/
mod.rs

1#![allow(unused_variables)]
2
3use crate::{State, permissions::PermissionGroup};
4use indexmap::IndexMap;
5use serde::Serialize;
6use std::{ops::Deref, sync::Arc};
7use utoipa::ToSchema;
8use utoipa_axum::router::OpenApiRouter;
9
10pub mod background_tasks;
11pub mod commands;
12pub mod distr;
13pub mod manager;
14pub mod settings;
15pub mod shutdown_handlers;
16
17pub struct ExtensionRouteBuilder {
18    state: State,
19    pub global: Option<Box<OpenApiRouter<State>>>,
20    pub api_admin: Option<Box<OpenApiRouter<State>>>,
21    pub api_auth: Option<Box<OpenApiRouter<State>>>,
22    pub api_client: Option<Box<OpenApiRouter<State>>>,
23    pub api_client_servers_server: Option<Box<OpenApiRouter<State>>>,
24    pub api_remote: Option<Box<OpenApiRouter<State>>>,
25    pub api_remote_servers_server: Option<Box<OpenApiRouter<State>>>,
26}
27
28impl ExtensionRouteBuilder {
29    pub fn new(state: State) -> Self {
30        Self {
31            state,
32            global: None,
33            api_admin: None,
34            api_auth: None,
35            api_client: None,
36            api_client_servers_server: None,
37            api_remote: None,
38            api_remote_servers_server: None,
39        }
40    }
41
42    /// Adds a router for handling requests to `/`.
43    pub fn add_global_router(
44        mut self,
45        router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
46    ) -> Self {
47        self.global = Some(Box::new(router(self.global.map_or_else(
48            || OpenApiRouter::new().with_state(self.state.clone()),
49            |b| *b,
50        ))));
51
52        self
53    }
54
55    /// Adds a router for handling requests to `/api/admin`.
56    /// Authentication middleware is already handled by the parent router.
57    pub fn add_admin_api_router(
58        mut self,
59        router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
60    ) -> Self {
61        self.api_admin = Some(Box::new(router(self.api_admin.map_or_else(
62            || OpenApiRouter::new().with_state(self.state.clone()),
63            |b| *b,
64        ))));
65
66        self
67    }
68
69    /// Adds a router for handling requests to `/api/auth`.
70    pub fn add_auth_api_router(
71        mut self,
72        router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
73    ) -> Self {
74        self.api_auth = Some(Box::new(router(self.api_auth.map_or_else(
75            || OpenApiRouter::new().with_state(self.state.clone()),
76            |b| *b,
77        ))));
78
79        self
80    }
81
82    /// Adds a router for handling requests to `/api/client`.
83    /// Authentication middleware is already handled by the parent router.
84    pub fn add_client_api_router(
85        mut self,
86        router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
87    ) -> Self {
88        self.api_client = Some(Box::new(router(self.api_client.map_or_else(
89            || OpenApiRouter::new().with_state(self.state.clone()),
90            |b| *b,
91        ))));
92
93        self
94    }
95
96    /// Adds a router for handling requests to `/api/client/servers/{server}`.
97    /// Authentication middleware is already handled by the parent router.
98    pub fn add_client_server_api_router(
99        mut self,
100        router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
101    ) -> Self {
102        self.api_client_servers_server = Some(Box::new(router(
103            self.api_client_servers_server.map_or_else(
104                || OpenApiRouter::new().with_state(self.state.clone()),
105                |b| *b,
106            ),
107        )));
108
109        self
110    }
111
112    /// Adds a router for handling requests to `/api/remote`.
113    /// Authentication middleware is already handled by the parent router.
114    pub fn add_remote_api_router(
115        mut self,
116        router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
117    ) -> Self {
118        self.api_remote = Some(Box::new(router(self.api_remote.map_or_else(
119            || OpenApiRouter::new().with_state(self.state.clone()),
120            |b| *b,
121        ))));
122
123        self
124    }
125
126    /// Adds a router for handling requests to `/api/admin`.
127    /// Authentication middleware is already handled by the parent router.
128    pub fn add_remote_server_api_router(
129        mut self,
130        router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
131    ) -> Self {
132        self.api_remote_servers_server = Some(Box::new(router(
133            self.api_remote_servers_server.map_or_else(
134                || OpenApiRouter::new().with_state(self.state.clone()),
135                |b| *b,
136            ),
137        )));
138
139        self
140    }
141}
142
143type RawPermissionMap = IndexMap<&'static str, PermissionGroup>;
144pub struct ExtensionPermissionsBuilder {
145    pub user_permissions: RawPermissionMap,
146    pub admin_permissions: RawPermissionMap,
147    pub server_permissions: RawPermissionMap,
148}
149
150impl ExtensionPermissionsBuilder {
151    pub fn new(
152        user_permissions: RawPermissionMap,
153        admin_permissions: RawPermissionMap,
154        server_permissions: RawPermissionMap,
155    ) -> Self {
156        Self {
157            user_permissions,
158            admin_permissions,
159            server_permissions,
160        }
161    }
162
163    /// Adds a permission group to the user permissions.
164    pub fn add_user_permission_group(
165        mut self,
166        group_name: &'static str,
167        group: PermissionGroup,
168    ) -> Self {
169        self.user_permissions.insert(group_name, group);
170
171        self
172    }
173
174    /// Adds a permission group to the admin permissions.
175    pub fn add_admin_permission_group(
176        mut self,
177        group_name: &'static str,
178        group: PermissionGroup,
179    ) -> Self {
180        self.admin_permissions.insert(group_name, group);
181
182        self
183    }
184
185    /// Adds a permission group to the server permissions.
186    pub fn add_server_permission_group(
187        mut self,
188        group_name: &'static str,
189        group: PermissionGroup,
190    ) -> Self {
191        self.server_permissions.insert(group_name, group);
192
193        self
194    }
195}
196
197pub type ExtensionCallValue = Box<dyn std::any::Any + Send + Sync>;
198
199#[async_trait::async_trait]
200pub trait Extension: Send + Sync {
201    /// Your extension entrypoint, this runs as soon as the database is migrated and before the webserver starts
202    async fn initialize(&mut self, state: State) {}
203
204    /// Your extension cli entrypoint, this runs after the env has been parsed
205    async fn initialize_cli(
206        &mut self,
207        env: Option<&Arc<crate::env::Env>>,
208        builder: commands::CliCommandGroupBuilder,
209    ) -> commands::CliCommandGroupBuilder {
210        builder
211    }
212
213    /// Your extension routes entrypoint, this runs as soon as the database is migrated and before the webserver starts
214    async fn initialize_router(
215        &mut self,
216        state: State,
217        builder: ExtensionRouteBuilder,
218    ) -> ExtensionRouteBuilder {
219        builder
220    }
221
222    /// Your extension background tasks entrypoint, this runs as soon as the database is migrated and before the webserver starts
223    async fn initialize_background_tasks(
224        &mut self,
225        state: State,
226        builder: background_tasks::BackgroundTaskBuilder,
227    ) -> background_tasks::BackgroundTaskBuilder {
228        builder
229    }
230
231    /// Your extension shutdown handler entrypoint, this runs as soon as the database is migrated and before the webserver starts
232    async fn initialize_shutdown_handlers(
233        &mut self,
234        state: State,
235        builder: shutdown_handlers::ShutdownHandlerBuilder,
236    ) -> shutdown_handlers::ShutdownHandlerBuilder {
237        builder
238    }
239
240    /// Your extension permissions entrypoint, this runs as soon as the database is migrated and before the webserver starts
241    async fn initialize_permissions(
242        &mut self,
243        state: State,
244        builder: ExtensionPermissionsBuilder,
245    ) -> ExtensionPermissionsBuilder {
246        builder
247    }
248
249    /// Your extension settings deserializer, this is used to deserialize your extension settings from the database
250    /// Whatever value you return in the `deserialize_boxed` method must match the trait `ExtensionSettings`, which requires
251    /// `SettingsSerializeExt` to be implemented for it. If you have no clue what this means. copy code from the docs.
252    async fn settings_deserializer(&self, state: State) -> settings::ExtensionSettingsDeserializer {
253        Arc::new(settings::EmptySettings)
254    }
255
256    /// Your extension call processor, this can be called by other extensions to interact with yours,
257    /// if the call does not apply to your extension, simply return `None` to continue the matching process.
258    ///
259    /// Optimally (if applies) make sure your calls are globally unique, for example by prepending them with your package name
260    async fn process_call(
261        &self,
262        name: &str,
263        args: &[ExtensionCallValue],
264    ) -> Option<ExtensionCallValue> {
265        None
266    }
267
268    /// Your extension call processor, this can be called by other extensions to interact with yours,
269    /// if the call does not apply to your extension, simply return `None` to continue the matching process.
270    ///
271    /// The only difference to `process_call` is that this takes an owned vec, its automatically implemented in terms of `process_call`.
272    ///
273    /// Optimally (if applies) make sure your calls are globally unique, for example by prepending them with your package name
274    async fn process_call_owned(
275        &self,
276        name: &str,
277        args: Vec<ExtensionCallValue>,
278    ) -> Option<ExtensionCallValue> {
279        self.process_call(name, &args).await
280    }
281}
282
283#[derive(ToSchema, Serialize, Clone)]
284pub struct ConstructedExtension {
285    pub metadata_toml: distr::MetadataToml,
286    pub package_name: &'static str,
287    pub description: &'static str,
288    pub authors: &'static [&'static str],
289    #[schema(value_type = String)]
290    pub version: semver::Version,
291
292    #[serde(skip)]
293    #[schema(ignore)]
294    pub extension: Arc<dyn Extension>,
295}
296
297impl Deref for ConstructedExtension {
298    type Target = Arc<dyn Extension>;
299
300    fn deref(&self) -> &Self::Target {
301        &self.extension
302    }
303}