This commit is contained in:
Your Name
2026-04-16 21:35:47 +08:00
parent ff32aa50bf
commit ebf632e651
86 changed files with 14097 additions and 585 deletions

View File

@@ -3,6 +3,7 @@ from __future__ import annotations
import json
from datetime import datetime
from app.core.config import settings as app_settings
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
@@ -12,6 +13,8 @@ REDIS_KEYS = {
"detect_options": "domain_tool:detect_options",
"proxy_config": "domain_tool:proxy_config",
"thread_count": "domain_tool:thread_count",
"node_thread_counts": "domain_tool:node_thread_counts",
"credentials": "domain_tool:credentials",
}
DETECT_OPTION_KEYS = {
@@ -26,10 +29,90 @@ DETECT_OPTION_KEYS = {
}
def _normalize_thread_count(value: object, *, field_name: str = "thread_count") -> int:
try:
thread_count = int(value)
except Exception as exc:
raise ValueError(f"{field_name} must be an integer") from exc
if thread_count < 1 or thread_count > 256:
raise ValueError(f"{field_name} out of range")
return thread_count
def _normalize_node_thread_counts(payload: object) -> dict[str, int]:
if payload in (None, ""):
return {}
if not isinstance(payload, dict):
raise ValueError("node_thread_counts must be an object")
normalized: dict[str, int] = {}
for raw_node_code, raw_thread_count in payload.items():
node_code = str(raw_node_code or "").strip()
if not node_code:
raise ValueError("node_thread_counts contains empty node code")
normalized[node_code] = _normalize_thread_count(raw_thread_count, field_name=f"node_thread_counts.{node_code}")
return normalized
def _load_thread_count_config() -> tuple[int, dict[str, int]]:
thread_count_payload = read_json("thread_count.json", default={"thread_count": "2"})
node_thread_counts_payload = read_json("node_thread_counts.json", default={})
try:
default_thread_count = _normalize_thread_count(thread_count_payload.get("thread_count", 2))
except ValueError:
default_thread_count = 2
try:
node_thread_counts = _normalize_node_thread_counts(node_thread_counts_payload)
except ValueError:
node_thread_counts = {}
redis_client = get_redis()
try:
if redis_thread_count := redis_client.get(REDIS_KEYS["thread_count"]):
try:
default_thread_count = _normalize_thread_count(redis_thread_count)
except ValueError:
pass
if redis_node_thread_counts := redis_client.get(REDIS_KEYS["node_thread_counts"]):
try:
node_thread_counts = _normalize_node_thread_counts(json.loads(redis_node_thread_counts))
except ValueError:
pass
except Exception:
pass
return default_thread_count, node_thread_counts
def resolve_thread_count(node_code: str | None = None, settings_payload: dict | None = None) -> dict:
payload = settings_payload or get_settings_payload()
default_thread_count = int(payload.get("thread_count", 2))
node_thread_counts = _normalize_node_thread_counts(payload.get("node_thread_counts", {}))
normalized_node_code = str(node_code or app_settings.node_code or "").strip()
override_thread_count = None
source = "default"
effective_thread_count = default_thread_count
if normalized_node_code and normalized_node_code in node_thread_counts:
override_thread_count = node_thread_counts[normalized_node_code]
effective_thread_count = override_thread_count
source = "node_override"
return {
"node_code": normalized_node_code,
"default_thread_count": default_thread_count,
"effective_thread_count": effective_thread_count,
"override_thread_count": override_thread_count,
"source": source,
"node_thread_counts": node_thread_counts,
}
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"})
thread_count, node_thread_counts = _load_thread_count_config()
redis_client = get_redis()
try:
@@ -37,29 +120,84 @@ def get_settings_payload() -> dict:
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)),
"thread_count": thread_count,
"node_thread_counts": node_thread_counts,
"current_node_code": app_settings.node_code,
"runtime_settings": get_runtime_settings(),
}
def get_credentials_payload() -> dict:
credentials = read_json(
"credentials.json",
default={
"juming": {"email": "", "password": ""},
"juziseo": {"email": "", "password": ""},
},
)
redis_client = get_redis()
try:
if redis_credentials := redis_client.get(REDIS_KEYS["credentials"]):
credentials = json.loads(redis_credentials)
except Exception:
pass
return {
"juming": {
"email": str(credentials.get("juming", {}).get("email", "")),
"password": str(credentials.get("juming", {}).get("password", "")),
},
"juziseo": {
"email": str(credentials.get("juziseo", {}).get("email", "")),
"password": str(credentials.get("juziseo", {}).get("password", "")),
},
}
def update_credentials_payload(payload: dict) -> dict:
current = get_credentials_payload()
credentials = {
"juming": {
"email": str(payload.get("juming", {}).get("email", current["juming"]["email"])),
"password": str(payload.get("juming", {}).get("password", current["juming"]["password"])),
},
"juziseo": {
"email": str(payload.get("juziseo", {}).get("email", current["juziseo"]["email"])),
"password": str(payload.get("juziseo", {}).get("password", current["juziseo"]["password"])),
},
}
write_json("credentials.json", credentials)
redis_client = get_redis()
try:
redis_client.set(REDIS_KEYS["credentials"], json.dumps(credentials, ensure_ascii=False))
redis_client.publish("domain_tool:credentials:update", json.dumps(credentials, ensure_ascii=False))
except Exception:
pass
return credentials
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"]))
thread_count = _normalize_thread_count(payload.get("thread_count", current["thread_count"]))
node_thread_counts = _normalize_node_thread_counts(payload.get("node_thread_counts", current.get("node_thread_counts", {})))
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)})
write_json("node_thread_counts.json", node_thread_counts)
redis_client = get_redis()
try:
@@ -69,6 +207,8 @@ def update_settings_payload(payload: dict) -> dict:
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))
redis_client.set(REDIS_KEYS["node_thread_counts"], json.dumps(node_thread_counts, ensure_ascii=False))
redis_client.publish("domain_tool:node_thread_counts:update", json.dumps(node_thread_counts, ensure_ascii=False))
except Exception:
pass
@@ -76,6 +216,8 @@ def update_settings_payload(payload: dict) -> dict:
"detect_options": detect_options,
"proxy_config": proxy_config,
"thread_count": thread_count,
"node_thread_counts": node_thread_counts,
"current_node_code": app_settings.node_code,
"runtime_settings": runtime_settings,
}
@@ -101,12 +243,10 @@ def validate_settings_payload(payload: dict) -> None:
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")
_normalize_thread_count(payload["thread_count"])
if "node_thread_counts" in payload:
_normalize_node_thread_counts(payload["node_thread_counts"])
if "detect_options" in payload:
detect_options = payload["detect_options"]
@@ -135,6 +275,9 @@ def validate_settings_payload(payload: dict) -> None:
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")
for key in ("worker_service_name", "api_service_name", "sync_agent_service_name"):
if key in runtime_settings and runtime_settings[key] is not None and not str(runtime_settings[key]).strip():
raise ValueError(f"{key} must not be empty")
def backup_current_settings(reason: str = "manual") -> dict: