Add live reloading to some options, email fix

live reloading was intended for previous release, but needed some
tweaking. Settings that still require a restart are marked with an R.
Fixed issue where default values weren't being filled in on reload of
config that broke emails if settings were changed at all.
This commit is contained in:
Harvey Tindall 2020-06-30 21:24:07 +01:00
parent 0bb54d1c45
commit ac60cc37da
7 changed files with 90 additions and 76 deletions

View File

@ -1,5 +1,5 @@
#!/usr/bin/env python3
__version__ = "0.2.5"
__version__ = "0.2.6"
import secrets
import configparser
@ -66,14 +66,14 @@ if data_dir.exists() is False or (data_dir / "config.ini").exists() is False:
else:
config_path = data_dir / "config.ini"
config = configparser.RawConfigParser()
config.read(config_path)
temp_config = configparser.RawConfigParser()
temp_config.read(config_path)
def create_log(name):
log = logging.getLogger(name)
handler = logging.StreamHandler(sys.stdout)
if config.getboolean("ui", "debug"):
if temp_config.getboolean('ui', 'debug'):
log.setLevel(logging.DEBUG)
handler.setLevel(logging.DEBUG)
else:
@ -88,6 +88,59 @@ def create_log(name):
log = create_log("main")
def load_config(config_path, data_dir):
config = configparser.RawConfigParser()
config.read(config_path)
global log
for key in config["files"]:
if config["files"][key] == "":
if key != "custom_css":
log.debug(f"Using default {key}")
config["files"][key] = str(data_dir / (key + ".json"))
for key in ["user_configuration", "user_displayprefs"]:
if key not in config["files"]:
log.debug(f"Using default {key}")
config["files"][key] = str(data_dir / (key + ".json"))
if "no_username" not in config["email"]:
config["email"]["no_username"] = "false"
log.debug("Set no_username to false")
if (
"email_html" not in config["password_resets"]
or config["password_resets"]["email_html"] == ""
):
log.debug("Using default password reset email HTML template")
config["password_resets"]["email_html"] = str(local_dir / "email.html")
if (
"email_text" not in config["password_resets"]
or config["password_resets"]["email_text"] == ""
):
log.debug("Using default password reset email plaintext template")
config["password_resets"]["email_text"] = str(local_dir / "email.txt")
if (
"email_html" not in config["invite_emails"]
or config["invite_emails"]["email_html"] == ""
):
log.debug("Using default invite email HTML template")
config["invite_emails"]["email_html"] = str(local_dir / "invite-email.html")
if (
"email_text" not in config["invite_emails"]
or config["invite_emails"]["email_text"] == ""
):
log.debug("Using default invite email plaintext template")
config["invite_emails"]["email_text"] = str(local_dir / "invite-email.txt")
if (
"public_server" not in config["jellyfin"]
or config["jellyfin"]["public_server"] == ""
):
config["jellyfin"]["public_server"] = config["jellyfin"]["server"]
return config
config = load_config(config_path, data_dir)
web_log = create_log("waitress")
if not first_run:
email_log = create_log("emails")
@ -100,20 +153,6 @@ if args.port is not None:
log.debug(f"Using specified port {args.port}")
config["ui"]["port"] = args.port
for key in config["files"]:
if config["files"][key] == "":
if key != "custom_css":
log.debug(f"Using default {key}")
config["files"][key] = str(data_dir / (key + ".json"))
for key in ["user_configuration", "user_displayprefs"]:
if key not in config["files"]:
log.debug(f"Using default {key}")
config["files"][key] = str(data_dir / (key + ".json"))
if "no_username" not in config["email"]:
config["email"]["no_username"] = "false"
log.debug("Set no_username to false")
try:
with open(config["files"]["invites"], "r") as f:
@ -171,36 +210,6 @@ if "custom_css" in config["files"]:
)
if (
"email_html" not in config["password_resets"]
or config["password_resets"]["email_html"] == ""
):
log.debug("Using default password reset email HTML template")
config["password_resets"]["email_html"] = str(local_dir / "email.html")
if (
"email_text" not in config["password_resets"]
or config["password_resets"]["email_text"] == ""
):
log.debug("Using default password reset email plaintext template")
config["password_resets"]["email_text"] = str(local_dir / "email.txt")
if (
"email_html" not in config["invite_emails"]
or config["invite_emails"]["email_html"] == ""
):
log.debug("Using default invite email HTML template")
config["invite_emails"]["email_html"] = str(local_dir / "invite-email.html")
if (
"email_text" not in config["invite_emails"]
or config["invite_emails"]["email_text"] == ""
):
log.debug("Using default invite email plaintext template")
config["invite_emails"]["email_text"] = str(local_dir / "invite-email.txt")
if (
"public_server" not in config["jellyfin"]
or config["jellyfin"]["public_server"] == ""
):
config["jellyfin"]["public_server"] = config["jellyfin"]["server"]
def resp(success=True, code=500):

