mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2026-01-08 02:33:53 -05:00
Cached config operations
This commit is contained in:
@@ -12,6 +12,7 @@ use serde_json::Value;
|
||||
use crate::{
|
||||
api::{core::now, ApiResult, EmptyResult},
|
||||
auth::decode_file_download,
|
||||
config::CachedConfigOperation,
|
||||
db::models::{AttachmentId, CipherId},
|
||||
error::Error,
|
||||
util::Cached,
|
||||
@@ -52,19 +53,18 @@ fn not_found() -> ApiResult<Html<String>> {
|
||||
Ok(Html(text))
|
||||
}
|
||||
|
||||
#[get("/css/vaultwarden.css")]
|
||||
fn vaultwarden_css() -> Cached<Css<String>> {
|
||||
static VAULTWARDEN_CSS_CACHE: CachedConfigOperation<String> = CachedConfigOperation::new(|config| {
|
||||
let css_options = json!({
|
||||
"emergency_access_allowed": CONFIG.emergency_access_allowed(),
|
||||
"emergency_access_allowed": config.emergency_access_allowed(),
|
||||
"load_user_scss": true,
|
||||
"mail_2fa_enabled": CONFIG._enable_email_2fa(),
|
||||
"mail_enabled": CONFIG.mail_enabled(),
|
||||
"sends_allowed": CONFIG.sends_allowed(),
|
||||
"signup_disabled": CONFIG.is_signup_disabled(),
|
||||
"sso_enabled": CONFIG.sso_enabled(),
|
||||
"sso_only": CONFIG.sso_enabled() && CONFIG.sso_only(),
|
||||
"yubico_enabled": CONFIG._enable_yubico() && CONFIG.yubico_client_id().is_some() && CONFIG.yubico_secret_key().is_some(),
|
||||
"webauthn_2fa_supported": CONFIG.is_webauthn_2fa_supported(),
|
||||
"mail_2fa_enabled": config._enable_email_2fa(),
|
||||
"mail_enabled": config.mail_enabled(),
|
||||
"sends_allowed": config.sends_allowed(),
|
||||
"signup_disabled": config.is_signup_disabled(),
|
||||
"sso_enabled": config.sso_enabled(),
|
||||
"sso_only": config.sso_enabled() && config.sso_only(),
|
||||
"yubico_enabled": config._enable_yubico() && config.yubico_client_id().is_some() && config.yubico_secret_key().is_some(),
|
||||
"webauthn_2fa_supported": config.is_webauthn_2fa_supported(),
|
||||
});
|
||||
|
||||
let scss = match CONFIG.render_template("scss/vaultwarden.scss", &css_options) {
|
||||
@@ -78,7 +78,7 @@ fn vaultwarden_css() -> Cached<Css<String>> {
|
||||
}
|
||||
};
|
||||
|
||||
let css = match grass_compiler::from_string(
|
||||
match grass_compiler::from_string(
|
||||
scss,
|
||||
&grass_compiler::Options::default().style(grass_compiler::OutputStyle::Compressed),
|
||||
) {
|
||||
@@ -97,10 +97,12 @@ fn vaultwarden_css() -> Cached<Css<String>> {
|
||||
)
|
||||
.expect("SCSS to compile")
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// Cache for one day should be enough and not too much
|
||||
Cached::ttl(Css(css), 86_400, false)
|
||||
#[get("/css/vaultwarden.css")]
|
||||
fn vaultwarden_css() -> Css<String> {
|
||||
Css(CONFIG.cached_operation(&VAULTWARDEN_CSS_CACHE))
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::{
|
||||
fmt,
|
||||
process::exit,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||
LazyLock, RwLock,
|
||||
},
|
||||
};
|
||||
@@ -103,6 +103,7 @@ macro_rules! make_config {
|
||||
|
||||
struct Inner {
|
||||
rocket_shutdown_handle: Option<rocket::Shutdown>,
|
||||
revision: usize,
|
||||
|
||||
templates: Handlebars<'static>,
|
||||
config: ConfigItems,
|
||||
@@ -322,7 +323,7 @@ macro_rules! make_config {
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
struct ConfigItems { $($( $name: make_config! {@type $ty, $none_action}, )+)+ }
|
||||
struct ConfigItems { $($( pub $name: make_config! {@type $ty, $none_action}, )+)+ }
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ElementDoc {
|
||||
@@ -1467,6 +1468,23 @@ pub enum PathType {
|
||||
RsaKey,
|
||||
}
|
||||
|
||||
pub struct CachedConfigOperation<T: Clone> {
|
||||
generator: fn(&Config) -> T,
|
||||
value_cache: RwLock<Option<T>>,
|
||||
revision: AtomicUsize,
|
||||
}
|
||||
|
||||
impl<T: Clone> CachedConfigOperation<T> {
|
||||
#[allow(private_interfaces)]
|
||||
pub const fn new(generator: fn(&Config) -> T) -> Self {
|
||||
CachedConfigOperation {
|
||||
generator,
|
||||
value_cache: RwLock::new(None),
|
||||
revision: AtomicUsize::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub async fn load() -> Result<Self, Error> {
|
||||
// Loading from env and file
|
||||
@@ -1486,6 +1504,7 @@ impl Config {
|
||||
Ok(Config {
|
||||
inner: RwLock::new(Inner {
|
||||
rocket_shutdown_handle: None,
|
||||
revision: 1,
|
||||
templates: load_templates(&config.templates_folder),
|
||||
config,
|
||||
_env,
|
||||
@@ -1524,6 +1543,7 @@ impl Config {
|
||||
writer.config = config;
|
||||
writer._usr = builder;
|
||||
writer._overrides = overrides;
|
||||
writer.revision += 1;
|
||||
}
|
||||
|
||||
//Save to file
|
||||
@@ -1542,6 +1562,51 @@ impl Config {
|
||||
self.update_config(builder, false).await
|
||||
}
|
||||
|
||||
pub async fn delete_user_config(&self) -> Result<(), Error> {
|
||||
let operator = opendal_operator_for_path(&CONFIG_FILE_PARENT_DIR)?;
|
||||
operator.delete(&CONFIG_FILENAME).await?;
|
||||
|
||||
// Empty user config
|
||||
let usr = ConfigBuilder::default();
|
||||
|
||||
// Config now is env + defaults
|
||||
let config = {
|
||||
let env = &self.inner.read().unwrap()._env;
|
||||
env.build()
|
||||
};
|
||||
|
||||
// Save configs
|
||||
{
|
||||
let mut writer = self.inner.write().unwrap();
|
||||
writer.config = config;
|
||||
writer._usr = usr;
|
||||
writer._overrides = Vec::new();
|
||||
writer.revision += 1;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cached_operation<T: Clone>(&self, operation: &CachedConfigOperation<T>) -> T {
|
||||
let config_revision = self.inner.read().unwrap().revision;
|
||||
let cache_revision = operation.revision.load(Ordering::Relaxed);
|
||||
|
||||
// If the current revision matches the cached revision, return the cached value
|
||||
if cache_revision == config_revision {
|
||||
let reader = operation.value_cache.read().unwrap();
|
||||
return reader.as_ref().unwrap().clone();
|
||||
}
|
||||
|
||||
// Otherwise, compute the value, update the cache and revision, and return the new value
|
||||
let value = (operation.generator)(&CONFIG);
|
||||
{
|
||||
let mut writer = operation.value_cache.write().unwrap();
|
||||
*writer = Some(value.clone());
|
||||
operation.revision.store(config_revision, Ordering::Relaxed);
|
||||
}
|
||||
value
|
||||
}
|
||||
|
||||
/// Tests whether an email's domain is allowed. A domain is allowed if it
|
||||
/// is in signups_domains_whitelist, or if no whitelist is set (so there
|
||||
/// are no domain restrictions in effect).
|
||||
@@ -1591,33 +1656,10 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn delete_user_config(&self) -> Result<(), Error> {
|
||||
let operator = opendal_operator_for_path(&CONFIG_FILE_PARENT_DIR)?;
|
||||
operator.delete(&CONFIG_FILENAME).await?;
|
||||
|
||||
// Empty user config
|
||||
let usr = ConfigBuilder::default();
|
||||
|
||||
// Config now is env + defaults
|
||||
let config = {
|
||||
let env = &self.inner.read().unwrap()._env;
|
||||
env.build()
|
||||
};
|
||||
|
||||
// Save configs
|
||||
{
|
||||
let mut writer = self.inner.write().unwrap();
|
||||
writer.config = config;
|
||||
writer._usr = usr;
|
||||
writer._overrides = Vec::new();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn private_rsa_key(&self) -> String {
|
||||
format!("{}.pem", self.rsa_key_filename())
|
||||
}
|
||||
|
||||
pub fn mail_enabled(&self) -> bool {
|
||||
let inner = &self.inner.read().unwrap().config;
|
||||
inner._enable_smtp && (inner.smtp_host.is_some() || inner.use_sendmail)
|
||||
|
||||
Reference in New Issue
Block a user