Skip to main content

quokka_admin/
lib.rs

1mod command;
2mod controller;
3pub mod data;
4pub mod entity;
5pub mod handler;
6pub mod helper;
7pub mod middleware;
8pub mod service;
9pub mod state;
10
11use axum::routing::get;
12use command::AdminCreateCommand;
13use controller::logout::get_logout;
14use middleware::{toast_middleware, AdminAuthMiddleware};
15use quokka::{
16    handler::html::{DataTemplate, FormTemplate},
17    state::{Database, FromState, ProvideState, ProvideStateRef, State, Templating},
18};
19use rust_embed::Embed as RustEmbed;
20use service::{
21    page_loader::{
22        AdminDashboardPageLoader, AdminLoginPageLoader, AdminNavigationGroup, AdminPageLoader,
23        AdminUiElementsPageLoader, NavigationItem,
24    },
25    DbAuthProvider, DbLoginProvider,
26};
27use state::AdminState;
28
29#[derive(RustEmbed)]
30#[folder = "web/templates"]
31#[include = "*.hbs"]
32struct Templates;
33
34#[derive(RustEmbed)]
35#[folder = "web/templates"]
36#[include = "*.scss"]
37struct Styles;
38
39#[derive(RustEmbed)]
40#[folder = "web/templates"]
41#[include = "*.js"]
42struct JavaScript;
43
44#[derive(RustEmbed)]
45#[folder = "web/assets"]
46struct Resources;
47
48pub struct AdminBundle {}
49
50/// This is provides the minimum to get a Bundle started.
51///
52/// See the [quokka::bundle::Pouch] for available setup functions.
53impl<S: State + 'static> quokka::bundle::Pouch<S> for AdminBundle
54where
55    S: ProvideState<Templating>,
56    S: ProvideState<Database>,
57    S: ProvideStateRef<AdminState<S>>,
58{
59    /// Find your config module from here
60    fn from_config(_: &quokka::config::Config) -> quokka::Result<Self>
61    where
62        Self: Sized,
63    {
64        Ok(AdminBundle {})
65    }
66
67    fn configure_commands(&self, commands: &mut quokka::state::Commands<S>) -> quokka::Result<()> {
68        commands.register_command::<AdminCreateCommand>();
69
70        Ok(())
71    }
72
73    fn configure_state(&mut self, state: &mut S) -> quokka::Result<()> {
74        let login_provider = DbLoginProvider::from_state(state);
75        let auth_provider = DbAuthProvider::from_state(state);
76        let admin: &mut AdminState<S> = state.provide_mut();
77
78        admin.add_login_provider(login_provider);
79        admin.add_auth_provider(auth_provider);
80
81        admin.add_navigation(
82            AdminNavigationGroup::new("Quokka Admin")
83                .order(-100)
84                .add(NavigationItem::new("dashboard", "Dashboard").link("/admin")),
85        );
86
87        #[cfg(debug_assertions)]
88        admin.add_navigation(
89            AdminNavigationGroup::new("Quokka Admin").add(
90                NavigationItem::new("", "Debugging")
91                    .nest(NavigationItem::new("colors", "Color Overview").link("/admin/colors"))
92                    .nest(
93                        NavigationItem::new("ui_elements", "UI Elements Overview")
94                            .link("/admin/ui-elements"),
95                    ),
96            ),
97        );
98
99        Ok(())
100    }
101
102    fn configure_styles(&mut self, styles: &mut quokka::state::Styling) -> quokka::Result<()> {
103        styles.register_embedded_styles::<Styles>();
104        styles.add_merged_style_group("quokka-admin", "quokka-admin/style.scss");
105
106        Ok(())
107    }
108
109    fn configure_scripts(&mut self, scripts: &mut quokka::state::Scripting) -> quokka::Result<()> {
110        scripts.register_embedded_scripts::<JavaScript>();
111        scripts.add_merged_script_group(
112            "quokka-admin",
113            "/resources/quokka-admin/scripts/htmx.min.js",
114        );
115        scripts.add_merged_script_group("quokka-admin", "/scripting/quokka-admin/script.js");
116
117        Ok(())
118    }
119
120    fn configure_templates(
121        &mut self,
122        templates: &mut quokka::state::Templating,
123    ) -> quokka::Result<()> {
124        templates.register_embedded_templates_aliased_partials::<Templates>()
125    }
126
127    fn configure_resources(
128        &mut self,
129        resources: &mut quokka::state::Resources,
130    ) -> quokka::Result<()> {
131        resources.register_embedded_resources::<Resources>();
132
133        Ok(())
134    }
135
136    fn get_router(&mut self, state: &S) -> axum::Router<S> {
137        let admin: AdminState<S> = state.provide();
138        let mut router = admin.register_routes(axum::Router::new());
139
140        router = router
141            .route(
142                "/admin",
143                DataTemplate::<AdminDashboardPageLoader, S>::new(
144                    "quokka-admin/page/dashboard/dashboard.html.hbs",
145                )
146                .into(),
147            )
148            .layer(AdminAuthMiddleware::from_state(state))
149            .route(
150                "/admin/login",
151                FormTemplate::<AdminLoginPageLoader, S>::new(
152                    "quokka-admin/page/login/login.html.hbs",
153                )
154                .into(),
155            )
156            .route("/admin/logout", get(get_logout));
157
158        #[cfg(debug_assertions)]
159        {
160            router = router
161                .route(
162                    "/admin/colors",
163                    DataTemplate::<AdminPageLoader, S>::new(
164                        "quokka-admin/page/colors/colors.html.hbs",
165                    )
166                    .into(),
167                )
168                .route(
169                    "/admin/ui-elements",
170                    DataTemplate::<AdminUiElementsPageLoader, S>::new(
171                        "quokka-admin/page/ui_elements/ui_elements.html.hbs",
172                    )
173                    .into(),
174                )
175                .route(
176                    "/admin/error",
177                    DataTemplate::<AdminPageLoader, S>::new("/dev/null").into(),
178                );
179        }
180
181        router.layer(axum::middleware::from_fn(toast_middleware))
182    }
183
184    fn get_migrations(&self) -> Option<sqlx::migrate::Migrator> {
185        Some(sqlx::migrate!())
186    }
187}