refactor: trying to align my naming into something specific. removing dynamic routing code.
This commit is contained in:
@@ -17,19 +17,33 @@ mod dto {
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct AdminModel {
|
||||
pub key: String,
|
||||
pub name: String,
|
||||
pub object_name: String,
|
||||
|
||||
pub admin_url: String,
|
||||
pub view_only: bool,
|
||||
|
||||
pub add_url: Option<String>,
|
||||
}
|
||||
|
||||
impl AdminModel {
|
||||
pub fn object_name(&self) -> &'static str {
|
||||
"hi there"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct AdminApp {
|
||||
pub key: String,
|
||||
pub name: String,
|
||||
pub app_label: String,
|
||||
|
||||
pub app_url: String,
|
||||
pub models: Vec<AdminModel>,
|
||||
}
|
||||
|
||||
impl AdminApp {
|
||||
pub fn app_label(&self) -> &'static str {
|
||||
"gogo"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@ pub mod views;
|
||||
pub fn routes() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/", get(views::index).post(views::index_action))
|
||||
.route("/app/:app_name", get(views::list_app))
|
||||
.route("/app/:app_key", get(views::list_app))
|
||||
.route(
|
||||
"/app/:app_name/:model_name",
|
||||
"/app/:app_key/models/:model_key",
|
||||
get(views::list_item_collection),
|
||||
)
|
||||
.route(
|
||||
"/app/:app_name/:model_name/detail/:model_id",
|
||||
"/app/:app_key/models/:model_key/detail/:id",
|
||||
get(views::item_details),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -29,18 +29,6 @@ impl AdminRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_router<T>(&self) -> Router<T>
|
||||
where
|
||||
T: Clone,
|
||||
T: Send,
|
||||
T: Sync,
|
||||
T: 'static,
|
||||
crate::service::templates::Templates: axum::extract::FromRef<T>,
|
||||
AdminState: axum::extract::FromRef<T>,
|
||||
{
|
||||
routing::generate_routes::<T>(&self)
|
||||
}
|
||||
|
||||
pub fn get_apps(&self) -> Vec<AdminApp> {
|
||||
self.apps
|
||||
.iter()
|
||||
@@ -49,37 +37,41 @@ impl AdminRegistry {
|
||||
}
|
||||
|
||||
fn get_app(&self, name: &str, node: &internal::AdminApp) -> AdminApp {
|
||||
let label = node.label.as_ref().unwrap_or(&String::new()).clone();
|
||||
AdminApp {
|
||||
name: name.to_owned(),
|
||||
app_label: label,
|
||||
key: node.key.to_owned(),
|
||||
app_url: name.to_owned().to_lowercase(),
|
||||
models: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_app(&mut self, name: &str, app_label: &str, app_url: &str) {
|
||||
let key = self.get_key(name);
|
||||
self.apps.insert(
|
||||
name.to_owned(),
|
||||
key.to_owned(),
|
||||
internal::AdminApp {
|
||||
object_name: name.to_owned(),
|
||||
label: Some(app_label.to_owned()),
|
||||
key: key,
|
||||
name: name.to_owned(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn get_models(&self, app_name: &str) -> Vec<AdminModel> {
|
||||
fn get_key(&self, name: &str) -> String {
|
||||
slug::slugify(name)
|
||||
}
|
||||
|
||||
pub fn get_models(&self, app_key: &str) -> Vec<AdminModel> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
pub fn get_model(&self, app_name: &str, model_name: &str) -> Option<AdminModel> {
|
||||
let full_model_name = format!("{}.{}", app_name, model_name);
|
||||
let internal_model = self.models.get(&full_model_name)?;
|
||||
pub fn get_model(&self, app_key: &str, model_key: &str) -> Option<AdminModel> {
|
||||
let full_model_key = format!("{}.{}", app_key, model_key);
|
||||
let internal_model = self.models.get(&full_model_key)?;
|
||||
|
||||
// unfinished: we need to think about model_name vs. model_id vs. entry_id, as "name" is ambiguous.
|
||||
// unfinished: we need to think about model_key vs. model_id vs. entry_id, as "name" is ambiguous.
|
||||
Some(AdminModel {
|
||||
object_name: internal_model.model_name.clone(),
|
||||
name: internal_model.model_name.clone(),
|
||||
key: internal_model.model_key.clone(),
|
||||
name: internal_model.model_key.clone(),
|
||||
admin_url: "".to_owned(),
|
||||
view_only: false,
|
||||
add_url: None,
|
||||
@@ -88,15 +80,15 @@ impl AdminRegistry {
|
||||
|
||||
fn register_model_config(&mut self, model: config::AdminModelConfig) -> Result<String, String> {
|
||||
let local_config = internal::AdminModel::from(model);
|
||||
if local_config.model_name.is_empty() {
|
||||
if local_config.model_key.is_empty() {
|
||||
return Err("No model name".to_owned());
|
||||
}
|
||||
let local_config_name = format!("{}.{}", local_config.app_name, local_config.model_name);
|
||||
let local_config_name = format!("{}.{}", local_config.app_key, local_config.model_key);
|
||||
if self.models.contains_key(&local_config_name) {
|
||||
return Err(format!("Model {} already exists", local_config_name));
|
||||
}
|
||||
if let Some(local_config) = self.models.insert(local_config_name, local_config) {
|
||||
return Ok(local_config.model_name);
|
||||
return Ok(local_config.model_key);
|
||||
}
|
||||
Err("Model could not be added!".to_owned())
|
||||
}
|
||||
@@ -106,111 +98,41 @@ impl AdminRegistry {
|
||||
model: config::AdminModelConfig,
|
||||
repository: R,
|
||||
) -> Result<(), String> {
|
||||
let model_name = self.register_model_config(model)?;
|
||||
self.repositories.insert(model_name, Box::new(repository));
|
||||
let model_key = self.register_model_config(model)?;
|
||||
self.repositories.insert(model_key, Box::new(repository));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub mod config {
|
||||
use axum::routing::MethodRouter;
|
||||
|
||||
// user uses this configuration object to register another model.
|
||||
pub struct AdminModelConfig {
|
||||
pub name: String,
|
||||
pub app_name: String,
|
||||
|
||||
pub custom_index_handler: Option<MethodRouter>,
|
||||
pub app_key: String,
|
||||
}
|
||||
}
|
||||
|
||||
mod internal {
|
||||
#[derive(Clone)]
|
||||
pub struct AdminApp {
|
||||
pub object_name: String,
|
||||
pub label: Option<String>,
|
||||
pub key: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AdminModel {
|
||||
pub app_name: String,
|
||||
pub model_name: String,
|
||||
pub app_key: String,
|
||||
pub model_key: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl From<super::config::AdminModelConfig> for AdminModel {
|
||||
fn from(value: super::config::AdminModelConfig) -> Self {
|
||||
AdminModel {
|
||||
app_name: value.app_name,
|
||||
model_name: value.name,
|
||||
app_key: value.app_key,
|
||||
model_key: value.name.clone(),
|
||||
name: value.name,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod routing {
|
||||
use axum::{routing::get, Router};
|
||||
|
||||
pub fn generate_routes<T>(registry: &super::AdminRegistry) -> Router<T>
|
||||
where
|
||||
T: Clone,
|
||||
T: Send,
|
||||
T: Sync,
|
||||
T: 'static,
|
||||
crate::service::templates::Templates: axum::extract::FromRef<T>,
|
||||
super::AdminState: axum::extract::FromRef<T>,
|
||||
{
|
||||
let mut router: Router<T> = Router::new();
|
||||
let apps = registry.get_apps();
|
||||
router = router.route(
|
||||
&format!("/{}", registry.base_path),
|
||||
get(crate::admin::views::index),
|
||||
);
|
||||
for app in apps {
|
||||
let app_route = format!("/{}/{}", registry.base_path, app.app_url);
|
||||
|
||||
// Add a route for the app
|
||||
router = router.route(
|
||||
&app_route,
|
||||
get(move || async move { format!("Admin panel for {}", app.app_label) }),
|
||||
);
|
||||
|
||||
// Add routes for each model in the app
|
||||
/*for model in data_node.models.iter() {
|
||||
let model_route = format!("{}/{}", app_route, model.name);
|
||||
router = router.route(
|
||||
&model_route,
|
||||
get(move || async move { format!("Model page for {}", model.name) }),
|
||||
);
|
||||
}*/
|
||||
}
|
||||
|
||||
router
|
||||
}
|
||||
|
||||
fn define_example_data(registry: &mut super::AdminRegistry) {
|
||||
/*let models = vec![
|
||||
AdminModel {
|
||||
name: "User".to_owned(),
|
||||
object_name: "User".to_owned(),
|
||||
admin_url: "/admin/users/user".to_owned(),
|
||||
view_only: false,
|
||||
add_url: Some("/admin/users/user/add".to_owned()),
|
||||
},
|
||||
AdminModel {
|
||||
name: "Group".to_owned(),
|
||||
object_name: "Group".to_owned(),
|
||||
admin_url: "/admin/users/group".to_owned(),
|
||||
view_only: false,
|
||||
add_url: None,
|
||||
},
|
||||
AdminModel {
|
||||
name: "Permission".to_owned(),
|
||||
object_name: "Permission".to_owned(),
|
||||
admin_url: "/admin/users/permission".to_owned(),
|
||||
view_only: true,
|
||||
add_url: None,
|
||||
},
|
||||
];*/
|
||||
registry.register_app("auth", "Authorities", "auth");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,46 +60,6 @@ impl Default for AdminContext {
|
||||
}
|
||||
}
|
||||
|
||||
// Index is the entry point of Admin. It should display the Dashboard to a logged in user.
|
||||
pub async fn index_example(templates: State<templates::Templates>) -> impl IntoResponse {
|
||||
let models = vec![
|
||||
AdminModel {
|
||||
name: "User".to_owned(),
|
||||
object_name: "User".to_owned(),
|
||||
admin_url: "/admin/users/user".to_owned(),
|
||||
view_only: false,
|
||||
add_url: Some("/admin/users/user/add".to_owned()),
|
||||
},
|
||||
AdminModel {
|
||||
name: "Group".to_owned(),
|
||||
object_name: "Group".to_owned(),
|
||||
admin_url: "/admin/users/group".to_owned(),
|
||||
view_only: false,
|
||||
add_url: None,
|
||||
},
|
||||
AdminModel {
|
||||
name: "Permission".to_owned(),
|
||||
object_name: "Permission".to_owned(),
|
||||
admin_url: "/admin/users/permission".to_owned(),
|
||||
view_only: true,
|
||||
add_url: None,
|
||||
},
|
||||
];
|
||||
let core_app = AdminApp {
|
||||
name: "Admin".to_owned(),
|
||||
app_label: "admin".to_owned(),
|
||||
app_url: "/admin/users/".to_owned(),
|
||||
models: models,
|
||||
};
|
||||
templates.render_html(
|
||||
"admin/index.html",
|
||||
AdminContext {
|
||||
available_apps: vec![core_app],
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn index(
|
||||
templates: State<templates::Templates>,
|
||||
administration: State<Arc<state::AdminRegistry>>,
|
||||
@@ -107,21 +67,21 @@ pub async fn index(
|
||||
let models = vec![
|
||||
AdminModel {
|
||||
name: "User".to_owned(),
|
||||
object_name: "User".to_owned(),
|
||||
admin_url: "/admin/users/user".to_owned(),
|
||||
key: "user".to_owned(),
|
||||
admin_url: "/admin/app/users/user".to_owned(),
|
||||
view_only: false,
|
||||
add_url: Some("/admin/users/user/add".to_owned()),
|
||||
},
|
||||
AdminModel {
|
||||
name: "Group".to_owned(),
|
||||
object_name: "Group".to_owned(),
|
||||
key: "group".to_owned(),
|
||||
admin_url: "/admin/users/group".to_owned(),
|
||||
view_only: false,
|
||||
add_url: None,
|
||||
},
|
||||
AdminModel {
|
||||
name: "Permission".to_owned(),
|
||||
object_name: "Permission".to_owned(),
|
||||
key: "permission".to_owned(),
|
||||
admin_url: "/admin/users/permission".to_owned(),
|
||||
view_only: true,
|
||||
add_url: None,
|
||||
@@ -129,7 +89,7 @@ pub async fn index(
|
||||
];
|
||||
let core_app = AdminApp {
|
||||
name: "Admin".to_owned(),
|
||||
app_label: "admin".to_owned(),
|
||||
key: "admin".to_owned(),
|
||||
app_url: "/admin/users/".to_owned(),
|
||||
models: models,
|
||||
};
|
||||
@@ -153,7 +113,7 @@ pub async fn index_action(Form(example_data): Form<ExampleData>) -> impl IntoRes
|
||||
|
||||
pub async fn list_app(
|
||||
templates: State<templates::Templates>,
|
||||
Path(app_name): Path<String>,
|
||||
Path(app_key): Path<String>,
|
||||
) -> impl IntoResponse {
|
||||
templates.render_html("admin/app_list.html", ())
|
||||
}
|
||||
@@ -161,7 +121,7 @@ pub async fn list_app(
|
||||
// List Items renders the entire list item page.
|
||||
pub async fn list_item_collection(
|
||||
templates: State<templates::Templates>,
|
||||
Path((app_name, model_name)): Path<(String, String)>,
|
||||
Path((app_key, model_key)): Path<(String, String)>,
|
||||
) -> impl IntoResponse {
|
||||
templates.render_html("admin/items/list_items.html", ())
|
||||
}
|
||||
@@ -169,7 +129,7 @@ pub async fn list_item_collection(
|
||||
// Items Action is a POST to an item list. By default these are actions, that work on a list of items as input.
|
||||
pub async fn item_collection_action(
|
||||
Form(question): Form<Question>,
|
||||
Path((app_name, model_name)): Path<(String, String)>,
|
||||
Path((app_key, model_key)): Path<(String, String)>,
|
||||
) -> impl IntoResponse {
|
||||
"There is your answer!".to_owned()
|
||||
}
|
||||
@@ -177,15 +137,15 @@ pub async fn item_collection_action(
|
||||
// Item Details shows one single dataset.
|
||||
pub async fn item_details(
|
||||
templates: State<templates::Templates>,
|
||||
Path((app_name, model_name, model_id)): Path<(String, String, String)>,
|
||||
Path((app_key, model_key, id)): Path<(String, String, String)>,
|
||||
) -> impl IntoResponse {
|
||||
templates.render_html("admin/item_detail.html", ())
|
||||
templates.render_html("admin/items/item_detail.html", ())
|
||||
}
|
||||
|
||||
// Item Action allows running an action on one single dataset.
|
||||
pub async fn item_action(
|
||||
Form(question): Form<Question>,
|
||||
Path((app_name, model_name, model_id)): Path<(String, String, String)>,
|
||||
Path((app_key, model_key, model_id)): Path<(String, String, String)>,
|
||||
) -> impl IntoResponse {
|
||||
"There is your answer!".to_owned()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user