mirror of
https://github.com/Casvt/MIND.git
synced 2026-02-19 11:54:46 -05:00
Added hosting settings to admin panel
This commit is contained in:
52
MIND.py
52
MIND.py
@@ -20,18 +20,25 @@ from backend.db import (DBConnection, ThreadedTaskDispatcher, close_db,
|
||||
revert_db_import, setup_db)
|
||||
from backend.helpers import RestartVars, check_python_version, folder_path
|
||||
from backend.reminders import ReminderHandler
|
||||
from backend.settings import get_setting, restore_hosting_settings
|
||||
from frontend.api import (APIVariables, admin_api, admin_api_prefix, api,
|
||||
api_prefix, revert_db_thread)
|
||||
api_prefix, revert_db_thread, revert_hosting_thread)
|
||||
from frontend.ui import UIVariables, ui
|
||||
|
||||
#=============================
|
||||
# WARNING:
|
||||
# These settings have moved into the admin panel. Their current value has been
|
||||
# taken over. The values will from now on be ignored, and the variables will
|
||||
# be deleted next version.
|
||||
HOST = '0.0.0.0'
|
||||
PORT = '8080'
|
||||
URL_PREFIX = '' # Must either be empty or start with '/' e.g. '/mind'
|
||||
#=============================
|
||||
|
||||
LOGGING_LEVEL = logging.INFO
|
||||
THREADS = 10
|
||||
DB_FILENAME = 'db', 'MIND.db'
|
||||
|
||||
UIVariables.url_prefix = URL_PREFIX
|
||||
logging.basicConfig(
|
||||
level=LOGGING_LEVEL,
|
||||
format='[%(asctime)s][%(threadName)s][%(levelname)s] %(message)s',
|
||||
@@ -53,11 +60,6 @@ def _create_app() -> Flask:
|
||||
app.config['SECRET_KEY'] = urandom(32)
|
||||
app.config['JSONIFY_PRETTYPRINT_REGULAR'] = True
|
||||
app.config['JSON_SORT_KEYS'] = False
|
||||
app.config['APPLICATION_ROOT'] = URL_PREFIX
|
||||
app.wsgi_app = DispatcherMiddleware(
|
||||
Flask(__name__),
|
||||
{URL_PREFIX: app.wsgi_app}
|
||||
)
|
||||
|
||||
# Add error handlers
|
||||
@app.errorhandler(400)
|
||||
@@ -87,6 +89,21 @@ def _create_app() -> Flask:
|
||||
|
||||
return app
|
||||
|
||||
def _set_url_prefix(app: Flask, url_prefix: str) -> None:
|
||||
"""Change the URL prefix of the server.
|
||||
|
||||
Args:
|
||||
app (Flask): The `Flask` instance to change the URL prefix of.
|
||||
url_prefix (str): The desired URL prefix to set it to.
|
||||
"""
|
||||
app.config["APPLICATION_ROOT"] = url_prefix
|
||||
app.wsgi_app = DispatcherMiddleware(
|
||||
Flask(__name__),
|
||||
{url_prefix: app.wsgi_app}
|
||||
)
|
||||
UIVariables.url_prefix = url_prefix
|
||||
return
|
||||
|
||||
def _handle_flags(flag: Union[None, str]) -> None:
|
||||
"""Run flag specific actions on startup.
|
||||
|
||||
@@ -97,6 +114,10 @@ def _handle_flags(flag: Union[None, str]) -> None:
|
||||
logging.info('Starting timer for database import')
|
||||
revert_db_thread.start()
|
||||
|
||||
elif flag == RestartVars.HOST_CHANGE:
|
||||
logging.info('Starting timer for hosting changes')
|
||||
revert_hosting_thread.start()
|
||||
|
||||
return
|
||||
|
||||
def _handle_flags_pre_restart(flag: Union[None, str]) -> None:
|
||||
@@ -108,6 +129,11 @@ def _handle_flags_pre_restart(flag: Union[None, str]) -> None:
|
||||
if flag == RestartVars.DB_IMPORT:
|
||||
revert_db_import(swap=True)
|
||||
|
||||
elif flag == RestartVars.HOST_CHANGE:
|
||||
with Flask(__name__).app_context():
|
||||
restore_hosting_settings()
|
||||
close_db()
|
||||
|
||||
return
|
||||
|
||||
def MIND() -> None:
|
||||
@@ -134,6 +160,12 @@ def MIND() -> None:
|
||||
reminder_handler = ReminderHandler(app.app_context)
|
||||
with app.app_context():
|
||||
setup_db()
|
||||
|
||||
host = get_setting("host")
|
||||
port = get_setting("port")
|
||||
url_prefix = get_setting("url_prefix")
|
||||
_set_url_prefix(app, url_prefix)
|
||||
|
||||
reminder_handler.find_next_reminder()
|
||||
|
||||
# Create waitress server and run
|
||||
@@ -142,12 +174,12 @@ def MIND() -> None:
|
||||
server = create_server(
|
||||
app,
|
||||
_dispatcher=dispatcher,
|
||||
host=HOST,
|
||||
port=PORT,
|
||||
host=host,
|
||||
port=port,
|
||||
threads=THREADS
|
||||
)
|
||||
APIVariables.server_instance = server
|
||||
logging.info(f'MIND running on http://{HOST}:{PORT}{URL_PREFIX}')
|
||||
logging.info(f'MIND running on http://{host}:{port}{url_prefix}')
|
||||
# =================
|
||||
server.run()
|
||||
# =================
|
||||
|
||||
@@ -21,7 +21,7 @@ from backend.custom_exceptions import (AccessUnauthorized, InvalidDatabaseFile,
|
||||
UserNotFound)
|
||||
from backend.helpers import RestartVars
|
||||
|
||||
__DATABASE_VERSION__ = 8
|
||||
__DATABASE_VERSION__ = 9
|
||||
__DATEBASE_NAME_ORIGINAL__ = "MIND_original.db"
|
||||
|
||||
class DB_Singleton(type):
|
||||
@@ -271,6 +271,19 @@ def migrate_db(current_db_version: int) -> None:
|
||||
WHERE username = 'admin';
|
||||
""")
|
||||
|
||||
current_db_version = 8
|
||||
|
||||
if current_db_version == 8:
|
||||
# V8 -> V9
|
||||
from backend.settings import set_setting
|
||||
from MIND import HOST, PORT, URL_PREFIX
|
||||
|
||||
set_setting('host', HOST)
|
||||
set_setting('port', int(PORT))
|
||||
set_setting('url_prefix', URL_PREFIX)
|
||||
|
||||
current_db_version = 9
|
||||
|
||||
return
|
||||
|
||||
def setup_db() -> None:
|
||||
|
||||
@@ -112,3 +112,4 @@ class RepeatQuantity(BaseEnum):
|
||||
|
||||
class RestartVars(BaseEnum):
|
||||
DB_IMPORT = "db_import"
|
||||
HOST_CHANGE = "host_change"
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
Getting and setting settings
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from backend.custom_exceptions import InvalidKeyValue, KeyNotFound
|
||||
from backend.db import __DATABASE_VERSION__, get_db
|
||||
|
||||
@@ -11,21 +13,24 @@ default_settings = {
|
||||
'allow_new_accounts': True,
|
||||
'login_time': 3600,
|
||||
'login_time_reset': True,
|
||||
'database_version': __DATABASE_VERSION__
|
||||
'database_version': __DATABASE_VERSION__,
|
||||
'host': '0.0.0.0',
|
||||
'port': 8080,
|
||||
'url_prefix': ''
|
||||
}
|
||||
|
||||
def _format_setting(key: str, value):
|
||||
"""Turn python value in to database value
|
||||
"""Turn python value in to database value.
|
||||
|
||||
Args:
|
||||
key (str): The key of the value
|
||||
value (Any): The value itself
|
||||
key (str): The key of the value.
|
||||
value (Any): The value itself.
|
||||
|
||||
Raises:
|
||||
InvalidKeyValue: The value is not valid
|
||||
InvalidKeyValue: The value is not valid.
|
||||
|
||||
Returns:
|
||||
Any: The converted value
|
||||
Any: The converted value.
|
||||
"""
|
||||
if key == 'database_version':
|
||||
try:
|
||||
@@ -42,33 +47,48 @@ def _format_setting(key: str, value):
|
||||
if not isinstance(value, int) or not 60 <= value <= 2592000:
|
||||
raise InvalidKeyValue(key, value)
|
||||
|
||||
elif key == 'host':
|
||||
if not isinstance(value, str):
|
||||
raise InvalidKeyValue(key, value)
|
||||
|
||||
elif key == 'port':
|
||||
if not isinstance(value, int) or not 1 <= value <= 65535:
|
||||
raise InvalidKeyValue(key, value)
|
||||
|
||||
elif key == 'url_prefix':
|
||||
if not isinstance(value, str):
|
||||
raise InvalidKeyValue(key, value)
|
||||
|
||||
if value:
|
||||
value = '/' + value.strip('/')
|
||||
|
||||
return value
|
||||
|
||||
def _reverse_format_setting(key: str, value):
|
||||
"""Turn database value in to python value
|
||||
def _reverse_format_setting(key: str, value: Any) -> Any:
|
||||
"""Turn database value in to python value.
|
||||
|
||||
Args:
|
||||
key (str): The key of the value
|
||||
value (Any): The value itself
|
||||
key (str): The key of the value.
|
||||
value (Any): The value itself.
|
||||
|
||||
Returns:
|
||||
Any: The converted value
|
||||
Any: The converted value.
|
||||
"""
|
||||
if key in ('allow_new_accounts', 'login_time_reset'):
|
||||
value = value == 1
|
||||
return value
|
||||
|
||||
def get_setting(key: str):
|
||||
"""Get a value from the config
|
||||
def get_setting(key: str) -> Any:
|
||||
"""Get a value from the config.
|
||||
|
||||
Args:
|
||||
key (str): The key of which to get the value
|
||||
key (str): The key of which to get the value.
|
||||
|
||||
Raises:
|
||||
KeyNotFound: Key is not in config
|
||||
KeyNotFound: Key is not in config.
|
||||
|
||||
Returns:
|
||||
Any: The value of the key
|
||||
Any: The value of the key.
|
||||
"""
|
||||
result = get_db().execute(
|
||||
"SELECT value FROM config WHERE key = ? LIMIT 1;",
|
||||
@@ -95,12 +115,15 @@ def get_admin_settings() -> dict:
|
||||
WHERE
|
||||
key = 'allow_new_accounts'
|
||||
OR key = 'login_time'
|
||||
OR key = 'login_time_reset';
|
||||
OR key = 'login_time_reset'
|
||||
OR key = 'host'
|
||||
OR key = 'port'
|
||||
OR key = 'url_prefix';
|
||||
"""
|
||||
)
|
||||
))
|
||||
|
||||
def set_setting(key: str, value) -> None:
|
||||
def set_setting(key: str, value: Any) -> None:
|
||||
"""Set a value in the config
|
||||
|
||||
Args:
|
||||
@@ -121,3 +144,55 @@ def set_setting(key: str, value) -> None:
|
||||
(value, key)
|
||||
)
|
||||
return
|
||||
|
||||
def backup_hosting_settings() -> None:
|
||||
"""Copy current hosting settings to backup values.
|
||||
"""
|
||||
cursor = get_db()
|
||||
hosting_settings = dict(cursor.execute("""
|
||||
SELECT key, value
|
||||
FROM config
|
||||
WHERE key = 'host'
|
||||
OR key = 'port'
|
||||
OR key = 'url_prefix'
|
||||
LIMIT 3;
|
||||
"""
|
||||
))
|
||||
hosting_settings = {f'{k}_backup': v for k, v in hosting_settings.items()}
|
||||
|
||||
cursor.executemany("""
|
||||
INSERT INTO config(key, value)
|
||||
VALUES (?, ?)
|
||||
ON CONFLICT(key) DO
|
||||
UPDATE
|
||||
SET value = ?;
|
||||
""",
|
||||
((k, v, v) for k, v in hosting_settings.items())
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
def restore_hosting_settings() -> None:
|
||||
"""Copy the hosting settings from the backup over to the main keys.
|
||||
"""
|
||||
cursor = get_db()
|
||||
hosting_settings = dict(cursor.execute("""
|
||||
SELECT key, value
|
||||
FROM config
|
||||
WHERE key = 'host_backup'
|
||||
OR key = 'port_backup'
|
||||
OR key = 'url_prefix_backup'
|
||||
LIMIT 3;
|
||||
"""
|
||||
))
|
||||
if len(hosting_settings) < 3:
|
||||
return
|
||||
|
||||
hosting_settings = {k.split('_backup')[0]: v for k, v in hosting_settings.items()}
|
||||
|
||||
cursor.executemany(
|
||||
"UPDATE config SET value = ? WHERE key = ?",
|
||||
((v, k) for k, v in hosting_settings.items())
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
@@ -24,25 +24,28 @@ from backend.custom_exceptions import (AccessUnauthorized, APIKeyExpired,
|
||||
UsernameInvalid, UsernameTaken,
|
||||
UserNotFound)
|
||||
from backend.db import get_db, import_db, revert_db_import
|
||||
from backend.helpers import folder_path
|
||||
from backend.helpers import RestartVars, folder_path
|
||||
from backend.notification_service import get_apprise_services
|
||||
from backend.settings import get_admin_settings, get_setting, set_setting
|
||||
from backend.settings import (backup_hosting_settings, get_admin_settings,
|
||||
get_setting, set_setting)
|
||||
from backend.users import Users
|
||||
from frontend.input_validation import (AllowNewAccountsVariable, ColorVariable,
|
||||
DatabaseFileVariable,
|
||||
EditNotificationServicesVariable,
|
||||
EditTimeVariable, EditTitleVariable,
|
||||
EditURLVariable, LoginTimeResetVariable,
|
||||
EditURLVariable, HostVariable,
|
||||
LoginTimeResetVariable,
|
||||
LoginTimeVariable, Method, Methods,
|
||||
NewPasswordVariable,
|
||||
NotificationServicesVariable,
|
||||
PasswordCreateVariable,
|
||||
PasswordVariable, QueryVariable,
|
||||
RepeatIntervalVariable,
|
||||
PasswordVariable, PortVariable,
|
||||
QueryVariable, RepeatIntervalVariable,
|
||||
RepeatQuantityVariable, SortByVariable,
|
||||
TextVariable, TimelessSortByVariable,
|
||||
TimeVariable, TitleVariable,
|
||||
URLVariable, UsernameCreateVariable,
|
||||
UrlPrefixVariable, URLVariable,
|
||||
UsernameCreateVariable,
|
||||
UsernameVariable, WeekDaysVariable,
|
||||
admin_api, admin_api_prefix, api,
|
||||
api_prefix, get_api_docs,
|
||||
@@ -93,12 +96,22 @@ def revert_db() -> None:
|
||||
restart_server()
|
||||
return
|
||||
|
||||
def revert_hosting() -> None:
|
||||
"""Revert the hosting changes.
|
||||
"""
|
||||
logging.warning(f'Timer for hosting changes expired; reverting back to original settings')
|
||||
APIVariables.handle_flags = True
|
||||
restart_server()
|
||||
return
|
||||
|
||||
shutdown_server_thread = Timer(1.0, shutdown_server)
|
||||
shutdown_server_thread.name = "InternalStateHandler"
|
||||
restart_server_thread = Timer(1.0, restart_server)
|
||||
restart_server_thread.name = "InternalStateHandler"
|
||||
revert_db_thread = Timer(60.0, revert_db)
|
||||
revert_db_thread.name = "DatabaseImportHandler"
|
||||
revert_hosting_thread = Timer(60.0, revert_hosting)
|
||||
revert_hosting_thread.name = "HostingHandler"
|
||||
|
||||
@dataclass
|
||||
class ApiKeyEntry:
|
||||
@@ -211,6 +224,10 @@ def api_login(inputs: Dict[str, str]):
|
||||
revert_db_thread.cancel()
|
||||
revert_db_import(swap=False)
|
||||
|
||||
elif user.admin and revert_hosting_thread.is_alive():
|
||||
logging.info('Timer for hosting changes diffused')
|
||||
revert_hosting_thread.cancel()
|
||||
|
||||
# Generate an API key until one
|
||||
# is generated that isn't used already
|
||||
while True:
|
||||
@@ -737,7 +754,8 @@ def api_settings():
|
||||
),
|
||||
put=Method(
|
||||
vars=[AllowNewAccountsVariable, LoginTimeVariable,
|
||||
LoginTimeResetVariable],
|
||||
LoginTimeResetVariable, HostVariable, PortVariable,
|
||||
UrlPrefixVariable],
|
||||
description='Edit the admin settings'
|
||||
)
|
||||
),
|
||||
@@ -749,14 +767,24 @@ def api_admin_settings(inputs: Dict[str, Any]):
|
||||
return return_api(get_admin_settings())
|
||||
|
||||
elif request.method == 'PUT':
|
||||
values = {
|
||||
'allow_new_accounts': inputs['allow_new_accounts'],
|
||||
'login_time': inputs['login_time'],
|
||||
'login_time_reset': inputs['login_time_reset']
|
||||
}
|
||||
logging.info(f'Submitting admin settings: {values}')
|
||||
for k, v in values.items():
|
||||
set_setting(k, v)
|
||||
logging.info(f'Submitting admin settings: {inputs}')
|
||||
|
||||
hosting_changes = any(
|
||||
inputs[s] is not None
|
||||
for s in ('host', 'port', 'url_prefix')
|
||||
)
|
||||
|
||||
if hosting_changes:
|
||||
backup_hosting_settings()
|
||||
|
||||
for k, v in inputs.items():
|
||||
if v is not None:
|
||||
set_setting(k, v)
|
||||
|
||||
if hosting_changes:
|
||||
APIVariables.restart_args = [RestartVars.HOST_CHANGE.value]
|
||||
restart_server_thread.start()
|
||||
|
||||
return return_api({})
|
||||
|
||||
@admin_api.route(
|
||||
|
||||
@@ -378,23 +378,38 @@ class AdminSettingsVariable(BaseInputVariable):
|
||||
return True
|
||||
|
||||
|
||||
class AllowNewAccountsVariable(AdminSettingsVariable):
|
||||
class AllowNewAccountsVariable(NonRequiredVersion, AdminSettingsVariable):
|
||||
name = 'allow_new_accounts'
|
||||
description = ('Whether or not to allow users to register a new account. '
|
||||
+ 'The admin can always add a new account.')
|
||||
|
||||
|
||||
class LoginTimeVariable(AdminSettingsVariable):
|
||||
class LoginTimeVariable(NonRequiredVersion, AdminSettingsVariable):
|
||||
name = 'login_time'
|
||||
description = ('How long a user stays logged in, in seconds. '
|
||||
+ 'Between 1 min and 1 month (60 <= sec <= 2592000)')
|
||||
|
||||
|
||||
class LoginTimeResetVariable(AdminSettingsVariable):
|
||||
class LoginTimeResetVariable(NonRequiredVersion, AdminSettingsVariable):
|
||||
name = 'login_time_reset'
|
||||
description = 'If the Login Time timer should reset with each API request.'
|
||||
|
||||
|
||||
class HostVariable(NonRequiredVersion, AdminSettingsVariable):
|
||||
name = 'host'
|
||||
description = 'The IP to bind to. Use 0.0.0.0 to bind to all addresses.'
|
||||
|
||||
|
||||
class PortVariable(NonRequiredVersion, AdminSettingsVariable):
|
||||
name = 'port'
|
||||
description = 'The port to listen on.'
|
||||
|
||||
|
||||
class UrlPrefixVariable(NonRequiredVersion, AdminSettingsVariable):
|
||||
name = 'url_prefix'
|
||||
description = 'The base url to run on. Useful for reverse proxies. Empty string to disable.'
|
||||
|
||||
|
||||
class DatabaseFileVariable(BaseInputVariable):
|
||||
name = 'file'
|
||||
description = 'The MIND database file'
|
||||
|
||||
@@ -52,7 +52,8 @@ main {
|
||||
padding-top: var(--nav-width);
|
||||
}
|
||||
|
||||
#settings-form {
|
||||
#settings-form,
|
||||
#hosting-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@@ -74,7 +75,8 @@ h2 {
|
||||
overflow-x: auto;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.settings-table {
|
||||
@@ -96,6 +98,7 @@ h2 {
|
||||
.settings-table td:first-child {
|
||||
width: 50%;
|
||||
padding-right: var(--middle-spacing);
|
||||
padding-top: .55rem;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@@ -155,14 +158,14 @@ h2 {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#save-hosting-button,
|
||||
#add-user-button,
|
||||
#download-db-button,
|
||||
#upload-db-button,
|
||||
#restart-button,
|
||||
#shutdown-button {
|
||||
width: min(15rem, 100%);
|
||||
height: 2rem;
|
||||
|
||||
|
||||
padding: .5rem 1rem;
|
||||
border-radius: 4px;
|
||||
background-color: var(--color-gray);
|
||||
@@ -170,11 +173,12 @@ h2 {
|
||||
box-shadow: var(--default-shadow);
|
||||
}
|
||||
|
||||
#download-db-button,
|
||||
#upload-db-button,
|
||||
#restart-button,
|
||||
#shutdown-button {
|
||||
height: unset;
|
||||
#save-hosting-button {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
#add-user-button {
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
#add-user-button > svg {
|
||||
@@ -230,6 +234,7 @@ h2 {
|
||||
padding: .25rem;
|
||||
}
|
||||
|
||||
#hosting-form > p,
|
||||
#upload-database-form p {
|
||||
max-width: 50rem;
|
||||
margin-inline: auto;
|
||||
@@ -238,10 +243,6 @@ h2 {
|
||||
}
|
||||
|
||||
@media (max-width: 40rem) {
|
||||
#settings-form {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
padding-inline: 0;
|
||||
@@ -249,7 +250,7 @@ h2 {
|
||||
|
||||
.settings-table-container,
|
||||
.user-table-container {
|
||||
justify-content: left;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.settings-table tbody {
|
||||
|
||||
@@ -4,6 +4,14 @@ const setting_inputs = {
|
||||
login_time_reset: document.querySelector('#login-time-reset-input')
|
||||
};
|
||||
|
||||
const hosting_inputs = {
|
||||
form: document.querySelector('#hosting-form'),
|
||||
host: document.querySelector('#host-input'),
|
||||
port: document.querySelector('#port-input'),
|
||||
url_prefix: document.querySelector('#url-prefix-input'),
|
||||
submit: document.querySelector('#save-hosting-button')
|
||||
};
|
||||
|
||||
const user_inputs = {
|
||||
username: document.querySelector('#new-username-input'),
|
||||
password: document.querySelector('#new-password-input')
|
||||
@@ -44,6 +52,9 @@ function loadSettings() {
|
||||
setting_inputs.allow_new_accounts.checked = json.result.allow_new_accounts;
|
||||
setting_inputs.login_time.value = Math.round(json.result.login_time / 60);
|
||||
setting_inputs.login_time_reset.value = json.result.login_time_reset.toString();
|
||||
hosting_inputs.host.value = json.result.host;
|
||||
hosting_inputs.port.value = json.result.port;
|
||||
hosting_inputs.url_prefix.value = json.result.url_prefix;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -69,6 +80,34 @@ function submitSettings() {
|
||||
});
|
||||
};
|
||||
|
||||
function submitHostingSettings() {
|
||||
hosting_inputs.submit.innerText = 'Restarting';
|
||||
const data = {
|
||||
host: hosting_inputs.host.value,
|
||||
port: parseInt(hosting_inputs.port.value),
|
||||
url_prefix: hosting_inputs.url_prefix.value
|
||||
};
|
||||
fetch(`${url_prefix}/api/admin/settings?api_key=${api_key}`, {
|
||||
'method': 'PUT',
|
||||
'headers': {'Content-Type': 'application/json'},
|
||||
'body': JSON.stringify(data)
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
if (json.error !== null)
|
||||
return Promise.reject(json)
|
||||
|
||||
setTimeout(
|
||||
() => window.location.reload(),
|
||||
1000
|
||||
)
|
||||
})
|
||||
.catch(json => {
|
||||
if (['ApiKeyInvalid', 'ApiKeyExpired'].includes(json.error))
|
||||
window.location.href = `${url_prefix}/`;
|
||||
});
|
||||
};
|
||||
|
||||
function toggleAddUser() {
|
||||
const el = document.querySelector('#add-user-row');
|
||||
if (el.classList.contains('hidden')) {
|
||||
@@ -244,6 +283,7 @@ loadUsers();
|
||||
|
||||
document.querySelector('#logout-button').onclick = e => logout();
|
||||
document.querySelector('#settings-form').action = 'javascript:submitSettings();';
|
||||
hosting_inputs.form.action = 'javascript:submitHostingSettings();';
|
||||
document.querySelector('#add-user-button').onclick = e => toggleAddUser();
|
||||
document.querySelector('#add-user-form').action = 'javascript:addUser()';
|
||||
document.querySelector('#download-db-button').onclick = e =>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<td><label for="login-time-input">Login Time</label></td>
|
||||
<td>
|
||||
<div class="number-input">
|
||||
<input type="number" id="login-time-input" min="1" max="43200">
|
||||
<input type="number" id="login-time-input" min="1" max="43200" required>
|
||||
<p>Min</p>
|
||||
</div>
|
||||
<p>For how long users stay logged in before having to authenticate again. Between 1 minute and 1 month.</p>
|
||||
@@ -74,6 +74,38 @@
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
<form id="hosting-form">
|
||||
<h2>Hosting</h2>
|
||||
<div class="settings-table-container">
|
||||
<table class="settings-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><label for="host-input">Host</label></td>
|
||||
<td>
|
||||
<input type="text" id="host-input" required>
|
||||
<p>Valid IPv4 address (default is '0.0.0.0' for all available interfaces)</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="port-input">Port</label></td>
|
||||
<td>
|
||||
<input type="number" id="port-input" min="1" max="65535" required>
|
||||
<p>The port used to access the web UI (default is '8080')</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="url-prefix-input">URL Prefix</label></td>
|
||||
<td>
|
||||
<input type="text" id="url-prefix-input">
|
||||
<p>For reverse proxy support (default is empty).</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<button type="submit" id="save-hosting-button">Save and Restart</button>
|
||||
</div>
|
||||
<p>IMPORTANT: After saving the hosting settings, it is required to log into the admin panel within 1 minute (60 seconds) in order to keep the new hosting settings. Otherwise, MIND will revert the changes and go back to the old hosting settings.</p>
|
||||
</form>
|
||||
<h2>User Management</h2>
|
||||
<div class="add-user-container">
|
||||
<button id="add-user-button">
|
||||
|
||||
Reference in New Issue
Block a user