Files
getDomain/domain-api/app/services/settings_service.py
Your Name 32efff1670 dev
2026-04-16 13:05:07 +08:00

173 lines
6.6 KiB
Python

from __future__ import annotations
import json
from datetime import datetime
from app.core.files import read_json, settings_backup_root, write_json
from app.core.redis_client import get_redis
from app.services.runtime_settings_service import get_runtime_settings, update_runtime_settings
REDIS_KEYS = {
"detect_options": "domain_tool:detect_options",
"proxy_config": "domain_tool:proxy_config",
"thread_count": "domain_tool:thread_count",
}
DETECT_OPTION_KEYS = {
"detect_register",
"detect_wayback",
"detect_chinaz",
"detect_aizhan",
"detect_baidu_site",
"detect_360_site",
"detect_jucha",
"detect_juziseo",
}
def get_settings_payload() -> dict:
detect_options = read_json("detect_options.json", default={})
proxy_config = read_json("proxy_config.json", default={})
thread_count = read_json("thread_count.json", default={"thread_count": "2"})
redis_client = get_redis()
try:
if redis_detect_options := redis_client.get(REDIS_KEYS["detect_options"]):
detect_options = json.loads(redis_detect_options)
if redis_proxy_config := redis_client.get(REDIS_KEYS["proxy_config"]):
proxy_config = json.loads(redis_proxy_config)
if redis_thread_count := redis_client.get(REDIS_KEYS["thread_count"]):
thread_count = {"thread_count": str(redis_thread_count)}
except Exception:
pass
return {
"detect_options": detect_options,
"proxy_config": proxy_config,
"thread_count": int(thread_count.get("thread_count", 2)),
"runtime_settings": get_runtime_settings(),
}
def update_settings_payload(payload: dict) -> dict:
current = get_settings_payload()
detect_options = payload.get("detect_options", current["detect_options"])
proxy_config = payload.get("proxy_config", current["proxy_config"])
thread_count = int(payload.get("thread_count", current["thread_count"]))
runtime_settings = update_runtime_settings(payload.get("runtime_settings", current["runtime_settings"]))
write_json("detect_options.json", detect_options)
write_json("proxy_config.json", proxy_config)
write_json("thread_count.json", {"thread_count": str(thread_count)})
redis_client = get_redis()
try:
redis_client.set(REDIS_KEYS["detect_options"], json.dumps(detect_options, ensure_ascii=False))
redis_client.publish("domain_tool:detect_options:update", json.dumps(detect_options, ensure_ascii=False))
redis_client.set(REDIS_KEYS["proxy_config"], json.dumps(proxy_config, ensure_ascii=False))
redis_client.publish("domain_tool:proxy_config:update", json.dumps(proxy_config, ensure_ascii=False))
redis_client.set(REDIS_KEYS["thread_count"], thread_count)
redis_client.publish("domain_tool:thread_count:update", str(thread_count))
except Exception:
pass
return {
"detect_options": detect_options,
"proxy_config": proxy_config,
"thread_count": thread_count,
"runtime_settings": runtime_settings,
}
def export_settings_snapshot() -> dict:
return {
"schema_version": "1.0",
"exported_at": datetime.now().isoformat(),
"source": "domain-api",
"settings": get_settings_payload(),
}
def import_settings_snapshot(payload: dict) -> dict:
settings_payload = payload.get("settings", payload)
validate_settings_payload(settings_payload)
backup_current_settings("import")
return update_settings_payload(settings_payload)
def validate_settings_payload(payload: dict) -> None:
if not isinstance(payload, dict):
raise ValueError("invalid settings payload")
if "thread_count" in payload:
try:
thread_count = int(payload["thread_count"])
except Exception as exc:
raise ValueError("thread_count must be an integer") from exc
if thread_count < 1 or thread_count > 256:
raise ValueError("thread_count out of range")
if "detect_options" in payload:
detect_options = payload["detect_options"]
if not isinstance(detect_options, dict):
raise ValueError("detect_options must be an object")
detect_order = detect_options.get("detect_order", [])
if detect_order and not isinstance(detect_order, list):
raise ValueError("detect_order must be an array")
if isinstance(detect_order, list):
unknown_keys = [item for item in detect_order if item not in DETECT_OPTION_KEYS]
if unknown_keys:
raise ValueError(f"unknown detect option keys: {', '.join(unknown_keys)}")
if "proxy_config" in payload:
proxy_config = payload["proxy_config"]
if not isinstance(proxy_config, dict):
raise ValueError("proxy_config must be an object")
proxy_urls = proxy_config.get("proxy_urls", [])
if proxy_urls and not isinstance(proxy_urls, list):
raise ValueError("proxy_urls must be an array")
if "runtime_settings" in payload:
runtime_settings = payload["runtime_settings"]
if not isinstance(runtime_settings, dict):
raise ValueError("runtime_settings must be an object")
worker_mode = runtime_settings.get("worker_mode")
if worker_mode and worker_mode not in {"windows-local", "linux-systemd"}:
raise ValueError("worker_mode must be windows-local or linux-systemd")
def backup_current_settings(reason: str = "manual") -> dict:
snapshot = export_settings_snapshot()
snapshot["backup_reason"] = reason
filename = f"settings_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
path = settings_backup_root() / filename
path.write_text(json.dumps(snapshot, ensure_ascii=False, indent=2), encoding="utf-8")
return {
"filename": filename,
"path": str(path),
}
def list_settings_backups(limit: int = 20) -> list[dict]:
root = settings_backup_root()
files = sorted(root.glob("settings_backup_*.json"), key=lambda item: item.stat().st_mtime, reverse=True)
result: list[dict] = []
for item in files[:limit]:
backup_reason = ""
try:
payload = json.loads(item.read_text(encoding="utf-8"))
backup_reason = str(payload.get("backup_reason", ""))
except Exception:
backup_reason = ""
result.append(
{
"filename": item.name,
"path": str(item),
"size": item.stat().st_size,
"modified_at": datetime.fromtimestamp(item.stat().st_mtime).isoformat(),
"backup_reason": backup_reason,
}
)
return result