from __future__ import annotations import json from pathlib import Path from datetime import datetime from app.core.config import settings def domain_root() -> Path: return Path(settings.domain_root) def read_json(relative_path: str, default: dict | list | None = None): path = domain_root() / relative_path if not path.exists(): return {} if default is None else default with path.open("r", encoding="utf-8") as handle: return json.load(handle) def write_json(relative_path: str, payload) -> None: path = domain_root() / relative_path path.parent.mkdir(parents=True, exist_ok=True) with path.open("w", encoding="utf-8") as handle: json.dump(payload, handle, ensure_ascii=False, indent=2) def tail_lines(relative_path: str, max_lines: int = 120) -> list[str]: path = domain_root() / relative_path if not path.exists(): return [] with path.open("r", encoding="utf-8", errors="replace") as handle: return handle.read().splitlines()[-max_lines:] def runtime_root() -> Path: path = Path(__file__).resolve().parents[2] / "runtime" path.mkdir(parents=True, exist_ok=True) return path def read_runtime_json(filename: str, default: dict | list | None = None): path = runtime_root() / filename if not path.exists(): return {} if default is None else default with path.open("r", encoding="utf-8") as handle: return json.load(handle) def write_runtime_json(filename: str, payload) -> None: path = runtime_root() / filename path.parent.mkdir(parents=True, exist_ok=True) with path.open("w", encoding="utf-8") as handle: json.dump(payload, handle, ensure_ascii=False, indent=2, default=str) def exports_root() -> Path: path = runtime_root() / "exports" path.mkdir(parents=True, exist_ok=True) return path def import_root() -> Path: path = runtime_root() / "imports" path.mkdir(parents=True, exist_ok=True) return path def settings_backup_root() -> Path: path = runtime_root() / "settings_backups" path.mkdir(parents=True, exist_ok=True) return path def load_export_records() -> list[dict]: path = runtime_root() / "export_tasks.json" if not path.exists(): return [] with path.open("r", encoding="utf-8") as handle: return json.load(handle) def save_export_record(record: dict) -> None: records = load_export_records() records.insert(0, record) path = runtime_root() / "export_tasks.json" with path.open("w", encoding="utf-8") as handle: json.dump(records[:200], handle, ensure_ascii=False, indent=2, default=str) def load_import_records() -> list[dict]: path = runtime_root() / "import_tasks.json" if not path.exists(): return [] with path.open("r", encoding="utf-8") as handle: return json.load(handle) def save_import_records(records: list[dict]) -> None: path = runtime_root() / "import_tasks.json" with path.open("w", encoding="utf-8") as handle: json.dump(records[:200], handle, ensure_ascii=False, indent=2, default=str) def timestamp_filename(prefix: str, ext: str) -> str: return f"{prefix}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.{ext}"