Files
MIND/backend/settings.py
CasVT 99d59cd0e7 Fixed manifest updating for smaller url prefix
When the value of the URL prefix would be changed, if the resulting json would be smaller than the current json, it would create invalid json. Now we wipe the contents before writing to avoid this.
2024-03-10 11:53:12 +01:00

242 lines
5.1 KiB
Python

#-*- coding: utf-8 -*-
"""
Getting and setting settings
"""
import logging
from json import dump, load
from typing import Any
from backend.custom_exceptions import InvalidKeyValue, KeyNotFound
from backend.db import __DATABASE_VERSION__, get_db
from backend.helpers import folder_path
from backend.logging import set_log_level
default_settings = {
'allow_new_accounts': True,
'login_time': 3600,
'login_time_reset': True,
'database_version': __DATABASE_VERSION__,
'host': '0.0.0.0',
'port': 8080,
'url_prefix': '',
'log_level': logging.INFO
}
def _format_setting(key: str, value):
"""Turn python value in to database value.
Args:
key (str): The key of the value.
value (Any): The value itself.
Raises:
InvalidKeyValue: The value is not valid.
Returns:
Any: The converted value.
"""
if key == 'database_version':
try:
value = int(value)
except ValueError:
raise InvalidKeyValue(key, value)
elif key in ('allow_new_accounts', 'login_time_reset'):
if not isinstance(value, bool):
raise InvalidKeyValue(key, value)
value = int(value)
elif key == 'login_time':
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 = ''
elif value:
value = '/' + value.strip('/')
elif key == 'log_level' and not value in (logging.INFO, logging.DEBUG):
raise InvalidKeyValue(key, value)
return 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.
Returns:
Any: The converted value.
"""
if key in ('allow_new_accounts', 'login_time_reset'):
value = value == 1
return value
def get_setting(key: str) -> Any:
"""Get a value from the config.
Args:
key (str): The key of which to get the value.
Raises:
KeyNotFound: Key is not in config.
Returns:
Any: The value of the key.
"""
result = get_db().execute(
"SELECT value FROM config WHERE key = ? LIMIT 1;",
(key,)
).fetchone()
if result is None:
raise KeyNotFound(key)
result = _reverse_format_setting(key, result[0])
return result
def get_admin_settings() -> dict:
"""Get all admin settings
Returns:
dict: The admin settings
"""
return dict((
(key, _reverse_format_setting(key, value))
for key, value in get_db().execute("""
SELECT key, value
FROM config
WHERE
key = 'allow_new_accounts'
OR key = 'login_time'
OR key = 'login_time_reset'
OR key = 'host'
OR key = 'port'
OR key = 'url_prefix'
OR key = 'log_level';
"""
)
))
def set_setting(key: str, value: Any) -> None:
"""Set a value in the config
Args:
key (str): The key for which to set the value
value (Any): The value to give to the key
Raises:
KeyNotFound: The key is not in the config
InvalidKeyValue: The value is not allowed for the key
"""
if not key in (*default_settings, 'database_version'):
raise KeyNotFound(key)
value = _format_setting(key, value)
get_db().execute(
"UPDATE config SET value = ? WHERE key = ?;",
(value, key)
)
if key == 'url_prefix':
update_manifest(value)
elif key == 'log_level':
set_log_level(value)
return
def update_manifest(url_base: str) -> None:
"""Update the url's in the manifest file.
Needs to happen when url base changes.
Args:
url_base (str): The url base to use in the file.
"""
filename = folder_path('frontend', 'static', 'json', 'pwa_manifest.json')
with open(filename, 'r') as f:
manifest = load(f)
manifest['start_url'] = url_base + '/'
manifest['icons'][0]['src'] = f'{url_base}/static/img/favicon.svg'
with open(filename, 'w') as f:
dump(manifest, f, indent=4)
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())
)
update_manifest(hosting_settings['url_prefix'])
return