Skip to main content

actix_web_admin/
site.rs

1use crate::auth::UserStore;
2use crate::handlers;
3use crate::registry::{AdminRegistry, SharedRegistry};
4use crate::resource::{AdminPrefix, AdminTitle};
5use actix_web::web;
6use std::sync::Arc;
7
8/// Main entry point for configuring the admin site.
9pub struct AdminSite {
10    prefix: String,
11    title: String,
12    user_store: Option<Arc<dyn UserStore>>,
13}
14
15impl AdminSite {
16    /// Create a new admin site with the given URL prefix.
17    pub fn new(prefix: &str) -> Self {
18        let prefix = prefix.trim_start_matches('/');
19        Self {
20            prefix: format!("/{}", prefix),
21            title: "Admin".to_string(),
22            user_store: None,
23        }
24    }
25
26    /// Set the title of the admin site.
27    pub fn title(mut self, t: &str) -> Self {
28        self.title = t.to_string();
29        self
30    }
31
32    /// Provide a UserStore for authentication (required for login to work).
33    pub fn with_user_store(mut self, store: Arc<dyn UserStore>) -> Self {
34        self.user_store = Some(store);
35        self
36    }
37
38    /// Mount the admin site onto an Actix-Web application.
39    pub fn mount(self, cfg: &mut web::ServiceConfig, registry: AdminRegistry) {
40        let shared_registry: SharedRegistry = Arc::new(registry);
41        let prefix = self.prefix.clone();
42        let title = self.title.clone();
43
44        cfg.app_data(web::Data::new(shared_registry.clone()));
45        cfg.app_data(web::Data::new(AdminTitle(title)));
46        cfg.app_data(web::Data::new(AdminPrefix(prefix.trim_start_matches('/').to_string())));
47
48        if let Some(ref store) = self.user_store {
49            cfg.app_data(web::Data::new(store.clone()));
50        } else {
51            // No UserStore provided — inject a no-op store that rejects all logins
52            log::warn!(
53                "No UserStore configured for admin site at {}. \
54                 Login will reject all attempts. \
55                 Use AdminSite::with_user_store() to provide one.",
56                prefix
57            );
58            let noop: Arc<dyn UserStore> = Arc::new(NoopUserStore);
59            cfg.app_data(web::Data::new(noop));
60        }
61
62        cfg.service(
63            web::scope(&prefix)
64                .wrap(crate::auth::RequireAuth::new(&prefix))
65                .route("", web::get().to(handlers::dashboard::index))
66                .route("/", web::get().to(handlers::dashboard::index))
67                .route("/login", web::get().to(handlers::auth::login_page))
68                .route("/login", web::post().to(handlers::auth::login))
69                .route("/logout", web::get().to(handlers::auth::logout))
70                .service(
71                    web::scope("/{slug}")
72                        .route("", web::get().to(handlers::resource::list))
73                        .route("/", web::get().to(handlers::resource::list))
74                        .route("/new", web::get().to(handlers::resource::new))
75                        .route("/new", web::post().to(handlers::resource::create))
76                        .route("/{id}", web::get().to(handlers::resource::edit))
77                        .route("/{id}", web::post().to(handlers::resource::update))
78                        .route("/{id}/delete", web::post().to(handlers::resource::delete)),
79                ),
80        );
81    }
82}
83
84struct NoopUserStore;
85
86#[async_trait::async_trait]
87impl UserStore for NoopUserStore {
88    async fn find_by_username(&self, _: &str) -> Result<Option<crate::auth::User>, crate::auth::AuthError> {
89        Ok(None)
90    }
91
92    async fn create_user(
93        &self,
94        _: &str,
95        _: &str,
96        _: &str,
97        _: &str,
98        _: bool,
99    ) -> Result<crate::auth::User, crate::auth::AuthError> {
100        Err(crate::auth::AuthError::Storage("NoopUserStore: cannot create users".to_string()))
101    }
102
103    async fn delete_user(&self, _: &str) -> Result<(), crate::auth::AuthError> {
104        Err(crate::auth::AuthError::Storage("NoopUserStore: cannot delete users".to_string()))
105    }
106}