1mod csrf;
2mod helpers;
3mod auth;
4#[cfg(feature = "seaorm")]
5mod entity;
6#[cfg(feature = "seaorm")]
7mod users;
8#[cfg(feature = "seaorm")]
9mod roles;
10
11use crate::{app::AdminApp, middleware::require_auth};
12use axum::{
13 http::header,
14 middleware,
15 response::{IntoResponse, Redirect},
16 routing::{delete, get, post},
17 Router,
18};
19use axum::extract::DefaultBodyLimit;
20use tower_cookies::CookieManagerLayer;
21use axum::extract::Extension;
22
23async fn serve_htmx() -> impl IntoResponse {
24 (
25 [(header::CONTENT_TYPE, "application/javascript")],
26 include_str!("../../static/htmx.min.js"),
27 )
28}
29
30async fn serve_alpine() -> impl IntoResponse {
31 (
32 [(header::CONTENT_TYPE, "application/javascript")],
33 include_str!("../../static/alpine.min.js"),
34 )
35}
36
37async fn serve_admin_css() -> impl IntoResponse {
38 (
39 [(header::CONTENT_TYPE, "text/css")],
40 include_str!("../../static/admin.css"),
41 )
42}
43
44impl AdminApp {
45 pub async fn into_router(self) -> Router {
46 #[cfg(feature = "seaorm")]
47 let entity_names: Vec<String> = self
48 .entities
49 .iter()
50 .map(|e| e.entity_name.clone())
51 .collect();
52
53 #[cfg(feature = "seaorm")]
54 if let Some(ref seaorm_auth) = self.seaorm_auth {
55 seaorm_auth
56 .seed_roles(&entity_names)
57 .await
58 .expect("failed to seed RBAC roles");
59 }
60
61 let (auth, state, upload_limit) = self.into_state();
62
63 #[cfg(feature = "seaorm")]
64 let protected = {
65 use axum::extract::Path;
66 Router::new()
67 .route("/admin", get(|| async { Redirect::permanent("/admin/") }))
68 .route("/admin/", get(entity::admin_home))
69 .route("/admin/logout", get(auth::logout))
70 .route("/admin/change-password", get(auth::change_password_page))
71 .route("/admin/change-password", post(auth::change_password_submit))
72 .route("/admin/users/", get(users::user_list))
73 .route("/admin/users/new", get(users::user_create_form))
74 .route("/admin/users/new", post(users::user_create_submit))
75 .route("/admin/users/:id/", get(users::user_edit_form))
76 .route("/admin/users/:id/", post(users::user_edit_submit))
77 .route("/admin/users/:id/delete", delete(users::user_delete))
78 .route("/admin/roles/", get(roles::role_list))
79 .route("/admin/roles/new", get(roles::role_create_form))
80 .route("/admin/roles/new", post(roles::role_create_submit))
81 .route("/admin/roles/:role/", get(roles::role_edit_form))
82 .route("/admin/roles/:role/", post(roles::role_edit_submit))
83 .route("/admin/roles/:role/delete", delete(roles::role_delete))
84 .route("/admin/:entity", get(|Path(e): Path<String>| async move {
85 Redirect::permanent(&format!("/admin/{}/", e))
86 }))
87 .route("/admin/:entity/", get(entity::entity_list))
88 .route("/admin/:entity/new", get(entity::entity_create_form))
89 .route("/admin/:entity/new", post(entity::entity_create_submit))
90 .route("/admin/:entity/:id/", get(entity::entity_edit_form))
91 .route("/admin/:entity/:id/", post(entity::entity_edit_submit))
92 .route("/admin/:entity/:id/delete", delete(entity::entity_delete))
93 .route("/admin/:entity/action/:action_name", post(entity::entity_action))
94 .route_layer(middleware::from_fn(require_auth))
95 .layer(DefaultBodyLimit::max(upload_limit))
96 };
97
98 #[cfg(not(feature = "seaorm"))]
99 let protected = {
100 Router::new()
101 .route("/admin", get(|| async { Redirect::permanent("/admin/") }))
102 .route("/admin/", get(|| async { axum::response::Html("<h1>axum-admin</h1><p>Enable the <code>seaorm</code> feature to use the admin dashboard.</p>") }))
103 .route("/admin/logout", get(auth::logout))
104 .route("/admin/change-password", get(auth::change_password_page))
105 .route("/admin/change-password", post(auth::change_password_submit))
106 .route_layer(middleware::from_fn(require_auth))
107 .layer(DefaultBodyLimit::max(upload_limit))
108 };
109
110 Router::new()
111 .route("/admin/login", get(auth::login_page))
112 .route("/admin/login", post(auth::login_submit))
113 .route("/admin/_static/htmx.min.js", get(serve_htmx))
114 .route("/admin/_static/alpine.min.js", get(serve_alpine))
115 .route("/admin/_static/admin.css", get(serve_admin_css))
116 .merge(protected)
117 .layer(Extension(state))
118 .layer(Extension(auth))
119 .layer(CookieManagerLayer::new())
120 }
121}