refactor: moving to my own template, using fomantic instead of uikit. removing uikit, and some other removed stuff
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
pub use config::AdminModelConfig;
|
||||
pub use dto::AdminApp;
|
||||
pub use dto::AdminModel;
|
||||
pub use repository::{AdminRepository, RepositoryList};
|
||||
|
||||
mod auth {
|
||||
|
||||
@@ -12,6 +14,14 @@ mod auth {
|
||||
struct AdminActionLog {}
|
||||
}
|
||||
|
||||
mod config {
|
||||
// user uses this configuration object to register another model.
|
||||
pub struct AdminModelConfig {
|
||||
pub name: String,
|
||||
pub app_key: String,
|
||||
}
|
||||
}
|
||||
|
||||
mod dto {
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -26,12 +36,6 @@ mod dto {
|
||||
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,
|
||||
@@ -40,10 +44,59 @@ mod dto {
|
||||
pub app_url: String,
|
||||
pub models: Vec<AdminModel>,
|
||||
}
|
||||
}
|
||||
|
||||
impl AdminApp {
|
||||
pub fn app_label(&self) -> &'static str {
|
||||
"gogo"
|
||||
pub mod repository {
|
||||
use serde::{Serialize, Serializer};
|
||||
use serde_json::Value;
|
||||
use std::vec::IntoIter;
|
||||
|
||||
pub enum RepositoryList {
|
||||
Empty,
|
||||
List {
|
||||
values: Vec<Value>,
|
||||
},
|
||||
Page {
|
||||
values: Vec<Value>,
|
||||
offset: usize,
|
||||
total: usize,
|
||||
},
|
||||
Stream {
|
||||
values: Vec<Value>,
|
||||
next_index: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
impl IntoIterator for RepositoryList {
|
||||
type Item = Value;
|
||||
type IntoIter = IntoIter<Self::Item>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
match self {
|
||||
RepositoryList::Empty => vec![].into_iter(),
|
||||
RepositoryList::List { values } => values.into_iter(),
|
||||
RepositoryList::Page { values, .. } => values.into_iter(),
|
||||
RepositoryList::Stream { values, .. } => values.into_iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for RepositoryList {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match self {
|
||||
RepositoryList::Empty => serializer.serialize_unit(),
|
||||
RepositoryList::List { values }
|
||||
| RepositoryList::Page { values, .. }
|
||||
| RepositoryList::Stream { values, .. } => values.serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AdminRepository: Send + Sync {
|
||||
fn get_item(&self, id: usize) -> Option<Value>;
|
||||
fn get_list(&self) -> RepositoryList;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// implementation of static repository
|
||||
|
||||
use super::state::{config::AdminModelConfig, AdminRegistry, AdminRepository};
|
||||
use super::domain::{AdminModelConfig, AdminRepository, RepositoryList};
|
||||
use super::state::AdminRegistry;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
struct MyStaticRepository {}
|
||||
@@ -13,11 +14,13 @@ impl AdminRepository for MyStaticRepository {
|
||||
}))
|
||||
}
|
||||
|
||||
fn get_list(&self) -> Value {
|
||||
json!([
|
||||
{"name": "Strange", "age": 150 },
|
||||
{"name": "Adam", "age": 12}
|
||||
])
|
||||
fn get_list(&self) -> RepositoryList {
|
||||
RepositoryList::List {
|
||||
values: vec![
|
||||
json!({"name": "Strange", "age": 150 }),
|
||||
json!({"name": "Adam", "age": 12}),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
use crate::admin::domain::{AdminApp, AdminModel};
|
||||
use axum::Router;
|
||||
use serde_json::Value;
|
||||
use super::domain::{AdminApp, AdminModel, AdminModelConfig, AdminRepository};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub type AdminState = Arc<AdminRegistry>;
|
||||
|
||||
pub trait AdminRepository: Send + Sync {
|
||||
fn get_item(&self, id: usize) -> Option<Value>;
|
||||
fn get_list(&self) -> Value;
|
||||
}
|
||||
|
||||
// main registry.
|
||||
pub struct AdminRegistry {
|
||||
base_path: String,
|
||||
@@ -41,7 +34,7 @@ impl AdminRegistry {
|
||||
AdminApp {
|
||||
name: key.to_owned(),
|
||||
key: node.name.to_owned(),
|
||||
app_url: key.to_owned(),
|
||||
app_url: format!("/{}/app/{}", self.base_path, key.to_owned()),
|
||||
models: my_models,
|
||||
}
|
||||
}
|
||||
@@ -67,7 +60,7 @@ impl AdminRegistry {
|
||||
key: internal_model.model_key.clone(),
|
||||
name: internal_model.name.clone(),
|
||||
admin_url: format!(
|
||||
"{}/app/{}/model/{}",
|
||||
"/{}/app/{}/model/{}",
|
||||
self.base_path, internal_model.app_key, internal_model.model_key
|
||||
),
|
||||
view_only: false,
|
||||
@@ -91,7 +84,7 @@ impl AdminRegistry {
|
||||
Some(self.model_from_internal(internal_model))
|
||||
}
|
||||
|
||||
fn register_model_config(&mut self, model: config::AdminModelConfig) -> Result<String, String> {
|
||||
fn register_model_config(&mut self, model: AdminModelConfig) -> Result<String, String> {
|
||||
let local_config = internal::AdminModel::from(model);
|
||||
if local_config.model_key.is_empty() {
|
||||
return Err("No model name".to_owned());
|
||||
@@ -107,7 +100,7 @@ impl AdminRegistry {
|
||||
|
||||
pub fn register_model<R: AdminRepository + 'static>(
|
||||
&mut self,
|
||||
model: config::AdminModelConfig,
|
||||
model: AdminModelConfig,
|
||||
repository: R,
|
||||
) -> Result<(), String> {
|
||||
let model_key = self.register_model_config(model)?;
|
||||
@@ -124,15 +117,10 @@ impl AdminRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
pub mod config {
|
||||
// user uses this configuration object to register another model.
|
||||
pub struct AdminModelConfig {
|
||||
pub name: String,
|
||||
pub app_key: String,
|
||||
}
|
||||
}
|
||||
|
||||
mod internal {
|
||||
// how the registry saves data internally.
|
||||
|
||||
use super::super::domain::AdminModelConfig;
|
||||
#[derive(Clone)]
|
||||
pub struct AdminApp {
|
||||
pub key: String,
|
||||
@@ -146,8 +134,8 @@ mod internal {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl From<super::config::AdminModelConfig> for AdminModel {
|
||||
fn from(value: super::config::AdminModelConfig) -> Self {
|
||||
impl From<AdminModelConfig> for AdminModel {
|
||||
fn from(value: AdminModelConfig) -> Self {
|
||||
AdminModel {
|
||||
app_key: value.app_key,
|
||||
model_key: slug::slugify(value.name.clone()),
|
||||
|
||||
@@ -10,6 +10,8 @@ use crate::service::templates;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
use super::domain::RepositoryList;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Question {
|
||||
question: String,
|
||||
@@ -26,11 +28,12 @@ pub struct AdminRequest {
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Serialize)]
|
||||
pub struct AdminContext {
|
||||
pub language_code: Option<String>,
|
||||
pub language_bidi: Option<bool>,
|
||||
pub user: Option<String>, // Todo: user type
|
||||
pub admin_url: String,
|
||||
pub site_url: Option<String>,
|
||||
pub docsroot: Option<String>,
|
||||
pub messages: Vec<String>, // Todo: message type
|
||||
@@ -40,7 +43,7 @@ pub struct AdminContext {
|
||||
|
||||
pub request: AdminRequest,
|
||||
pub available_apps: Vec<AdminApp>,
|
||||
pub item_list: Option<Value>,
|
||||
pub item_list: RepositoryList,
|
||||
pub item: Option<Value>,
|
||||
}
|
||||
|
||||
@@ -50,6 +53,7 @@ impl Default for AdminContext {
|
||||
language_code: Some("en-us".to_string()), // Default language code
|
||||
language_bidi: Some(false), // Default language bidi
|
||||
user: None, //UserType::default(), // Assuming UserType has a Default impl
|
||||
admin_url: "/admin".to_owned(),
|
||||
site_url: None,
|
||||
docsroot: None,
|
||||
messages: Vec::new(), // Empty vector for messages
|
||||
@@ -60,7 +64,7 @@ impl Default for AdminContext {
|
||||
request: AdminRequest {
|
||||
path: "".to_owned(),
|
||||
},
|
||||
item_list: None,
|
||||
item_list: RepositoryList::Empty,
|
||||
item: None,
|
||||
}
|
||||
}
|
||||
@@ -88,7 +92,7 @@ pub async fn list_app(
|
||||
templates: State<templates::Templates>,
|
||||
Path(app_key): Path<String>,
|
||||
) -> impl IntoResponse {
|
||||
templates.render_html("admin/app_list.html", ())
|
||||
templates.render_html("admin/app_list.jinja", ())
|
||||
}
|
||||
|
||||
// List Items renders the entire list item page.
|
||||
@@ -101,15 +105,17 @@ pub async fn list_item_collection(
|
||||
let context = if let Ok(repo) = registry.get_repository(&model_key) {
|
||||
// we should consider using Vec<Value> instead in get_list.
|
||||
AdminContext {
|
||||
item_list: Some(repo.get_list()),
|
||||
available_apps: registry.get_apps(),
|
||||
item_list: repo.get_list(),
|
||||
..Default::default()
|
||||
}
|
||||
} else {
|
||||
AdminContext {
|
||||
available_apps: registry.get_apps(),
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
templates.render_html("admin/items/item_list.html", context)
|
||||
templates.render_html("admin/items/item_list.jinja", context)
|
||||
}
|
||||
|
||||
// Items Action is a POST to an item list. By default these are actions, that work on a list of items as input.
|
||||
@@ -125,7 +131,7 @@ pub async fn item_details(
|
||||
templates: State<templates::Templates>,
|
||||
Path((app_key, model_key, id)): Path<(String, String, String)>,
|
||||
) -> impl IntoResponse {
|
||||
templates.render_html("admin/items/item_detail.html", ())
|
||||
templates.render_html("admin/items/item_detail.jinja", ())
|
||||
}
|
||||
|
||||
// Item Action allows running an action on one single dataset.
|
||||
|
||||
@@ -12,7 +12,7 @@ use axum::{
|
||||
use dotenvy::dotenv;
|
||||
use log::info;
|
||||
use std::env;
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
|
||||
async fn home(templates: State<templates::Templates>) -> impl IntoResponse {
|
||||
@@ -60,7 +60,7 @@ async fn main() {
|
||||
.unwrap_or("3000".to_string())
|
||||
.parse()
|
||||
.expect("Port expected in APP_PORT");
|
||||
// the listen_addr is the address we bind to.
|
||||
// the listen_addr is the address we bind to. This might be multiple domains, like 0.0.0.0
|
||||
let listen_addr = SocketAddr::from((app_host, app_port));
|
||||
// the server addr is a concrete address the user can connect to.
|
||||
let server_addr = if app_host.is_unspecified() {
|
||||
|
||||
@@ -16,6 +16,7 @@ impl Templates {
|
||||
let mut environment = Environment::new();
|
||||
let template_path = "templates";
|
||||
environment.set_loader(path_loader(&template_path));
|
||||
environment.add_filter("none", none);
|
||||
environment.add_filter("markdown", markdown);
|
||||
environment.add_filter("yesno", filter_yesno);
|
||||
environment.add_function("static", tpl_static);
|
||||
@@ -78,9 +79,17 @@ fn tpl_translate(value: String) -> Value {
|
||||
}
|
||||
|
||||
fn tpl_to_be_implemented(value: String) -> Value {
|
||||
Value::from_safe_string("".into())
|
||||
Value::from_safe_string("<!-- To Be Implemented --/>".into())
|
||||
}
|
||||
|
||||
fn tpl_static(value: String) -> Value {
|
||||
Value::from_safe_string("".into())
|
||||
Value::from_safe_string(format!("/static/{}", value))
|
||||
}
|
||||
|
||||
pub fn none(value: Value, other: Option<Value>) -> Value {
|
||||
if value.is_undefined() || value.is_none() {
|
||||
other.unwrap_or_else(|| Value::from(""))
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
10
src/state.rs
10
src/state.rs
@@ -1,14 +1,12 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::extract::FromRef;
|
||||
|
||||
use crate::admin::state::AdminRegistry;
|
||||
use crate::admin::state::AdminState;
|
||||
use crate::service::templates;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
pub templates: templates::Templates,
|
||||
pub admin: Arc<AdminRegistry>,
|
||||
pub admin: AdminState,
|
||||
}
|
||||
|
||||
impl FromRef<AppState> for templates::Templates {
|
||||
@@ -17,8 +15,8 @@ impl FromRef<AppState> for templates::Templates {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRef<AppState> for Arc<AdminRegistry> {
|
||||
fn from_ref(app_state: &AppState) -> Arc<AdminRegistry> {
|
||||
impl FromRef<AppState> for AdminState {
|
||||
fn from_ref(app_state: &AppState) -> AdminState {
|
||||
app_state.admin.clone()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user