geforkt von mirrored/vaultwarden
Show organizations in admin panel, implement reload templates option
Dieser Commit ist enthalten in:
Ursprung
a797459560
Commit
bfd93e5b13
5 geänderte Dateien mit 56 neuen und 9 gelöschten Zeilen
|
@ -13,6 +13,8 @@
|
||||||
## Templates data folder, by default uses embedded templates
|
## Templates data folder, by default uses embedded templates
|
||||||
## Check source code to see the format
|
## Check source code to see the format
|
||||||
# TEMPLATES_FOLDER=/path/to/templates
|
# TEMPLATES_FOLDER=/path/to/templates
|
||||||
|
## Automatically reload the templates for every request, slow, use only for development
|
||||||
|
# RELOAD_TEMPLATES=false
|
||||||
|
|
||||||
## Cache time-to-live for successfully obtained icons, in seconds (0 is "forever")
|
## Cache time-to-live for successfully obtained icons, in seconds (0 is "forever")
|
||||||
# ICON_CACHE_TTL=2592000
|
# ICON_CACHE_TTL=2592000
|
||||||
|
|
|
@ -49,7 +49,7 @@ impl AdminTemplateData {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(self) -> Result<String, Error> {
|
fn render(self) -> Result<String, Error> {
|
||||||
CONFIG.templates.render("admin/base", &self).map_err(Into::into)
|
CONFIG.render_template("admin/base", &self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +72,7 @@ struct LoginForm {
|
||||||
fn post_admin_login(data: Form<LoginForm>, mut cookies: Cookies, ip: ClientIp) -> Result<Redirect, Flash<Redirect>> {
|
fn post_admin_login(data: Form<LoginForm>, mut cookies: Cookies, ip: ClientIp) -> Result<Redirect, Flash<Redirect>> {
|
||||||
let data = data.into_inner();
|
let data = data.into_inner();
|
||||||
|
|
||||||
|
// If the token is invalid, redirect to login page
|
||||||
if !_validate_token(&data.token) {
|
if !_validate_token(&data.token) {
|
||||||
error!("Invalid admin token. IP: {}", ip.ip);
|
error!("Invalid admin token. IP: {}", ip.ip);
|
||||||
Err(Flash::error(
|
Err(Flash::error(
|
||||||
|
|
|
@ -36,7 +36,7 @@ fn mailer(config: &MailConfig) -> SmtpTransport {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_text(template_name: &'static str, data: serde_json::Value) -> Result<(String, String), Error> {
|
fn get_text(template_name: &'static str, data: serde_json::Value) -> Result<(String, String), Error> {
|
||||||
let text = CONFIG.templates.render(template_name, &data)?;
|
let text = CONFIG.render_template(template_name, &data)?;
|
||||||
let mut text_split = text.split("<!---------------->");
|
let mut text_split = text.split("<!---------------->");
|
||||||
|
|
||||||
let subject = match text_split.next() {
|
let subject = match text_split.next() {
|
||||||
|
|
25
src/main.rs
25
src/main.rs
|
@ -331,9 +331,11 @@ pub struct Config {
|
||||||
|
|
||||||
mail: Option<MailConfig>,
|
mail: Option<MailConfig>,
|
||||||
templates: Handlebars,
|
templates: Handlebars,
|
||||||
|
templates_folder: String,
|
||||||
|
reload_templates: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_templates(path: String) -> Handlebars {
|
fn load_templates(path: &str) -> Handlebars {
|
||||||
let mut hb = Handlebars::new();
|
let mut hb = Handlebars::new();
|
||||||
// Error on missing params
|
// Error on missing params
|
||||||
hb.set_strict_mode(true);
|
hb.set_strict_mode(true);
|
||||||
|
@ -365,6 +367,21 @@ fn load_templates(path: String) -> Handlebars {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
pub fn render_template<T: serde::ser::Serialize>(&self, name: &str, data: &T) -> Result<String, error::Error> {
|
||||||
|
// We add this to signal the compiler not to drop the result of 'load_templates'
|
||||||
|
let hb_owned;
|
||||||
|
|
||||||
|
let hb = if CONFIG.reload_templates {
|
||||||
|
warn!("RELOADING TEMPLATES");
|
||||||
|
hb_owned = load_templates(&self.templates_folder);
|
||||||
|
&hb_owned
|
||||||
|
} else {
|
||||||
|
&self.templates
|
||||||
|
};
|
||||||
|
|
||||||
|
hb.render(name, data).map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
fn load() -> Self {
|
fn load() -> Self {
|
||||||
use crate::util::{get_env, get_env_or};
|
use crate::util::{get_env, get_env_or};
|
||||||
dotenv::dotenv().ok();
|
dotenv::dotenv().ok();
|
||||||
|
@ -377,11 +394,15 @@ impl Config {
|
||||||
let yubico_client_id = get_env("YUBICO_CLIENT_ID");
|
let yubico_client_id = get_env("YUBICO_CLIENT_ID");
|
||||||
let yubico_secret_key = get_env("YUBICO_SECRET_KEY");
|
let yubico_secret_key = get_env("YUBICO_SECRET_KEY");
|
||||||
|
|
||||||
|
let templates_folder = get_env_or("TEMPLATES_FOLDER", format!("{}/{}", &df, "templates"));
|
||||||
|
|
||||||
Config {
|
Config {
|
||||||
database_url: get_env_or("DATABASE_URL", format!("{}/{}", &df, "db.sqlite3")),
|
database_url: get_env_or("DATABASE_URL", format!("{}/{}", &df, "db.sqlite3")),
|
||||||
icon_cache_folder: get_env_or("ICON_CACHE_FOLDER", format!("{}/{}", &df, "icon_cache")),
|
icon_cache_folder: get_env_or("ICON_CACHE_FOLDER", format!("{}/{}", &df, "icon_cache")),
|
||||||
attachments_folder: get_env_or("ATTACHMENTS_FOLDER", format!("{}/{}", &df, "attachments")),
|
attachments_folder: get_env_or("ATTACHMENTS_FOLDER", format!("{}/{}", &df, "attachments")),
|
||||||
templates: load_templates(get_env_or("TEMPLATES_FOLDER", format!("{}/{}", &df, "templates"))),
|
templates: load_templates(&templates_folder),
|
||||||
|
templates_folder,
|
||||||
|
reload_templates: get_env_or("RELOAD_TEMPLATES", false),
|
||||||
|
|
||||||
// icon_cache_ttl defaults to 30 days (30 * 24 * 60 * 60 seconds)
|
// icon_cache_ttl defaults to 30 days (30 * 24 * 60 * 60 seconds)
|
||||||
icon_cache_ttl: get_env_or("ICON_CACHE_TTL", 2_592_000),
|
icon_cache_ttl: get_env_or("ICON_CACHE_TTL", 2_592_000),
|
||||||
|
|
|
@ -7,12 +7,24 @@
|
||||||
<div class="media pt-3">
|
<div class="media pt-3">
|
||||||
<img class="mr-2 rounded identicon" data-src="{{Email}}">
|
<img class="mr-2 rounded identicon" data-src="{{Email}}">
|
||||||
<div class="media-body pb-3 mb-0 small border-bottom">
|
<div class="media-body pb-3 mb-0 small border-bottom">
|
||||||
<div class="d-flex justify-content-between">
|
<div class="row justify-content-between">
|
||||||
|
<div class="col">
|
||||||
<strong>{{Name}}</strong>
|
<strong>{{Name}}</strong>
|
||||||
<a class="tmp-del mr-3" href="" onclick='deleteUser("{{Id}}", "{{Email}}");'>Delete User</a>
|
|
||||||
</div>
|
|
||||||
<span class="d-block">{{Email}}</span>
|
<span class="d-block">{{Email}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<strong> Organizations:</strong>
|
||||||
|
<span class="d-block">
|
||||||
|
{{#each Organizations}}
|
||||||
|
<span class="badge badge-primary" data-orgtype="{{Type}}">{{Name}}</span>
|
||||||
|
{{/each}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div style="flex: 0 0 100px;">
|
||||||
|
<a class="mr-3" href="" onclick='deleteUser("{{Id}}", "{{Email}}");'>Delete User</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
||||||
|
@ -73,11 +85,22 @@
|
||||||
"Error inviting user", data);
|
"Error inviting user", data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let OrgTypes = {
|
||||||
|
"0": { "name": "Owner", "color": "orange" },
|
||||||
|
"1": { "name": "Admin", "color": "blueviolet" },
|
||||||
|
"2": { "name": "User", "color": "blue" },
|
||||||
|
"3": { "name": "Manager", "color": "green" },
|
||||||
|
};
|
||||||
|
|
||||||
$(window).on('load', function () {
|
$(window).on('load', function () {
|
||||||
//$("#reload-btn").click(reload);
|
|
||||||
$("#invite-form").submit(inviteUser);
|
$("#invite-form").submit(inviteUser);
|
||||||
$("img.identicon").each(function (i, e) {
|
$("img.identicon").each(function (i, e) {
|
||||||
e.src = identicon(e.dataset.src);
|
e.src = identicon(e.dataset.src);
|
||||||
});
|
});
|
||||||
|
$('[data-orgtype]').each(function (i, e) {
|
||||||
|
let orgtype = OrgTypes[e.dataset.orgtype];
|
||||||
|
e.style.backgroundColor = orgtype.color;
|
||||||
|
e.title = orgtype.name;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
Laden …
In neuem Issue referenzieren