diff --git a/src/components/icon.rs b/src/components/icon.rs index 0e46079..5d28905 100644 --- a/src/components/icon.rs +++ b/src/components/icon.rs @@ -315,6 +315,22 @@ pub fn IconDocumentText( } } +#[component] +pub fn IconDocumentMagnifyingGlass( + #[prop(optional)] size: Option, + #[prop(attrs)] attrs: Vec<(&'static str, Attribute)>, +) -> impl IntoView { + view! { + + + + } +} + #[component] pub fn IconInfo( #[prop(optional)] size: Option, diff --git a/src/core/mod.rs b/src/core/mod.rs index 8464fe4..39c6d9e 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -92,6 +92,7 @@ pub enum Permission { PurgeDataStore, PurgeLookupStore, PurgeAccount, + FtsReindex, Undelete, DkimSignatureCreate, DkimSignatureGet, diff --git a/src/core/oauth.rs b/src/core/oauth.rs index eef50a5..e309b6b 100644 --- a/src/core/oauth.rs +++ b/src/core/oauth.rs @@ -289,6 +289,7 @@ impl OAuthCodeResponse { Permission::PurgeDataStore, Permission::PurgeLookupStore, Permission::PurgeAccount, + Permission::FtsReindex, Permission::Undelete, Permission::DkimSignatureCreate, Permission::DkimSignatureGet, diff --git a/src/pages/directory/mod.rs b/src/pages/directory/mod.rs index 8f7fda3..1ee1cf1 100644 --- a/src/pages/directory/mod.rs +++ b/src/pages/directory/mod.rs @@ -698,6 +698,10 @@ pub static PERMISSIONS: &[(&str, &str)] = &[ "purge-account", "Completely remove an account and all associated data", ), + ( + "fts-reindex", + "Rebuild the full-text search index for accounts", + ), ("undelete", "Restore deleted items"), ( "dkim-signature-create", diff --git a/src/pages/manage/maintenance.rs b/src/pages/manage/maintenance.rs index a9a7f40..4b62b98 100644 --- a/src/pages/manage/maintenance.rs +++ b/src/pages/manage/maintenance.rs @@ -9,12 +9,16 @@ use leptos_router::use_navigate; use crate::{ components::{ - icon::{IconCheckCircle, IconComputerDesktop, IconPower, IconRefresh, IconShieldCheck}, + icon::{ + IconCheckCircle, IconComputerDesktop, IconDocumentMagnifyingGlass, IconPower, + IconRefresh, IconShieldCheck, + }, messages::alert::{use_alerts, Alert, Alerts}, }, core::{ http::{self, HttpRequest}, oauth::use_authorization, + Permission, }, pages::config::ReloadSettings, }; @@ -26,6 +30,7 @@ struct Action { icon: &'static str, url: &'static str, success_message: &'static str, + permission: Permission, } const ACTIONS: &[Action] = &[ @@ -35,6 +40,7 @@ const ACTIONS: &[Action] = &[ icon: "refresh", url: "/api/reload", success_message: "Successfully reloaded configuration", + permission: Permission::SettingsReload, }, Action { title: "Validate configuration", @@ -42,6 +48,7 @@ const ACTIONS: &[Action] = &[ icon: "check_circle", url: "/api/reload?dry-run=true", success_message: "Configuration is valid", + permission: Permission::SettingsReload, }, Action { title: "Restart server", @@ -49,6 +56,7 @@ const ACTIONS: &[Action] = &[ icon: "power", url: "/api/restart", success_message: "Restarting server, try reloading this page in a few seconds.", + permission: Permission::Restart, }, Action { title: "Update SPAM rules", @@ -56,6 +64,7 @@ const ACTIONS: &[Action] = &[ icon: "shield_check", url: "/api/update/spam-filter", success_message: "Successfully updated SPAM rules to the latest version", + permission: Permission::UpdateSpamFilter, }, Action { title: "Update Webadmin", @@ -63,6 +72,15 @@ const ACTIONS: &[Action] = &[ icon: "computer_desktop", url: "/api/update/webadmin", success_message: "Successfully updated the web admin to the latest version", + permission: Permission::UpdateWebadmin, + }, + Action { + title: "Reindex FTS", + description: "Rebuilds the full-text search index for all accounts. This may take some time.", + icon: "document_magnifying_glass", + url: "/api/store/reindex", + success_message: "Successfully requested FTS reindex", + permission: Permission::FtsReindex, }, ]; @@ -124,7 +142,8 @@ pub fn Maintenance() -> impl IntoView { } }); - let actions = ACTIONS.iter().enumerate().map(|(idx, action)| { + let permissions = auth.get_untracked().permissions().clone(); + let actions = ACTIONS.iter().enumerate().filter_map(|(idx, action)| { let icon_class = "mt-1 flex-shrink-0 size-5 text-gray-800 dark:text-gray-200"; let icon = match action.icon { "refresh" => view! { }, @@ -132,10 +151,11 @@ pub fn Maintenance() -> impl IntoView { "power" => view! { }, "shield_check" => view! { }, "computer_desktop" => view! { }, + "document_magnifying_glass" => view! { }, _ => unreachable!("No icon specified"), }; - view! { + permissions.has_access(action.permission).then(|| view! { impl IntoView { - } + }) }).collect_view();