Browse Source

Cached config operations

cached-config-operations
Daniel García 2 weeks ago
parent
commit
c28eab74dd
No known key found for this signature in database GPG Key ID: FC8A7D14C3CD543A
  1. 32
      src/api/web.rs
  2. 94
      src/config.rs

32
src/api/web.rs

@ -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("/")]

94
src/config.rs

@ -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)

Loading…
Cancel
Save