mirror of
https://github.com/hrfee/jellyfin-accounts.git
synced 2024-12-22 09:00:14 +00:00
Merge pull request #26 from hrfee/dynamic-settings
Add live reloading to some options, email fix
This commit is contained in:
commit
ac500e14cd
@ -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