mirror of
https://github.com/hrfee/jellyfin-accounts.git
synced 2024-12-22 09:00:14 +00:00
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:
parent
0bb54d1c45
commit
ac60cc37da
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
__version__ = "0.2.5"
|
__version__ = "0.2.6"
|
||||||
|
|
||||||
import secrets
|
import secrets
|
||||||
import configparser
|
import configparser
|
||||||
@ -66,14 +66,14 @@ if data_dir.exists() is False or (data_dir / "config.ini").exists() is False:
|
|||||||
else:
|
else:
|
||||||
config_path = data_dir / "config.ini"
|
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):
|
def create_log(name):
|
||||||
log = logging.getLogger(name)
|
log = logging.getLogger(name)
|
||||||
handler = logging.StreamHandler(sys.stdout)
|
handler = logging.StreamHandler(sys.stdout)
|
||||||
if config.getboolean("ui", "debug"):
|
if temp_config.getboolean('ui', 'debug'):
|
||||||
log.setLevel(logging.DEBUG)
|
log.setLevel(logging.DEBUG)
|
||||||
handler.setLevel(logging.DEBUG)
|
handler.setLevel(logging.DEBUG)
|
||||||
else:
|
else:
|
||||||
@ -88,6 +88,59 @@ def create_log(name):
|
|||||||
|
|
||||||
|
|
||||||
log = create_log("main")
|
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")
|
web_log = create_log("waitress")
|
||||||
if not first_run:
|
if not first_run:
|
||||||
email_log = create_log("emails")
|
email_log = create_log("emails")
|
||||||
@ -100,20 +153,6 @@ if args.port is not None:
|
|||||||
log.debug(f"Using specified port {args.port}")
|
log.debug(f"Using specified port {args.port}")
|
||||||
config["ui"]["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:
|
try:
|
||||||
with open(config["files"]["invites"], "r") as f:
|
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):
|
def resp(success=True, code=500):
|
||||||
|
@ -87,7 +87,7 @@
|
|||||||
},
|
},
|
||||||
"jellyfin_login": {
|
"jellyfin_login": {
|
||||||
"name": "Use Jellyfin for authentication",
|
"name": "Use Jellyfin for authentication",
|
||||||
"required": true,
|
"required": false,
|
||||||
"requires_restart": true,
|
"requires_restart": true,
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"value": true,
|
"value": true,
|
||||||
@ -122,7 +122,7 @@
|
|||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"name": "Debug logging",
|
"name": "Debug logging",
|
||||||
"required": true,
|
"required": false,
|
||||||
"requires_restart": true,
|
"requires_restart": true,
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"value": false
|
"value": false
|
||||||
@ -130,7 +130,7 @@
|
|||||||
"contact_message": {
|
"contact_message": {
|
||||||
"name": "Contact message",
|
"name": "Contact message",
|
||||||
"required": false,
|
"required": false,
|
||||||
"requires_restart": false,
|
"requires_restart": true,
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"value": "Need help? contact me.",
|
"value": "Need help? contact me.",
|
||||||
"description": "Displayed at bottom of all pages except admin"
|
"description": "Displayed at bottom of all pages except admin"
|
||||||
@ -138,7 +138,7 @@
|
|||||||
"help_message": {
|
"help_message": {
|
||||||
"name": "Help message",
|
"name": "Help message",
|
||||||
"required": false,
|
"required": false,
|
||||||
"requires_restart": false,
|
"requires_restart": true,
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"value": "Enter your details to create an account.",
|
"value": "Enter your details to create an account.",
|
||||||
"description": "Display at top of invite form."
|
"description": "Display at top of invite form."
|
||||||
@ -146,7 +146,7 @@
|
|||||||
"success_message": {
|
"success_message": {
|
||||||
"name": "Success message",
|
"name": "Success message",
|
||||||
"required": false,
|
"required": false,
|
||||||
"requires_restart": false,
|
"requires_restart": true,
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"value": "Your account has been created. Click below to continue to Jellyfin.",
|
"value": "Your account has been created. Click below to continue to Jellyfin.",
|
||||||
"description": "Displayed when a user creates an account"
|
"description": "Displayed when a user creates an account"
|
||||||
@ -159,7 +159,7 @@
|
|||||||
},
|
},
|
||||||
"enabled": {
|
"enabled": {
|
||||||
"name": "Enabled",
|
"name": "Enabled",
|
||||||
"required": true,
|
"required": false,
|
||||||
"requires_restart": true,
|
"requires_restart": true,
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"value": true
|
"value": true
|
||||||
@ -195,6 +195,7 @@
|
|||||||
"special": {
|
"special": {
|
||||||
"name": "Minimum number of special characters",
|
"name": "Minimum number of special characters",
|
||||||
"requires_restart": true,
|
"requires_restart": true,
|
||||||
|
"depends_true": "enabled",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"value": "0"
|
"value": "0"
|
||||||
}
|
}
|
||||||
@ -277,7 +278,7 @@
|
|||||||
},
|
},
|
||||||
"enabled": {
|
"enabled": {
|
||||||
"name": "Enabled",
|
"name": "Enabled",
|
||||||
"required": true,
|
"required": false,
|
||||||
"requires_restart": true,
|
"requires_restart": true,
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"value": true,
|
"value": true,
|
||||||
@ -327,7 +328,7 @@
|
|||||||
},
|
},
|
||||||
"enabled": {
|
"enabled": {
|
||||||
"name": "Enabled",
|
"name": "Enabled",
|
||||||
"required": true,
|
"required": false,
|
||||||
"requires_restart": true,
|
"requires_restart": true,
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"value": true
|
"value": true
|
||||||
|
@ -498,6 +498,9 @@ document.getElementById('openSettings').onclick = function () {
|
|||||||
entryName += ' <sup class="text-danger">*</sup>';
|
entryName += ' <sup class="text-danger">*</sup>';
|
||||||
required = true;
|
required = true;
|
||||||
};
|
};
|
||||||
|
if (config[section][entry]['requires_restart']) {
|
||||||
|
entryName += ' <sup class="text-danger">R</sup>';
|
||||||
|
};
|
||||||
if (config[section][entry].hasOwnProperty('description')) {
|
if (config[section][entry].hasOwnProperty('description')) {
|
||||||
var tooltip = `
|
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>
|
<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 += ' ';
|
||||||
entryName += tooltip;
|
entryName += tooltip;
|
||||||
};
|
};
|
||||||
// if (config[section][entry]['requires_restart']) {
|
|
||||||
// entryName += ' <sup class="text-danger">R</sup>';
|
|
||||||
// };
|
|
||||||
var entryValue = config[section][entry]['value'];
|
var entryValue = config[section][entry]['value'];
|
||||||
var entryType = config[section][entry]['type'];
|
var entryType = config[section][entry]['type'];
|
||||||
var entryGroup = document.createElement('div');
|
var entryGroup = document.createElement('div');
|
||||||
if (entryType == 'bool') {
|
if (entryType == 'bool') {
|
||||||
entryGroup.classList.add('form-check');
|
entryGroup.classList.add('form-check');
|
||||||
if (entryValue) {
|
if (entryValue.toString() == 'true') {
|
||||||
var checked = true;
|
var checked = true;
|
||||||
} else {
|
} else {
|
||||||
var checked = false;
|
var checked = false;
|
||||||
@ -604,8 +604,8 @@ function sendConfig(modalId) {
|
|||||||
xhr.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":"));
|
xhr.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":"));
|
||||||
},
|
},
|
||||||
success: function() {
|
success: function() {
|
||||||
|
$('#' + modalId).modal('hide');
|
||||||
if (modalId != 'settingsMenu') {
|
if (modalId != 'settingsMenu') {
|
||||||
$('#' + modalId).modal('hide');
|
|
||||||
$('#settingsMenu').modal('hide');
|
$('#settingsMenu').modal('hide');
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -618,13 +618,11 @@ function sendConfig(modalId) {
|
|||||||
footer.appendChild(alert);
|
footer.appendChild(alert);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// placeholder
|
|
||||||
};
|
};
|
||||||
|
|
||||||
document.getElementById('settingsSave').onclick = function() {
|
document.getElementById('settingsSave').onclick = function() {
|
||||||
modifiedConfig = {};
|
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;
|
var settings_changed = false;
|
||||||
|
|
||||||
for (var section of Object.keys(config)) {
|
for (var section of Object.keys(config)) {
|
||||||
@ -643,23 +641,24 @@ document.getElementById('settingsSave').onclick = function() {
|
|||||||
};
|
};
|
||||||
modifiedConfig[section][entry] = value;
|
modifiedConfig[section][entry] = value;
|
||||||
settings_changed = true;
|
settings_changed = true;
|
||||||
// if (config[section][entry]['requires_restart']) {
|
if (config[section][entry]['requires_restart']) {
|
||||||
// restart_setting_changed = true;
|
restart_setting_changed = true;
|
||||||
// };
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
// if (restart_setting_changed) {
|
// if (restart_setting_changed) {
|
||||||
if (settings_changed) {
|
if (restart_setting_changed) {
|
||||||
document.getElementById('applyRestarts').onclick = function(){sendConfig('restartModal');};
|
document.getElementById('applyRestarts').onclick = function(){sendConfig('restartModal');};
|
||||||
$('#settingsMenu').modal('hide');
|
$('#settingsMenu').modal('hide');
|
||||||
$('#restartModal').modal({
|
$('#restartModal').modal({
|
||||||
backdrop: 'static',
|
backdrop: 'static',
|
||||||
show: true
|
show: true
|
||||||
});
|
});
|
||||||
|
} else if (settings_changed) {
|
||||||
|
sendConfig('settingsMenu');
|
||||||
} else {
|
} else {
|
||||||
// sendConfig('settingsMenu');
|
|
||||||
$('#settingsMenu').modal('hide');
|
$('#settingsMenu').modal('hide');
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -93,7 +93,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<ul class="list-group list-group-flush">
|
<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">
|
<button type="button" class="list-group-item list-group-item-action" id="openUsers">
|
||||||
Users <i class="fa fa-user"></i>
|
Users <i class="fa fa-user"></i>
|
||||||
</button>
|
</button>
|
||||||
@ -160,7 +160,7 @@
|
|||||||
<h5 class="modal-title">Warning</h5>
|
<h5 class="modal-title">Warning</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<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>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from flask import Flask, send_from_directory, render_template
|
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 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)
|
@app.errorhandler(404)
|
||||||
|
@ -7,6 +7,8 @@ import time
|
|||||||
from jellyfin_accounts import (
|
from jellyfin_accounts import (
|
||||||
config,
|
config,
|
||||||
config_path,
|
config_path,
|
||||||
|
load_config,
|
||||||
|
data_dir,
|
||||||
app,
|
app,
|
||||||
g,
|
g,
|
||||||
data_store,
|
data_store,
|
||||||
@ -326,6 +328,7 @@ def setDefaults():
|
|||||||
@app.route("/modifyConfig", methods=["POST"])
|
@app.route("/modifyConfig", methods=["POST"])
|
||||||
@auth.login_required
|
@auth.login_required
|
||||||
def modifyConfig():
|
def modifyConfig():
|
||||||
|
global config
|
||||||
log.info("Config modification requested")
|
log.info("Config modification requested")
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
temp_config = configparser.RawConfigParser(
|
temp_config = configparser.RawConfigParser(
|
||||||
@ -344,7 +347,8 @@ def modifyConfig():
|
|||||||
log.debug(f"{section}/{item} does not exist in config")
|
log.debug(f"{section}/{item} does not exist in config")
|
||||||
with open(config_path, "w") as config_file:
|
with open(config_path, "w") as config_file:
|
||||||
temp_config.write(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()
|
return resp()
|
||||||
|
|
||||||
|
|
||||||
@ -361,7 +365,7 @@ def getConfig():
|
|||||||
log.debug("Config requested")
|
log.debug("Config requested")
|
||||||
with open(config_base_path, "r") as f:
|
with open(config_base_path, "r") as f:
|
||||||
config_base = json.load(f)
|
config_base = json.load(f)
|
||||||
config.read(config_path)
|
# config.read(config_path)
|
||||||
response_config = config_base
|
response_config = config_base
|
||||||
for section in config_base:
|
for section in config_base:
|
||||||
for entry in config_base[section]:
|
for entry in config_base[section]:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "jellyfin-accounts"
|
name = "jellyfin-accounts"
|
||||||
version = "0.2.5"
|
version = "0.2.6"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
description = "A simple account management system for Jellyfin"
|
description = "A simple account management system for Jellyfin"
|
||||||
authors = ["Harvey Tindall <harveyltindall@gmail.com>"]
|
authors = ["Harvey Tindall <harveyltindall@gmail.com>"]
|
||||||
|
Loading…
Reference in New Issue
Block a user