View File

@ -87,7 +87,7 @@
},
"jellyfin_login": {
"name": "Use Jellyfin for authentication",
"required": true,
"required": false,
"requires_restart": true,
"type": "bool",
"value": true,
@ -122,7 +122,7 @@
},
"debug": {
"name": "Debug logging",
"required": true,
"required": false,
"requires_restart": true,
"type": "bool",
"value": false
@ -130,7 +130,7 @@
"contact_message": {
"name": "Contact message",
"required": false,
"requires_restart": false,
"requires_restart": true,
"type": "text",
"value": "Need help? contact me.",
"description": "Displayed at bottom of all pages except admin"
@ -138,7 +138,7 @@
"help_message": {
"name": "Help message",
"required": false,
"requires_restart": false,
"requires_restart": true,
"type": "text",
"value": "Enter your details to create an account.",
"description": "Display at top of invite form."
@ -146,7 +146,7 @@
"success_message": {
"name": "Success message",
"required": false,
"requires_restart": false,
"requires_restart": true,
"type": "text",
"value": "Your account has been created. Click below to continue to Jellyfin.",
"description": "Displayed when a user creates an account"
@ -159,7 +159,7 @@
},
"enabled": {
"name": "Enabled",
"required": true,
"required": false,
"requires_restart": true,
"type": "bool",
"value": true
@ -195,6 +195,7 @@
"special": {
"name": "Minimum number of special characters",
"requires_restart": true,
"depends_true": "enabled",
"type": "text",
"value": "0"
}
@ -277,7 +278,7 @@
},
"enabled": {
"name": "Enabled",
"required": true,
"required": false,
"requires_restart": true,
"type": "bool",
"value": true,
@ -327,7 +328,7 @@
},
"enabled": {
"name": "Enabled",
"required": true,
"required": false,
"requires_restart": true,
"type": "bool",
"value": true

View File

@ -498,6 +498,9 @@ document.getElementById('openSettings').onclick = function () {
entryName += ' <sup class="text-danger">*</sup>';
required = true;
};
if (config[section][entry]['requires_restart']) {
entryName += ' <sup class="text-danger">R</sup>';
};
if (config[section][entry].hasOwnProperty('description')) {
var tooltip = `
<a class="text-muted" href="#" data-toggle="tooltip" data-placement="right" title="${config[section][entry]['description']}"><i class="fa fa-question-circle-o"></i></a>
@ -505,15 +508,12 @@ document.getElementById('openSettings').onclick = function () {
entryName += ' ';
entryName += tooltip;
};
// if (config[section][entry]['requires_restart']) {
// entryName += ' <sup class="text-danger">R</sup>';
// };
var entryValue = config[section][entry]['value'];
var entryType = config[section][entry]['type'];
var entryGroup = document.createElement('div');
if (entryType == 'bool') {
entryGroup.classList.add('form-check');
if (entryValue) {
if (entryValue.toString() == 'true') {
var checked = true;
} else {
var checked = false;
@ -604,8 +604,8 @@ function sendConfig(modalId) {
xhr.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":"));
},
success: function() {
$('#' + modalId).modal('hide');
if (modalId != 'settingsMenu') {
$('#' + modalId).modal('hide');
$('#settingsMenu').modal('hide');
};
},
@ -618,13 +618,11 @@ function sendConfig(modalId) {
footer.appendChild(alert);
},
});
// placeholder
};
document.getElementById('settingsSave').onclick = function() {
modifiedConfig = {};
// Live config changes have not yet been implemented, so restart always required.
// var restart_setting_changed = false;
var restart_setting_changed = false;
var settings_changed = false;
for (var section of Object.keys(config)) {
@ -643,23 +641,24 @@ document.getElementById('settingsSave').onclick = function() {
};
modifiedConfig[section][entry] = value;
settings_changed = true;
// if (config[section][entry]['requires_restart']) {
// restart_setting_changed = true;
// };
if (config[section][entry]['requires_restart']) {
restart_setting_changed = true;
};
};
};
};
};
// if (restart_setting_changed) {
if (settings_changed) {
if (restart_setting_changed) {
document.getElementById('applyRestarts').onclick = function(){sendConfig('restartModal');};
$('#settingsMenu').modal('hide');
$('#restartModal').modal({
backdrop: 'static',
show: true
});
} else if (settings_changed) {
sendConfig('settingsMenu');
} else {
// sendConfig('settingsMenu');
$('#settingsMenu').modal('hide');
};
};

View File

@ -93,7 +93,7 @@
</div>
<div class="modal-body">
<ul class="list-group list-group-flush">
<p>Note: <sup class="text-danger">*</sup> Indicates required field.</p>
<p>Note: <sup class="text-danger">*</sup> Indicates required field, <sup class="text-danger">R</sup> Indicates changes require a restart.</p>
<button type="button" class="list-group-item list-group-item-action" id="openUsers">
Users <i class="fa fa-user"></i>
</button>
@ -160,7 +160,7 @@
<h5 class="modal-title">Warning</h5>
</div>
<div class="modal-body">
<p>A restart is needed to apply settings. This must be done manually. Apply now?</p>
<p>A restart is needed to apply some settings. This must be done manually. Apply now?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>

View File

@ -1,8 +1,9 @@
from pathlib import Path
from flask import Flask, send_from_directory, render_template
from jellyfin_accounts import config, app, g, css, data_store
from jellyfin_accounts import app, g, css, data_store
from jellyfin_accounts import web_log as log
from jellyfin_accounts.web_api import checkInvite, validator
from jellyfin_accounts.web_api import config, checkInvite, validator
@app.errorhandler(404)

View File

@ -7,6 +7,8 @@ import time
from jellyfin_accounts import (
config,
config_path,
load_config,
data_dir,
app,
g,
data_store,
@ -326,6 +328,7 @@ def setDefaults():
@app.route("/modifyConfig", methods=["POST"])
@auth.login_required
def modifyConfig():
global config
log.info("Config modification requested")
data = request.get_json()
temp_config = configparser.RawConfigParser(
@ -344,7 +347,8 @@ def modifyConfig():
log.debug(f"{section}/{item} does not exist in config")
with open(config_path, "w") as config_file:
temp_config.write(config_file)
log.info("Config written. Restart is needed to load settings.")
config = load_config(config_path, data_dir)
log.info("Config written. Restart may be needed to load settings.")
return resp()
@ -361,7 +365,7 @@ def getConfig():
log.debug("Config requested")
with open(config_base_path, "r") as f:
config_base = json.load(f)
config.read(config_path)
# config.read(config_path)
response_config = config_base
for section in config_base:
for entry in config_base[section]:

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "jellyfin-accounts"
version = "0.2.5"
version = "0.2.6"
readme = "README.md"
description = "A simple account management system for Jellyfin"
authors = ["Harvey Tindall <harveyltindall@gmail.com>"]