diff --git a/backend/base/definitions.py b/backend/base/definitions.py index 6cf0eb3..ae637a5 100644 --- a/backend/base/definitions.py +++ b/backend/base/definitions.py @@ -145,6 +145,7 @@ class DataType(BaseEnum): FLOAT = 'decimal number' BOOL = 'bool' INT_ARRAY = 'list of numbers' + STR_ARRAY = 'list of string' NA = 'N/A' diff --git a/backend/internals/settings.py b/backend/internals/settings.py index 792a593..dc3051f 100644 --- a/backend/internals/settings.py +++ b/backend/internals/settings.py @@ -173,6 +173,24 @@ class Settings(metaclass=Singleton): return + def get_default_value(self, key: str) -> Any: + """Get the default value of a setting. + + Args: + key (str): The key of the setting. + + Returns: + Any: The default value. + """ + if not isinstance( + SettingsValues.__dataclass_fields__[key].default_factory, + _MISSING_TYPE + ): + return SettingsValues.__dataclass_fields__[key].default_factory() + + else: + return SettingsValues.__dataclass_fields__[key].default + def reset(self, key: str) -> None: """Reset the value of the key to the default value. @@ -184,17 +202,7 @@ class Settings(metaclass=Singleton): """ LOGGER.debug(f'Setting reset: {key}') - if not isinstance( - SettingsValues.__dataclass_fields__[key].default_factory, - _MISSING_TYPE - ): - self.update({ - key: SettingsValues.__dataclass_fields__[key].default_factory() - }) - else: - self.update({ - key: SettingsValues.__dataclass_fields__[key].default - }) + self.update({key: self.get_default_value(key)}) return diff --git a/frontend/api.py b/frontend/api.py index a2b6628..22ca0d1 100644 --- a/frontend/api.py +++ b/frontend/api.py @@ -530,6 +530,7 @@ def api_admin_settings(inputs: Dict[str, Any]): hosting_changes = any( inputs[s] is not None + and inputs[s] != getattr(settings.sv, s) for s in ('host', 'port', 'url_prefix') ) @@ -547,6 +548,24 @@ def api_admin_settings(inputs: Dict[str, Any]): return return_api({}) + elif request.method == 'DELETE': + hosting_changes = any( + s in inputs["setting_keys"] + and settings.get_default_value(s) != getattr(settings.sv, s) + for s in ('host', 'port', 'url_prefix') + ) + + if hosting_changes: + settings.backup_hosting_settings() + + for setting in inputs["setting_keys"]: + settings.reset(setting) + + if hosting_changes: + Server().restart(StartType.RESTART_HOSTING_CHANGES) + + return return_api({}) + @admin_api.route('/logs', LogfileData) @endpoint_wrapper diff --git a/frontend/input_validation.py b/frontend/input_validation.py index a851970..7b6fd74 100644 --- a/frontend/input_validation.py +++ b/frontend/input_validation.py @@ -26,6 +26,7 @@ from backend.base.definitions import (DataSource, DataType, MindException, TimelessSortingMethod) from backend.base.helpers import folder_path from backend.internals.server import Server +from backend.internals.settings import SettingsValues if TYPE_CHECKING: from flask import Request @@ -409,7 +410,7 @@ class LoginTimeResetVariable(NonRequiredInputVariable): ) -class DBBackupInterval(NonRequiredInputVariable): +class DBBackupIntervalVariable(NonRequiredInputVariable): name = "db_backup_interval" description = "How often to make a backup of the database" data_type = [DataType.INT] @@ -420,7 +421,7 @@ class DBBackupInterval(NonRequiredInputVariable): ) -class DBBackupAmount(NonRequiredInputVariable): +class DBBackupAmountVariable(NonRequiredInputVariable): name = "db_backup_amount" description = "How many backups to keep. The oldest one will be removed if needed." data_type = [DataType.INT] @@ -431,7 +432,7 @@ class DBBackupAmount(NonRequiredInputVariable): ) -class DBBackupFolder(NonRequiredInputVariable): +class DBBackupFolderVariable(NonRequiredInputVariable): name = "db_backup_folder" description = "The folder to store the backups in" data_type = [DataType.STR] @@ -496,6 +497,8 @@ class DatabaseFileVariable(InputVariable): self.value.save(path) self.converted_value = path return True + + self.converted_value = self.value.filename return False @@ -513,6 +516,24 @@ class CopyHostingSettingsVariable(InputVariable): return True +class SettingKeysVariable(InputVariable): + name = "setting_keys" + description = "The keys of the settings for which to reset the value" + data_type = [DataType.STR_ARRAY] + + def validate(self) -> bool: + if not isinstance(self.value, list): + return False + if not self.value: + return False + for v in self.value: + if not isinstance(v, str): + return False + if v not in SettingsValues.__dataclass_fields__: + return False + return True + + # =================== # region Endpoint Datas # =================== @@ -781,10 +802,14 @@ class SettingsData(EndpointData): PortVariable, UrlPrefixVariable, LogLevelVariable, - DBBackupInterval, - DBBackupAmount, - DBBackupFolder + DBBackupIntervalVariable, + DBBackupAmountVariable, + DBBackupFolderVariable ] + ), + delete=( + "Reset the value of setting keys", + [SettingKeysVariable] ) ) @@ -936,7 +961,7 @@ def input_validation() -> Dict[str, Any]: if not value.validate(): if isinstance(value, DatabaseFileVariable): - raise InvalidDatabaseFile(value.value) + raise InvalidDatabaseFile(value.converted_value) elif noted_var.source == DataSource.FILES: raise InvalidKeyValue(noted_var.name, input_value.filename) else: