1use crate::{auth::AdminAuth, entity::{EntityAdmin, EntityGroupAdmin}, render::AdminRenderer};
2use std::{path::PathBuf, sync::Arc};
3
4pub enum AdminEntry {
5 Entity(EntityAdmin),
6 Group(EntityGroupAdmin),
7}
8
9impl From<EntityAdmin> for AdminEntry {
10 fn from(e: EntityAdmin) -> Self { AdminEntry::Entity(e) }
11}
12
13impl From<EntityGroupAdmin> for AdminEntry {
14 fn from(g: EntityGroupAdmin) -> Self { AdminEntry::Group(g) }
15}
16
17pub struct AdminAppState {
18 pub title: String,
19 pub icon: String,
20 pub entities: Vec<EntityAdmin>,
21 pub renderer: AdminRenderer,
22 #[cfg(feature = "seaorm")]
23 pub enforcer: Option<std::sync::Arc<tokio::sync::RwLock<casbin::Enforcer>>>,
24 #[cfg(not(feature = "seaorm"))]
25 pub enforcer: Option<()>,
26 #[cfg(feature = "seaorm")]
27 pub seaorm_auth: Option<std::sync::Arc<crate::adapters::seaorm_auth::SeaOrmAdminAuth>>,
28 pub show_auth_nav: bool,
29}
30
31pub struct AdminApp {
32 pub title: String,
33 pub icon: String,
34 pub prefix: String,
35 pub entities: Vec<EntityAdmin>,
36 pub auth: Option<Arc<dyn AdminAuth>>,
37 pub(crate) templates: Vec<(String, String)>,
38 pub(crate) template_dirs: Vec<PathBuf>,
39 pub upload_limit: usize,
41 #[cfg(feature = "seaorm")]
42 pub(crate) enforcer: Option<std::sync::Arc<tokio::sync::RwLock<casbin::Enforcer>>>,
43 #[cfg(not(feature = "seaorm"))]
44 pub(crate) enforcer: Option<()>,
45 #[cfg(feature = "seaorm")]
46 pub(crate) seaorm_auth: Option<std::sync::Arc<crate::adapters::seaorm_auth::SeaOrmAdminAuth>>,
47}
48
49impl AdminApp {
50 pub fn new() -> Self {
51 Self {
52 title: "Admin".to_string(),
53 icon: "fa-solid fa-bolt".to_string(),
54 prefix: "/admin".to_string(),
55 entities: Vec::new(),
56 auth: None,
57 templates: Vec::new(),
58 template_dirs: Vec::new(),
59 upload_limit: 10 * 1024 * 1024, enforcer: None,
61 #[cfg(feature = "seaorm")]
62 seaorm_auth: None,
63 }
64 }
65
66 pub fn upload_limit(mut self, bytes: usize) -> Self {
69 self.upload_limit = bytes;
70 self
71 }
72
73 pub fn title(mut self, title: &str) -> Self {
74 self.title = title.to_string();
75 self
76 }
77
78 pub fn icon(mut self, icon: &str) -> Self {
81 self.icon = icon.to_string();
82 self
83 }
84
85 pub fn prefix(mut self, prefix: &str) -> Self {
86 self.prefix = prefix.to_string();
87 self
88 }
89
90 pub fn register(mut self, entry: impl Into<AdminEntry>) -> Self {
91 match entry.into() {
92 AdminEntry::Entity(e) => self.entities.push(e),
93 AdminEntry::Group(g) => self.entities.extend(g.into_entities()),
94 }
95 self
96 }
97
98 pub fn auth(mut self, auth: Box<dyn AdminAuth>) -> Self {
99 self.auth = Some(Arc::from(auth));
100 self
101 }
102
103 #[cfg(feature = "seaorm")]
104 pub fn seaorm_auth(mut self, auth: crate::adapters::seaorm_auth::SeaOrmAdminAuth) -> Self {
105 let arc = std::sync::Arc::new(auth);
106 self.enforcer = Some(arc.enforcer());
107 self.auth = Some(arc.clone() as std::sync::Arc<dyn crate::auth::AdminAuth>);
108 self.seaorm_auth = Some(arc);
109 self
110 }
111
112 pub fn template(mut self, name: &str, content: &str) -> Self {
116 self.templates.push((name.to_string(), content.to_string()));
117 self
118 }
119
120 pub fn template_dir(mut self, path: impl Into<PathBuf>) -> Self {
126 self.template_dirs.push(path.into());
127 self
128 }
129
130 pub(crate) fn into_state(self) -> (Arc<dyn AdminAuth>, Arc<AdminAppState>, usize) {
131 let auth = self
132 .auth
133 .expect("AdminApp requires .auth() to be configured before calling into_router()");
134
135 let mut all_templates: Vec<(String, String)> = Vec::new();
138 for dir in &self.template_dirs {
139 if let Ok(entries) = std::fs::read_dir(dir) {
140 for entry in entries.flatten() {
141 let path = entry.path();
142 if path.extension().and_then(|e| e.to_str()) == Some("html") {
143 if let (Some(name), Ok(content)) = (
144 path.file_name().and_then(|n| n.to_str()).map(str::to_string),
145 std::fs::read_to_string(&path),
146 ) {
147 all_templates.push((name, content));
148 }
149 }
150 }
151 }
152 }
153 all_templates.extend(self.templates);
154
155 let upload_limit = self.upload_limit;
156 let show_auth_nav = {
157 #[cfg(feature = "seaorm")]
158 { self.seaorm_auth.is_some() }
159 #[cfg(not(feature = "seaorm"))]
160 { false }
161 };
162 let state = Arc::new(AdminAppState {
163 title: self.title,
164 icon: self.icon,
165 entities: self.entities,
166 renderer: AdminRenderer::with_overrides(all_templates),
167 enforcer: self.enforcer,
168 #[cfg(feature = "seaorm")]
169 seaorm_auth: self.seaorm_auth,
170 show_auth_nav,
171 });
172 (auth, state, upload_limit)
173 }
174}
175
176impl Default for AdminApp {
177 fn default() -> Self {
178 Self::new()
179 }
180}