geforkt von mirrored/vaultwarden
Added env variable to select config file. Initial work towards groups and added tooltips with descriptions and nicer names
Dieser Commit ist enthalten in:
Ursprung
3db815b969
Commit
dc92f07232
2 geänderte Dateien mit 62 neuen und 15 gelöschten Zeilen
|
@ -2,17 +2,18 @@ use std::process::exit;
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use crate::util::get_env;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref CONFIG: Config = Config::load().unwrap_or_else(|e| {
|
pub static ref CONFIG: Config = Config::load().unwrap_or_else(|e| {
|
||||||
println!("Error loading config:\n\t{:?}\n", e);
|
println!("Error loading config:\n\t{:?}\n", e);
|
||||||
exit(12)
|
exit(12)
|
||||||
});
|
});
|
||||||
pub static ref CONFIG_PATH: String = "data/config.json".into();
|
pub static ref CONFIG_FILE: String = get_env("CONFIG_FILE").unwrap_or("data/config.json".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! make_config {
|
macro_rules! make_config {
|
||||||
( $( $name:ident : $ty:ty, $editable:literal, $none_action:ident $(, $default:expr)? );+ $(;)? ) => {
|
( $( $(#[doc = $doc:literal])+ $name:ident : $ty:ty, $editable:literal, $none_action:ident $(, $default:expr)? );+ $(;)? ) => {
|
||||||
|
|
||||||
pub struct Config { inner: RwLock<Inner> }
|
pub struct Config { inner: RwLock<Inner> }
|
||||||
|
|
||||||
|
@ -35,7 +36,6 @@ macro_rules! make_config {
|
||||||
impl ConfigBuilder {
|
impl ConfigBuilder {
|
||||||
fn from_env() -> Self {
|
fn from_env() -> Self {
|
||||||
dotenv::dotenv().ok();
|
dotenv::dotenv().ok();
|
||||||
use crate::util::get_env;
|
|
||||||
|
|
||||||
let mut builder = ConfigBuilder::default();
|
let mut builder = ConfigBuilder::default();
|
||||||
$(
|
$(
|
||||||
|
@ -100,11 +100,9 @@ macro_rules! make_config {
|
||||||
)+
|
)+
|
||||||
|
|
||||||
pub fn load() -> Result<Self, Error> {
|
pub fn load() -> Result<Self, Error> {
|
||||||
// TODO: Get config.json from CONFIG_PATH env var or -c <CONFIG> console option
|
|
||||||
|
|
||||||
// Loading from env and file
|
// Loading from env and file
|
||||||
let _env = ConfigBuilder::from_env();
|
let _env = ConfigBuilder::from_env();
|
||||||
let _usr = ConfigBuilder::from_file(&CONFIG_PATH).unwrap_or_default();
|
let _usr = ConfigBuilder::from_file(&CONFIG_FILE).unwrap_or_default();
|
||||||
|
|
||||||
// Create merged config, config file overwrites env
|
// Create merged config, config file overwrites env
|
||||||
let builder = _env.merge(&_usr);
|
let builder = _env.merge(&_usr);
|
||||||
|
@ -138,12 +136,22 @@ macro_rules! make_config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn _get_doc(doc: &str) -> serde_json::Value {
|
||||||
|
let mut split = doc.split("|>").map(str::trim);
|
||||||
|
json!({
|
||||||
|
"group": split.next(),
|
||||||
|
"name": split.next(),
|
||||||
|
"description": split.next()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
json!([ $( {
|
json!([ $( {
|
||||||
"editable": $editable,
|
"editable": $editable,
|
||||||
"name": stringify!($name),
|
"name": stringify!($name),
|
||||||
"value": cfg.$name,
|
"value": cfg.$name,
|
||||||
"default": make_config!{ @default &cfg, $none_action, $($default)? },
|
"default": make_config!{ @default &cfg, $none_action, $($default)? },
|
||||||
"type": _get_form_type(stringify!($ty)),
|
"type": _get_form_type(stringify!($ty)),
|
||||||
|
"doc": _get_doc(concat!($($doc),+)),
|
||||||
}, )+ ])
|
}, )+ ])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,57 +184,96 @@ macro_rules! make_config {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//STRUCTURE: name: type, is_editable, none_action, <default_value (Optional)>
|
//STRUCTURE:
|
||||||
|
// /// Group |> Friendly Name |> Description (Optional)
|
||||||
|
// name: type, is_editable, none_action, <default_value (Optional)>
|
||||||
|
//
|
||||||
// Where none_action applied when the value wasn't provided and can be:
|
// Where none_action applied when the value wasn't provided and can be:
|
||||||
// def: Use a default value
|
// def: Use a default value
|
||||||
// auto: Value is auto generated based on other values
|
// auto: Value is auto generated based on other values
|
||||||
// option: Value is optional
|
// option: Value is optional
|
||||||
make_config! {
|
make_config! {
|
||||||
|
/// folders |> Data folder |> Main data folder
|
||||||
data_folder: String, false, def, "data".to_string();
|
data_folder: String, false, def, "data".to_string();
|
||||||
|
|
||||||
|
/// folders |> Database URL
|
||||||
database_url: String, false, auto, |c| format!("{}/{}", c.data_folder, "db.sqlite3");
|
database_url: String, false, auto, |c| format!("{}/{}", c.data_folder, "db.sqlite3");
|
||||||
|
/// folders |> Icon chache folder
|
||||||
icon_cache_folder: String, false, auto, |c| format!("{}/{}", c.data_folder, "icon_cache");
|
icon_cache_folder: String, false, auto, |c| format!("{}/{}", c.data_folder, "icon_cache");
|
||||||
|
/// folders |> Attachments folder
|
||||||
attachments_folder: String, false, auto, |c| format!("{}/{}", c.data_folder, "attachments");
|
attachments_folder: String, false, auto, |c| format!("{}/{}", c.data_folder, "attachments");
|
||||||
|
/// folders |> Templates folder
|
||||||
templates_folder: String, false, auto, |c| format!("{}/{}", c.data_folder, "templates");
|
templates_folder: String, false, auto, |c| format!("{}/{}", c.data_folder, "templates");
|
||||||
|
/// folders |> Session JWT key
|
||||||
rsa_key_filename: String, false, auto, |c| format!("{}/{}", c.data_folder, "rsa_key");
|
rsa_key_filename: String, false, auto, |c| format!("{}/{}", c.data_folder, "rsa_key");
|
||||||
|
|
||||||
|
/// ws |> Enable websocket notifications
|
||||||
websocket_enabled: bool, false, def, false;
|
websocket_enabled: bool, false, def, false;
|
||||||
|
/// ws |> Websocket address
|
||||||
websocket_address: String, false, def, "0.0.0.0".to_string();
|
websocket_address: String, false, def, "0.0.0.0".to_string();
|
||||||
|
/// ws |> Websocket port
|
||||||
websocket_port: u16, false, def, 3012;
|
websocket_port: u16, false, def, 3012;
|
||||||
|
|
||||||
|
/// folders |> Web vault folder
|
||||||
web_vault_folder: String, false, def, "web-vault/".to_string();
|
web_vault_folder: String, false, def, "web-vault/".to_string();
|
||||||
web_vault_enabled: bool, true, def, true;
|
/// settings |> Enable web vault
|
||||||
|
web_vault_enabled: bool, false, def, true;
|
||||||
|
|
||||||
|
/// icons |> Positive icon cache expiry |> Number of seconds to consider that an already cached icon is fresh. After this period, the icon will be redownloaded
|
||||||
icon_cache_ttl: u64, true, def, 2_592_000;
|
icon_cache_ttl: u64, true, def, 2_592_000;
|
||||||
|
/// icons |> Negative icon cache expiry |> Number of seconds before trying to download an icon that failed again.
|
||||||
icon_cache_negttl: u64, true, def, 259_200;
|
icon_cache_negttl: u64, true, def, 259_200;
|
||||||
|
|
||||||
|
/// settings |> Disable icon downloads |> Set to true to disable icon downloading, this would still serve icons from $ICON_CACHE_FOLDER,
|
||||||
|
/// but it won't produce any external network request. Needs to set $ICON_CACHE_TTL to 0,
|
||||||
|
/// otherwise it will delete them and they won't be downloaded again.
|
||||||
disable_icon_download: bool, true, def, false;
|
disable_icon_download: bool, true, def, false;
|
||||||
|
/// settings |> Allow new signups |> Controls if new users can register. Note that while this is disabled, users could still be invited
|
||||||
signups_allowed: bool, true, def, true;
|
signups_allowed: bool, true, def, true;
|
||||||
|
/// settings |> Allow invitations |> Controls whether users can be invited by organization admins, even when signups are disabled
|
||||||
invitations_allowed: bool, true, def, true;
|
invitations_allowed: bool, true, def, true;
|
||||||
|
/// settings |> Password iterations |> Number of server-side passwords hashing iterations. The changes only apply when a user changes their password. Not recommended to lower the value
|
||||||
password_iterations: i32, true, def, 100_000;
|
password_iterations: i32, true, def, 100_000;
|
||||||
|
/// settings |> Show password hints |> Controls if the password hint should be shown directly in the web page. Otherwise, if email is disabled, there is no way to see the password hint
|
||||||
show_password_hint: bool, true, def, true;
|
show_password_hint: bool, true, def, true;
|
||||||
|
|
||||||
|
/// settings |> Domain URL |> This needs to be set to the URL used to access the server, including 'http[s]://' and port, if it's different than the default. Some server functions don't work correctly without this value
|
||||||
domain: String, true, def, "http://localhost".to_string();
|
domain: String, true, def, "http://localhost".to_string();
|
||||||
|
/// private |> Domain set
|
||||||
domain_set: bool, false, def, false;
|
domain_set: bool, false, def, false;
|
||||||
|
|
||||||
|
/// settings |> Reload templates (Dev) |> When this is set to true, the templates get reloaded with every request. ONLY use this during development, as it can slow down the server
|
||||||
reload_templates: bool, true, def, false;
|
reload_templates: bool, true, def, false;
|
||||||
|
|
||||||
|
/// log |> Enable extended logging
|
||||||
extended_logging: bool, false, def, true;
|
extended_logging: bool, false, def, true;
|
||||||
|
/// log |> Log file path
|
||||||
log_file: String, false, option;
|
log_file: String, false, option;
|
||||||
|
|
||||||
|
/// settings |> Admin page token |> The token used to authenticate in this very same page. Changing it here won't deauthorize the current session
|
||||||
admin_token: String, true, option;
|
admin_token: String, true, option;
|
||||||
|
|
||||||
|
/// yubico |> Yubico Client ID
|
||||||
yubico_client_id: String, true, option;
|
yubico_client_id: String, true, option;
|
||||||
|
/// yubico |> Yubico secret Key
|
||||||
yubico_secret_key: String, true, option;
|
yubico_secret_key: String, true, option;
|
||||||
|
/// yubico |> Yubico Server
|
||||||
yubico_server: String, true, option;
|
yubico_server: String, true, option;
|
||||||
|
|
||||||
// Mail settings
|
// TODO: Remove SMTP from name once groups work
|
||||||
|
/// mail |> SMTP Host
|
||||||
smtp_host: String, true, option;
|
smtp_host: String, true, option;
|
||||||
|
/// mail |> Enable SMTP SSL
|
||||||
smtp_ssl: bool, true, def, true;
|
smtp_ssl: bool, true, def, true;
|
||||||
|
/// mail |> SMTP Port
|
||||||
smtp_port: u16, true, auto, |c| if c.smtp_ssl {587} else {25};
|
smtp_port: u16, true, auto, |c| if c.smtp_ssl {587} else {25};
|
||||||
|
/// mail |> SMTP From Address
|
||||||
smtp_from: String, true, def, String::new();
|
smtp_from: String, true, def, String::new();
|
||||||
|
/// mail |> SMTP From Name
|
||||||
smtp_from_name: String, true, def, "Bitwarden_RS".to_string();
|
smtp_from_name: String, true, def, "Bitwarden_RS".to_string();
|
||||||
|
/// mail |> SMTP Username
|
||||||
smtp_username: String, true, option;
|
smtp_username: String, true, option;
|
||||||
|
/// mail |> SMTP Password
|
||||||
smtp_password: String, true, option;
|
smtp_password: String, true, option;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,7 +317,7 @@ impl Config {
|
||||||
|
|
||||||
//Save to file
|
//Save to file
|
||||||
use std::{fs::File, io::Write};
|
use std::{fs::File, io::Write};
|
||||||
let mut file = File::create(&*CONFIG_PATH)?;
|
let mut file = File::create(&*CONFIG_FILE)?;
|
||||||
file.write_all(config_str.as_bytes())?;
|
file.write_all(config_str.as_bytes())?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -60,17 +60,17 @@
|
||||||
<form class="form" id="config-form">
|
<form class="form" id="config-form">
|
||||||
{{#each config}}
|
{{#each config}}
|
||||||
{{#if editable}}
|
{{#if editable}}
|
||||||
<div class="form-group row">
|
<div class="form-group row" title="{{doc.description}}">
|
||||||
{{#case type "text" "number"}}
|
{{#case type "text" "number"}}
|
||||||
<label for="input_{{name}}" class="col-sm-2 col-form-label">{{name}}</label>
|
<label for="input_{{name}}" class="col-sm-3 col-form-label">{{doc.name}}</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-8">
|
||||||
<input class="form-control" id="input_{{name}}" type="{{type}}" name="{{name}}" value="{{value}}"
|
<input class="form-control" id="input_{{name}}" type="{{type}}" name="{{name}}" value="{{value}}"
|
||||||
{{#if default}} placeholder="Default: {{default}}" {{/if}}>
|
{{#if default}} placeholder="Default: {{default}}" {{/if}}>
|
||||||
</div>
|
</div>
|
||||||
{{/case}}
|
{{/case}}
|
||||||
{{#case type "checkbox"}}
|
{{#case type "checkbox"}}
|
||||||
<div class="col-sm-2">{{name}}</div>
|
<div class="col-sm-3">{{doc.name}}</div>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-8">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox" id="input_{{name}}" name="{{name}}"
|
<input class="form-check-input" type="checkbox" id="input_{{name}}" name="{{name}}"
|
||||||
{{#if value}} checked {{/if}}>
|
{{#if value}} checked {{/if}}>
|
||||||
|
|
Laden …
In neuem Issue referenzieren