Merge pull request 'Refactoring to Rear' (#2) from refactor_to_rear into main
Reviewed-on: #2
This commit is contained in:
commit
55ad8a15f6
@ -1 +1,4 @@
|
|||||||
|
APP_HOST=127.0.0.1
|
||||||
|
APP_PORT=3000
|
||||||
DATABASE_URL=postgresql://miniweb:miniweb@localhost:54321/miniweb
|
DATABASE_URL=postgresql://miniweb:miniweb@localhost:54321/miniweb
|
||||||
|
RUST_LOG=info
|
||||||
|
116
Cargo.lock
generated
116
Cargo.lock
generated
@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.7.7"
|
version = "0.7.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd"
|
checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@ -30,9 +30,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.8.6"
|
version = "0.8.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
|
checksum = "8b79b82693f705137f8fb9b37871d99e4f9a7df12b917eed79c3d3954830a60b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
@ -792,41 +792,6 @@ dependencies = [
|
|||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "darling"
|
|
||||||
version = "0.14.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
|
|
||||||
dependencies = [
|
|
||||||
"darling_core",
|
|
||||||
"darling_macro",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "darling_core"
|
|
||||||
version = "0.14.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0"
|
|
||||||
dependencies = [
|
|
||||||
"fnv",
|
|
||||||
"ident_case",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"strsim",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "darling_macro"
|
|
||||||
version = "0.14.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
|
|
||||||
dependencies = [
|
|
||||||
"darling_core",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "der"
|
name = "der"
|
||||||
version = "0.7.8"
|
version = "0.7.8"
|
||||||
@ -859,37 +824,6 @@ dependencies = [
|
|||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "derive_builder"
|
|
||||||
version = "0.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "660047478bc508c0fde22c868991eec0c40a63e48d610befef466d48e2bee574"
|
|
||||||
dependencies = [
|
|
||||||
"derive_builder_macro",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "derive_builder_core"
|
|
||||||
version = "0.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9b217e6dd1011a54d12f3b920a411b5abd44b1716ecfe94f5f2f2f7b52e08ab7"
|
|
||||||
dependencies = [
|
|
||||||
"darling",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "derive_builder_macro"
|
|
||||||
version = "0.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7a5f77d7e20ac9153428f7ca14a88aba652adfc7a0ef0a06d654386310ef663b"
|
|
||||||
dependencies = [
|
|
||||||
"derive_builder_core",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deunicode"
|
name = "deunicode"
|
||||||
version = "1.4.2"
|
version = "1.4.2"
|
||||||
@ -1311,7 +1245,7 @@ version = "0.12.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.7.7",
|
"ahash 0.7.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1320,7 +1254,7 @@ version = "0.14.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.8.6",
|
"ahash 0.8.10",
|
||||||
"allocator-api2",
|
"allocator-api2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1538,12 +1472,6 @@ dependencies = [
|
|||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ident_case"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@ -1828,7 +1756,6 @@ dependencies = [
|
|||||||
"async-trait",
|
"async-trait",
|
||||||
"axum 0.7.4",
|
"axum 0.7.4",
|
||||||
"barrel",
|
"barrel",
|
||||||
"derive_builder",
|
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"dunce",
|
"dunce",
|
||||||
"entity",
|
"entity",
|
||||||
@ -1839,13 +1766,13 @@ dependencies = [
|
|||||||
"minijinja-autoreload",
|
"minijinja-autoreload",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"pulldown-cmark",
|
"pulldown-cmark",
|
||||||
|
"rear",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
"sea-orm",
|
"sea-orm",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"slug",
|
"slug",
|
||||||
"sqlformat",
|
"sqlformat",
|
||||||
"strinto",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"tracing",
|
"tracing",
|
||||||
@ -2371,6 +2298,24 @@ dependencies = [
|
|||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rear"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"async-trait",
|
||||||
|
"axum 0.7.4",
|
||||||
|
"log",
|
||||||
|
"minijinja",
|
||||||
|
"minijinja-autoreload",
|
||||||
|
"pulldown-cmark",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"slug",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
@ -3016,7 +2961,7 @@ version = "0.7.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d84b0a3c3739e220d94b3239fd69fb1f74bc36e16643423bd99de3b43c21bfbd"
|
checksum = "d84b0a3c3739e220d94b3239fd69fb1f74bc36e16643423bd99de3b43c21bfbd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.8.6",
|
"ahash 0.8.10",
|
||||||
"atoi",
|
"atoi",
|
||||||
"bigdecimal",
|
"bigdecimal",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
@ -3231,15 +3176,6 @@ dependencies = [
|
|||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "strinto"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.48",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
@ -9,7 +9,7 @@ default-run = "miniweb"
|
|||||||
|
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [".", "entity", "migration"]
|
members = [".", "entity", "migration", "rear"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# https://github.com/rust-db/barrel/blob/master/guides/diesel-setup.md
|
# https://github.com/rust-db/barrel/blob/master/guides/diesel-setup.md
|
||||||
@ -17,8 +17,9 @@ use_barrel = ["barrel", "sqlformat"]
|
|||||||
default = ["use_barrel"]
|
default = ["use_barrel"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
strinto = { path = "./strinto" }
|
# strinto = { path = "./strinto" }
|
||||||
entity = { path = "./entity" }
|
entity = { path = "./entity" }
|
||||||
|
rear = { path = "./rear" }
|
||||||
sea-orm = { version = "0.12.10", features = [
|
sea-orm = { version = "0.12.10", features = [
|
||||||
"runtime-tokio-native-tls",
|
"runtime-tokio-native-tls",
|
||||||
"sqlx-postgres",
|
"sqlx-postgres",
|
||||||
@ -50,7 +51,6 @@ log = "0.4.20"
|
|||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
serde_json = "1.0.108"
|
serde_json = "1.0.108"
|
||||||
slug = "0.1.5"
|
slug = "0.1.5"
|
||||||
derive_builder = "0.13.0"
|
|
||||||
async-trait = "0.1.77"
|
async-trait = "0.1.77"
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
tower-http = { version = "0.5.1", features = ["trace"] }
|
tower-http = { version = "0.5.1", features = ["trace"] }
|
||||||
|
30
NOTES.md
30
NOTES.md
@ -263,3 +263,33 @@ Make axum log requests!
|
|||||||
|
|
||||||
There is another crate, that uses some MIME detection middleware for cache-control, called axum-cc, which could inspire middlewares here.
|
There is another crate, that uses some MIME detection middleware for cache-control, called axum-cc, which could inspire middlewares here.
|
||||||
|
|
||||||
|
## Changing Rust Toolchain on Windows
|
||||||
|
|
||||||
|
change to GNU:
|
||||||
|
|
||||||
|
`rustup toolchain install stable-x86_64-pc-windows-gnu`
|
||||||
|
|
||||||
|
change back to MSVC:
|
||||||
|
|
||||||
|
`rustup toolchain install stable-x86_64-pc-windows-msvc`
|
||||||
|
|
||||||
|
or:
|
||||||
|
|
||||||
|
`rustup toolchain install stable-msvc`
|
||||||
|
|
||||||
|
activate toolchain with `rustup default`
|
||||||
|
|
||||||
|
|
||||||
|
### udeps
|
||||||
|
|
||||||
|
https://github.com/est31/cargo-udeps
|
||||||
|
|
||||||
|
While compilation of this tool also works on Rust stable, it needs Rust nightly to actually run.
|
||||||
|
`cargo install cargo-udeps --locked`
|
||||||
|
|
||||||
|
|
||||||
|
cargo +nightly udeps
|
||||||
|
|
||||||
|
#### switching to nightly and back
|
||||||
|
|
||||||
|
rustup override set nightly
|
||||||
|
28
rear/Cargo.toml
Normal file
28
rear/Cargo.toml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
[package]
|
||||||
|
name = "rear"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "rear"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.75"
|
||||||
|
axum = "0.7"
|
||||||
|
minijinja = { version = "1.0.11", features = [
|
||||||
|
"loader",
|
||||||
|
"builtins",
|
||||||
|
"urlencode",
|
||||||
|
"deserialization",
|
||||||
|
] }
|
||||||
|
minijinja-autoreload = "1.0.8"
|
||||||
|
pulldown-cmark = "0.9.3"
|
||||||
|
serde = { version = "1.0.188", features = ["derive"] }
|
||||||
|
tokio = { version = "1.32.0", features = ["full"] }
|
||||||
|
log = "0.4.20"
|
||||||
|
serde_json = "1.0.108"
|
||||||
|
slug = "0.1.5"
|
||||||
|
async-trait = "0.1.77"
|
||||||
|
tracing = "0.1.40"
|
@ -50,12 +50,11 @@ mod dto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub mod repository {
|
pub mod repository {
|
||||||
use super::dto::{AdminApp, AdminModel};
|
use super::dto::AdminModel;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use derive_builder::Builder;
|
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::{collections::HashMap, vec::IntoIter};
|
use std::vec::IntoIter;
|
||||||
|
|
||||||
pub type RepositoryContext = AdminModel;
|
pub type RepositoryContext = AdminModel;
|
||||||
|
|
27
rear/src/admin/mod.rs
Normal file
27
rear/src/admin/mod.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
use axum::{routing::get, Router};
|
||||||
|
|
||||||
|
pub mod domain;
|
||||||
|
pub mod state;
|
||||||
|
pub mod views;
|
||||||
|
|
||||||
|
pub fn routes<S: state::AdminState + Clone + Send + Sync + 'static>() -> Router<S> {
|
||||||
|
Router::new()
|
||||||
|
.route("/", get(views::index::<S>).post(views::index_action::<S>))
|
||||||
|
.route("/app/:app", get(views::list_app::<S>))
|
||||||
|
.route(
|
||||||
|
"/app/:app/model/:model",
|
||||||
|
get(views::list_item_collection::<S>),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/app/:app/model/:model/add",
|
||||||
|
get(views::new_item::<S>).post(views::create_item::<S>),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/app/:app/model/:model/change/:id",
|
||||||
|
get(views::change_item::<S>).patch(views::update_item::<S>),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/app/:app/model/:model/detail/:id",
|
||||||
|
get(views::view_item_details::<S>),
|
||||||
|
)
|
||||||
|
}
|
@ -1,10 +1,16 @@
|
|||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use super::domain::{AdminApp, AdminModel, AdminModelConfig, AdminRepository};
|
use super::domain::{AdminApp, AdminModel, AdminModelConfig, AdminRepository};
|
||||||
|
use crate::service::templates::Templates;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub type AdminState = Arc<AdminRegistry>;
|
pub trait AdminState {
|
||||||
|
fn get_templates(&self) -> &Templates;
|
||||||
|
fn get_registry(&self) -> SharedRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type SharedRegistry = Arc<AdminRegistry>;
|
||||||
|
|
||||||
// main registry.
|
// main registry.
|
||||||
pub struct AdminRegistry {
|
pub struct AdminRegistry {
|
@ -1,5 +1,3 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use axum::extract::Path;
|
use axum::extract::Path;
|
||||||
use axum::http::HeaderMap;
|
use axum::http::HeaderMap;
|
||||||
use axum::Form;
|
use axum::Form;
|
||||||
@ -8,8 +6,7 @@ use log::info;
|
|||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::admin::domain::{AdminApp, AdminModel};
|
use crate::admin::domain::{AdminApp, AdminModel};
|
||||||
use crate::admin::state;
|
use crate::admin::state::AdminState;
|
||||||
use crate::service::templates;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::domain::{LookupKey, RepositoryInfo, RepositoryItem, RepositoryList};
|
use super::domain::{LookupKey, RepositoryInfo, RepositoryItem, RepositoryList};
|
||||||
@ -76,11 +73,12 @@ pub fn base_template(headers: &HeaderMap) -> Option<String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn index(
|
pub async fn index<S: AdminState + Clone + Send + Sync + 'static>(
|
||||||
templates: State<templates::Templates>,
|
admin: State<S>,
|
||||||
registry: State<Arc<state::AdminRegistry>>,
|
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
|
let templates = admin.get_templates();
|
||||||
|
let registry = admin.get_registry();
|
||||||
templates.render_html(
|
templates.render_html(
|
||||||
"admin/index.html",
|
"admin/index.html",
|
||||||
AdminContext {
|
AdminContext {
|
||||||
@ -92,26 +90,29 @@ pub async fn index(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Index Action is POST to the index site. We can anchor some general business code here.
|
// Index Action is POST to the index site. We can anchor some general business code here.
|
||||||
pub async fn index_action() -> impl IntoResponse {
|
pub async fn index_action<S: AdminState + Clone + Send + Sync + 'static>(
|
||||||
|
admin: State<S>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
"There is your answer!".to_owned()
|
"There is your answer!".to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn list_app(
|
pub async fn list_app<S: AdminState + Clone + Send + Sync + 'static>(
|
||||||
templates: State<templates::Templates>,
|
admin: State<S>,
|
||||||
Path(app_key): Path<String>,
|
Path(app_key): Path<String>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
|
let templates = admin.get_templates();
|
||||||
templates.render_html("admin/app_list.jinja", ())
|
templates.render_html("admin/app_list.jinja", ())
|
||||||
}
|
}
|
||||||
|
|
||||||
// List Items renders the entire list item page.
|
// List Items renders the entire list item page.
|
||||||
pub async fn list_item_collection(
|
pub async fn list_item_collection<S: AdminState + Clone + Send + Sync + 'static>(
|
||||||
templates: State<templates::Templates>,
|
admin: State<S>,
|
||||||
registry: State<Arc<state::AdminRegistry>>,
|
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path((app_key, model_key)): Path<(String, String)>,
|
Path((app_key, model_key)): Path<(String, String)>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
info!("list_item_collection {} for model {}", app_key, model_key);
|
info!("list_item_collection {} for model {}", app_key, model_key);
|
||||||
|
let templates = admin.get_templates();
|
||||||
|
let registry = admin.get_registry();
|
||||||
let context = if let Ok(repo) = registry.get_repository(&app_key, &model_key) {
|
let context = if let Ok(repo) = registry.get_repository(&app_key, &model_key) {
|
||||||
let repo = repo.lock().await;
|
let repo = repo.lock().await;
|
||||||
let admin_model = registry
|
let admin_model = registry
|
||||||
@ -136,19 +137,21 @@ 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.
|
// 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(
|
pub async fn item_collection_action<S: AdminState + Clone + Send + Sync + 'static>(
|
||||||
|
admin: State<S>,
|
||||||
Path((app_key, model_key)): Path<(String, String)>,
|
Path((app_key, model_key)): Path<(String, String)>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
"There is your answer!".to_owned()
|
"There is your answer!".to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Item Details shows one single dataset.
|
// Item Details shows one single dataset.
|
||||||
pub async fn view_item_details(
|
pub async fn view_item_details<S: AdminState + Clone + Send + Sync + 'static>(
|
||||||
templates: State<templates::Templates>,
|
admin: State<S>,
|
||||||
registry: State<Arc<state::AdminRegistry>>,
|
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path((app_key, model_key, id)): Path<(String, String, String)>,
|
Path((app_key, model_key, id)): Path<(String, String, String)>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
|
let templates = admin.get_templates();
|
||||||
|
let registry = admin.get_registry();
|
||||||
let context = if let Ok(repo) = registry.get_repository(&app_key, &model_key) {
|
let context = if let Ok(repo) = registry.get_repository(&app_key, &model_key) {
|
||||||
let repo = repo.lock().await;
|
let repo = repo.lock().await;
|
||||||
let admin_model = registry
|
let admin_model = registry
|
||||||
@ -174,12 +177,13 @@ pub async fn view_item_details(
|
|||||||
templates.render_html("admin/items/item_detail.jinja", context)
|
templates.render_html("admin/items/item_detail.jinja", context)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn new_item(
|
pub async fn new_item<S: AdminState + Clone + Send + Sync + 'static>(
|
||||||
templates: State<templates::Templates>,
|
admin: State<S>,
|
||||||
registry: State<Arc<state::AdminRegistry>>,
|
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path((app_key, model_key)): Path<(String, String)>,
|
Path((app_key, model_key)): Path<(String, String)>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
|
let templates = admin.get_templates();
|
||||||
|
let registry = admin.get_registry();
|
||||||
let context = if let Ok(repo) = registry.get_repository(&app_key, &model_key) {
|
let context = if let Ok(repo) = registry.get_repository(&app_key, &model_key) {
|
||||||
let repo = repo.lock().await;
|
let repo = repo.lock().await;
|
||||||
let admin_model = registry
|
let admin_model = registry
|
||||||
@ -203,13 +207,14 @@ pub async fn new_item(
|
|||||||
templates.render_html("admin/items/item_create.jinja", context)
|
templates.render_html("admin/items/item_create.jinja", context)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_item(
|
pub async fn create_item<S: AdminState + Clone + Send + Sync + 'static>(
|
||||||
templates: State<templates::Templates>,
|
admin: State<S>,
|
||||||
registry: State<Arc<state::AdminRegistry>>,
|
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path((app_key, model_key)): Path<(String, String)>,
|
Path((app_key, model_key)): Path<(String, String)>,
|
||||||
Form(form): Form<Value>,
|
Form(form): Form<Value>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
|
let templates = admin.get_templates();
|
||||||
|
let registry = admin.get_registry();
|
||||||
let context = if let Ok(repo) = registry.get_repository(&app_key, &model_key) {
|
let context = if let Ok(repo) = registry.get_repository(&app_key, &model_key) {
|
||||||
let mut repo = repo.lock().await;
|
let mut repo = repo.lock().await;
|
||||||
let admin_model = registry
|
let admin_model = registry
|
||||||
@ -240,12 +245,13 @@ pub async fn create_item(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Change is the GET version.
|
/// Change is the GET version.
|
||||||
pub async fn change_item(
|
pub async fn change_item<S: AdminState + Clone + Send + Sync + 'static>(
|
||||||
templates: State<templates::Templates>,
|
admin: State<S>,
|
||||||
registry: State<Arc<state::AdminRegistry>>,
|
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path((app_key, model_key, id)): Path<(String, String, String)>,
|
Path((app_key, model_key, id)): Path<(String, String, String)>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
|
let templates = admin.get_templates();
|
||||||
|
let registry = admin.get_registry();
|
||||||
let context = if let Ok(repo) = registry.get_repository(&app_key, &model_key) {
|
let context = if let Ok(repo) = registry.get_repository(&app_key, &model_key) {
|
||||||
let repo = repo.lock().await;
|
let repo = repo.lock().await;
|
||||||
let admin_model = registry
|
let admin_model = registry
|
||||||
@ -271,13 +277,14 @@ pub async fn change_item(
|
|||||||
templates.render_html("admin/items/item_change.jinja", context)
|
templates.render_html("admin/items/item_change.jinja", context)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_item(
|
pub async fn update_item<S: AdminState + Clone + Send + Sync + 'static>(
|
||||||
templates: State<templates::Templates>,
|
admin: State<S>,
|
||||||
registry: State<Arc<state::AdminRegistry>>,
|
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Path((app_key, model_key, id)): Path<(String, String, String)>,
|
Path((app_key, model_key, id)): Path<(String, String, String)>,
|
||||||
Form(form): Form<Value>,
|
Form(form): Form<Value>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
|
let templates = admin.get_templates();
|
||||||
|
let registry = admin.get_registry();
|
||||||
let context = if let Ok(repo) = registry.get_repository(&app_key, &model_key) {
|
let context = if let Ok(repo) = registry.get_repository(&app_key, &model_key) {
|
||||||
let mut repo = repo.lock().await;
|
let mut repo = repo.lock().await;
|
||||||
let admin_model = registry
|
let admin_model = registry
|
||||||
@ -307,13 +314,17 @@ pub async fn update_item(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Item Action allows running an action on one single dataset.
|
// Item Action allows running an action on one single dataset.
|
||||||
pub async fn item_action(
|
pub async fn item_action<S: AdminState + Clone + Send + Sync + 'static>(
|
||||||
|
admin: State<S>,
|
||||||
Path((app_key, model_key, model_id)): Path<(String, String, String)>,
|
Path((app_key, model_key, model_id)): Path<(String, String, String)>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
"There is your answer!".to_owned()
|
"There is your answer!".to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn debug_view(Path(data): Path<String>) -> impl IntoResponse {
|
pub async fn debug_view<S: AdminState + Clone + Send + Sync + 'static>(
|
||||||
|
admin: State<S>,
|
||||||
|
Path(data): Path<String>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
println!("debug: {}", data);
|
println!("debug: {}", data);
|
||||||
"Debug!".to_owned()
|
"Debug!".to_owned()
|
||||||
}
|
}
|
3
rear/src/lib.rs
Normal file
3
rear/src/lib.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub mod admin;
|
||||||
|
pub mod auth;
|
||||||
|
pub mod service;
|
15
rear/src/service/handlers.rs
Normal file
15
rear/src/service/handlers.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/* Default handlers */
|
||||||
|
use crate::service::templates::Templates;
|
||||||
|
use axum::{extract::State, http::StatusCode, response::IntoResponse};
|
||||||
|
|
||||||
|
// usage: .fallback(not_found_handler)
|
||||||
|
pub async fn not_found_handler(
|
||||||
|
State(templates): State<Templates>,
|
||||||
|
) -> axum::response::Result<impl IntoResponse> {
|
||||||
|
not_found(templates)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not_found(templates: Templates) -> axum::response::Result<impl IntoResponse> {
|
||||||
|
let body = templates.render_html("http404.html", ());
|
||||||
|
Ok((StatusCode::NOT_FOUND, body))
|
||||||
|
}
|
@ -1,10 +1,8 @@
|
|||||||
use crate::service::error::Error;
|
use crate::service::error::Error;
|
||||||
use axum::response::Html;
|
use axum::response::Html;
|
||||||
use minijinja::value::ValueKind;
|
|
||||||
use minijinja::{path_loader, Environment, Value};
|
use minijinja::{path_loader, Environment, Value};
|
||||||
use minijinja_autoreload::AutoReloader;
|
use minijinja_autoreload::AutoReloader;
|
||||||
use pulldown_cmark::Event;
|
use pulldown_cmark::Event;
|
||||||
use serde::Deserializer;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
@ -1,26 +0,0 @@
|
|||||||
use axum::{routing::get, routing::post, Router};
|
|
||||||
|
|
||||||
use crate::state::AppState;
|
|
||||||
|
|
||||||
pub mod domain;
|
|
||||||
pub mod state;
|
|
||||||
pub mod views;
|
|
||||||
|
|
||||||
pub fn routes() -> Router<AppState> {
|
|
||||||
Router::new()
|
|
||||||
.route("/", get(views::index).post(views::index_action))
|
|
||||||
.route("/app/:app", get(views::list_app))
|
|
||||||
.route("/app/:app/model/:model", get(views::list_item_collection))
|
|
||||||
.route(
|
|
||||||
"/app/:app/model/:model/add",
|
|
||||||
get(views::new_item).post(views::create_item),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/app/:app/model/:model/change/:id",
|
|
||||||
get(views::change_item).patch(views::update_item),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/app/:app/model/:model/detail/:id",
|
|
||||||
get(views::view_item_details),
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,37 +1,11 @@
|
|||||||
/* Default handlers */
|
|
||||||
use crate::service::templates::Templates;
|
|
||||||
use axum::{
|
use axum::{
|
||||||
body::Body,
|
body::Body,
|
||||||
extract::State,
|
|
||||||
http::{header, StatusCode, Uri},
|
http::{header, StatusCode, Uri},
|
||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use rust_embed::RustEmbed;
|
use rust_embed::RustEmbed;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
// usage: .route_service("/static/*file", static_handler.into_service())
|
|
||||||
pub async fn static_handler(uri: Uri) -> impl IntoResponse {
|
|
||||||
let mut path = uri.path().trim_start_matches('/').to_string();
|
|
||||||
|
|
||||||
if path.starts_with("static/") {
|
|
||||||
path = path.replace("static/", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
StaticFile::get(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// usage: .fallback(not_found_handler)
|
|
||||||
pub async fn not_found_handler(
|
|
||||||
State(templates): State<Templates>,
|
|
||||||
) -> axum::response::Result<impl IntoResponse> {
|
|
||||||
not_found(templates)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn not_found(templates: Templates) -> axum::response::Result<impl IntoResponse> {
|
|
||||||
let body = templates.render_html("http404.html", ());
|
|
||||||
Ok((StatusCode::NOT_FOUND, body))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct EmbeddedFile<E, T> {
|
pub struct EmbeddedFile<E, T> {
|
||||||
pub path: T,
|
pub path: T,
|
||||||
embed: PhantomData<E>,
|
embed: PhantomData<E>,
|
||||||
@ -71,3 +45,14 @@ where
|
|||||||
#[folder = "static"]
|
#[folder = "static"]
|
||||||
struct StaticDir;
|
struct StaticDir;
|
||||||
type StaticFile<T> = EmbeddedFile<StaticDir, T>;
|
type StaticFile<T> = EmbeddedFile<StaticDir, T>;
|
||||||
|
|
||||||
|
// usage: .route_service("/static/*file", static_handler.into_service())
|
||||||
|
pub async fn static_handler(uri: Uri) -> impl IntoResponse {
|
||||||
|
let mut path = uri.path().trim_start_matches('/').to_string();
|
||||||
|
|
||||||
|
if path.starts_with("static/") {
|
||||||
|
path = path.replace("static/", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
StaticFile::get(path)
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
use axum::{extract::State, response::IntoResponse, Form};
|
use axum::{extract::State, response::IntoResponse, Form};
|
||||||
|
|
||||||
use crate::service::templates;
|
use rear::service::templates;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
@ -1,4 +1 @@
|
|||||||
pub mod admin;
|
|
||||||
pub mod auth;
|
|
||||||
pub mod service;
|
|
||||||
pub mod state;
|
pub mod state;
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
mod admin;
|
|
||||||
mod admin_examples;
|
mod admin_examples;
|
||||||
|
mod embed;
|
||||||
mod howto;
|
mod howto;
|
||||||
mod service;
|
|
||||||
mod state;
|
mod state;
|
||||||
|
|
||||||
use crate::service::{handlers, templates};
|
|
||||||
use crate::state::AppState;
|
use crate::state::AppState;
|
||||||
use admin_examples::static_repository;
|
use admin_examples::static_repository;
|
||||||
use admin_examples::user_repository;
|
use admin_examples::user_repository;
|
||||||
@ -19,6 +17,8 @@ use axum::{
|
|||||||
};
|
};
|
||||||
use dotenvy::dotenv;
|
use dotenvy::dotenv;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use rear::admin;
|
||||||
|
use rear::service::{handlers, templates};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -68,7 +68,7 @@ async fn main() {
|
|||||||
//.merge(admin_router)
|
//.merge(admin_router)
|
||||||
.nest("/admin", admin::routes())
|
.nest("/admin", admin::routes())
|
||||||
.nest("/howto", howto::routes())
|
.nest("/howto", howto::routes())
|
||||||
.route_service("/static/*file", handlers::static_handler.into_service())
|
.route_service("/static/*file", embed::static_handler.into_service())
|
||||||
.fallback(handlers::not_found_handler)
|
.fallback(handlers::not_found_handler)
|
||||||
.with_state(state)
|
.with_state(state)
|
||||||
.layer(
|
.layer(
|
||||||
|
20
src/state.rs
20
src/state.rs
@ -1,12 +1,12 @@
|
|||||||
use axum::extract::FromRef;
|
use axum::extract::FromRef;
|
||||||
|
|
||||||
use crate::admin::state::AdminState;
|
use rear::admin::state::{AdminState, SharedRegistry};
|
||||||
use crate::service::templates;
|
use rear::service::templates;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub templates: templates::Templates,
|
pub templates: templates::Templates,
|
||||||
pub admin: AdminState,
|
pub admin: SharedRegistry,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromRef<AppState> for templates::Templates {
|
impl FromRef<AppState> for templates::Templates {
|
||||||
@ -15,8 +15,18 @@ impl FromRef<AppState> for templates::Templates {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromRef<AppState> for AdminState {
|
impl FromRef<AppState> for SharedRegistry {
|
||||||
fn from_ref(app_state: &AppState) -> AdminState {
|
fn from_ref(app_state: &AppState) -> SharedRegistry {
|
||||||
app_state.admin.clone()
|
app_state.admin.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AdminState for AppState {
|
||||||
|
fn get_templates(&self) -> &templates::Templates {
|
||||||
|
&self.templates
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_registry(&self) -> SharedRegistry {
|
||||||
|
self.admin.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user