mod command;
mod controller;
pub mod data;
pub mod entity;
pub mod handler;
pub mod helper;
pub mod middleware;
pub mod service;
pub mod state;
use axum::routing::get;
use command::AdminCreateCommand;
use controller::logout::get_logout;
use middleware::{toast_middleware, AdminAuthMiddleware};
use quokka::{
handler::html::{DataTemplate, FormTemplate},
state::{Database, FromState, ProvideState, ProvideStateRef, State, Templating},
};
use rust_embed::Embed as RustEmbed;
use service::{
page_loader::{
AdminDashboardPageLoader, AdminLoginPageLoader, AdminNavigationGroup, AdminPageLoader,
AdminUiElementsPageLoader, NavigationItem,
},
DbAuthProvider, DbLoginProvider,
};
use state::AdminState;
#[derive(RustEmbed)]
#[folder = "web/templates"]
#[include = "*.hbs"]
struct Templates;
#[derive(RustEmbed)]
#[folder = "web/templates"]
#[include = "*.scss"]
struct Styles;
#[derive(RustEmbed)]
#[folder = "web/templates"]
#[include = "*.js"]
struct JavaScript;
#[derive(RustEmbed)]
#[folder = "web/assets"]
struct Resources;
pub struct AdminBundle {}
impl<S: State + 'static> quokka::bundle::Pouch<S> for AdminBundle
where
S: ProvideState<Templating>,
S: ProvideState<Database>,
S: ProvideStateRef<AdminState<S>>,
{
fn from_config(_: &quokka::config::Config) -> quokka::Result<Self>
where
Self: Sized,
{
Ok(AdminBundle {})
}
fn configure_commands(&self, commands: &mut quokka::state::Commands<S>) -> quokka::Result<()> {
commands.register_command::<AdminCreateCommand>();
Ok(())
}
fn configure_state(&mut self, state: &mut S) -> quokka::Result<()> {
let login_provider = DbLoginProvider::from_state(state);
let auth_provider = DbAuthProvider::from_state(state);
let admin: &mut AdminState<S> = state.provide_mut();
admin.add_login_provider(login_provider);
admin.add_auth_provider(auth_provider);
admin.add_navigation(
AdminNavigationGroup::new("Quokka Admin")
.order(-100)
.add(NavigationItem::new("dashboard", "Dashboard").link("/admin")),
);
#[cfg(debug_assertions)]
admin.add_navigation(
AdminNavigationGroup::new("Quokka Admin").add(
NavigationItem::new("", "Debugging")
.nest(NavigationItem::new("colors", "Color Overview").link("/admin/colors"))
.nest(
NavigationItem::new("ui_elements", "UI Elements Overview")
.link("/admin/ui-elements"),
),
),
);
Ok(())
}
fn configure_styles(&mut self, styles: &mut quokka::state::Styling) -> quokka::Result<()> {
styles.register_embedded_styles::<Styles>();
styles.add_merged_style_group("quokka-admin", "quokka-admin/style.scss");
Ok(())
}
fn configure_scripts(&mut self, scripts: &mut quokka::state::Scripting) -> quokka::Result<()> {
scripts.register_embedded_scripts::<JavaScript>();
scripts.add_merged_script_group(
"quokka-admin",
"/resources/quokka-admin/scripts/htmx.min.js",
);
scripts.add_merged_script_group("quokka-admin", "/scripting/quokka-admin/script.js");
Ok(())
}
fn configure_templates(
&mut self,
templates: &mut quokka::state::Templating,
) -> quokka::Result<()> {
templates.register_embedded_templates_aliased_partials::<Templates>()
}
fn configure_resources(
&mut self,
resources: &mut quokka::state::Resources,
) -> quokka::Result<()> {
resources.register_embedded_resources::<Resources>();
Ok(())
}
fn get_router(&mut self, state: &S) -> axum::Router<S> {
let admin: AdminState<S> = state.provide();
let mut router = admin.register_routes(axum::Router::new());
router = router
.route(
"/admin",
DataTemplate::<AdminDashboardPageLoader, S>::new(
"quokka-admin/page/dashboard/dashboard.html.hbs",
)
.into(),
)
.layer(AdminAuthMiddleware::from_state(state))
.route(
"/admin/login",
FormTemplate::<AdminLoginPageLoader, S>::new(
"quokka-admin/page/login/login.html.hbs",
)
.into(),
)
.route("/admin/logout", get(get_logout));
#[cfg(debug_assertions)]
{
router = router
.route(
"/admin/colors",
DataTemplate::<AdminPageLoader, S>::new(
"quokka-admin/page/colors/colors.html.hbs",
)
.into(),
)
.route(
"/admin/ui-elements",
DataTemplate::<AdminUiElementsPageLoader, S>::new(
"quokka-admin/page/ui_elements/ui_elements.html.hbs",
)
.into(),
)
.route(
"/admin/error",
DataTemplate::<AdminPageLoader, S>::new("/dev/null").into(),
);
}
router.layer(axum::middleware::from_fn(toast_middleware))
}
fn get_migrations(&self) -> Option<sqlx::migrate::Migrator> {
Some(sqlx::migrate!())
}
}