fix config: handle stringified JSON in API requests
ConfigUpdate fields (scheduler, logging, backup, nfo, other) arrive as strings from frontend. Parse JSON or ast.literal_eval before validation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -47,8 +47,19 @@ async def update_config(
|
|||||||
from src.config.settings import settings as app_settings
|
from src.config.settings import settings as app_settings
|
||||||
|
|
||||||
anime_dir_changed = False
|
anime_dir_changed = False
|
||||||
if update.other and update.other.get("anime_directory"):
|
other_data = update.other
|
||||||
anime_dir = update.other.get("anime_directory")
|
if isinstance(other_data, str):
|
||||||
|
try:
|
||||||
|
import ast
|
||||||
|
other_data = ast.literal_eval(other_data)
|
||||||
|
except (ValueError, SyntaxError):
|
||||||
|
try:
|
||||||
|
import json
|
||||||
|
other_data = json.loads(other_data)
|
||||||
|
except (ValueError, json.JSONDecodeError):
|
||||||
|
other_data = None
|
||||||
|
if other_data and other_data.get("anime_directory"):
|
||||||
|
anime_dir = other_data.get("anime_directory")
|
||||||
if anime_dir and not app_settings.anime_directory:
|
if anime_dir and not app_settings.anime_directory:
|
||||||
app_settings.anime_directory = str(anime_dir)
|
app_settings.anime_directory = str(anime_dir)
|
||||||
anime_dir_changed = True
|
anime_dir_changed = True
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
from typing import Dict, List, Optional
|
import ast
|
||||||
|
import json
|
||||||
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
from pydantic import BaseModel, Field, ValidationError, field_validator
|
from pydantic import BaseModel, Field, ValidationError, field_validator
|
||||||
|
|
||||||
@@ -244,12 +246,12 @@ class AppConfig(BaseModel):
|
|||||||
class ConfigUpdate(BaseModel):
|
class ConfigUpdate(BaseModel):
|
||||||
name: Optional[str] = None
|
name: Optional[str] = None
|
||||||
data_dir: Optional[str] = None
|
data_dir: Optional[str] = None
|
||||||
scheduler: Optional[SchedulerConfig] = None
|
scheduler: Optional[Any] = None
|
||||||
logging: Optional[LoggingConfig] = None
|
logging: Optional[Any] = None
|
||||||
backup: Optional[BackupConfig] = None
|
backup: Optional[Any] = None
|
||||||
nfo: Optional[NFOConfig] = None
|
nfo: Optional[Any] = None
|
||||||
scan_key_overrides: Optional[Dict[str, str]] = None
|
scan_key_overrides: Optional[Dict[str, str]] = None
|
||||||
other: Optional[Dict[str, object]] = None
|
other: Optional[Any] = None
|
||||||
|
|
||||||
def apply_to(self, current: AppConfig) -> AppConfig:
|
def apply_to(self, current: AppConfig) -> AppConfig:
|
||||||
"""Return a new AppConfig with updates applied to the current config.
|
"""Return a new AppConfig with updates applied to the current config.
|
||||||
@@ -257,18 +259,61 @@ class ConfigUpdate(BaseModel):
|
|||||||
Performs a shallow merge for `other`.
|
Performs a shallow merge for `other`.
|
||||||
"""
|
"""
|
||||||
data = current.model_dump()
|
data = current.model_dump()
|
||||||
|
if self.name is not None:
|
||||||
|
data["name"] = self.name
|
||||||
|
if self.data_dir is not None:
|
||||||
|
data["data_dir"] = self.data_dir
|
||||||
if self.scheduler is not None:
|
if self.scheduler is not None:
|
||||||
data["scheduler"] = self.scheduler.model_dump()
|
scheduler_data = self.scheduler
|
||||||
|
if isinstance(scheduler_data, str):
|
||||||
|
try:
|
||||||
|
scheduler_data = json.loads(scheduler_data)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
scheduler_data = ast.literal_eval(scheduler_data)
|
||||||
|
if isinstance(scheduler_data, dict):
|
||||||
|
scheduler_data = SchedulerConfig(**scheduler_data)
|
||||||
|
data["scheduler"] = scheduler_data.model_dump()
|
||||||
if self.logging is not None:
|
if self.logging is not None:
|
||||||
data["logging"] = self.logging.model_dump()
|
logging_data = self.logging
|
||||||
|
if isinstance(logging_data, str):
|
||||||
|
try:
|
||||||
|
logging_data = json.loads(logging_data)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
logging_data = ast.literal_eval(logging_data)
|
||||||
|
if isinstance(logging_data, dict):
|
||||||
|
logging_data = LoggingConfig(**logging_data)
|
||||||
|
data["logging"] = logging_data.model_dump()
|
||||||
if self.backup is not None:
|
if self.backup is not None:
|
||||||
data["backup"] = self.backup.model_dump()
|
backup_data = self.backup
|
||||||
|
if isinstance(backup_data, str):
|
||||||
|
try:
|
||||||
|
backup_data = json.loads(backup_data)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
backup_data = ast.literal_eval(backup_data)
|
||||||
|
if isinstance(backup_data, dict):
|
||||||
|
backup_data = BackupConfig(**backup_data)
|
||||||
|
data["backup"] = backup_data.model_dump()
|
||||||
if self.nfo is not None:
|
if self.nfo is not None:
|
||||||
data["nfo"] = self.nfo.model_dump()
|
nfo_data = self.nfo
|
||||||
|
if isinstance(nfo_data, str):
|
||||||
|
try:
|
||||||
|
nfo_data = json.loads(nfo_data)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
nfo_data = ast.literal_eval(nfo_data)
|
||||||
|
if isinstance(nfo_data, dict):
|
||||||
|
nfo_data = NFOConfig(**nfo_data)
|
||||||
|
data["nfo"] = nfo_data.model_dump()
|
||||||
if self.scan_key_overrides is not None:
|
if self.scan_key_overrides is not None:
|
||||||
data["scan_key_overrides"] = self.scan_key_overrides
|
data["scan_key_overrides"] = self.scan_key_overrides
|
||||||
if self.other is not None:
|
if self.other is not None:
|
||||||
merged = dict(current.other or {})
|
merged = dict(current.other or {})
|
||||||
merged.update(self.other)
|
other_data = self.other
|
||||||
|
if isinstance(other_data, str):
|
||||||
|
try:
|
||||||
|
other_data = json.loads(other_data)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
other_data = ast.literal_eval(other_data)
|
||||||
|
if isinstance(other_data, dict):
|
||||||
|
merged.update(other_data)
|
||||||
data["other"] = merged
|
data["other"] = merged
|
||||||
return AppConfig(**data)
|
return AppConfig(**data)
|
||||||
|
|||||||
Reference in New Issue
Block a user