"""API 측 config IO 헬퍼. 프록시와 동일한 파일을 가리키고, atomic rename 으로 갱신해서 프록시가 mtime polling 으로 안전하게 hot reload 할 수 있게 한다. """ from __future__ import annotations import json import os import threading import time from pathlib import Path CONFIG_PATH = Path(os.environ.get("MC_CONFIG_PATH", "/data/config.json")) LOG_DB = Path(os.environ.get("MC_LOG_DB", "/data/logs.db")) RESTART_SIGNAL = Path(os.environ.get("MC_RESTART_SIGNAL", "/data/restart.signal")) DEFAULT_CONFIG = { "proxy": {"listen_port": 25565, "enabled": True}, "backend": {"host": "127.0.0.1", "port": 25565}, "allowed_domains": [ {"domain": "mc.tkrmagid.kr", "enabled": True, "note": "메인 도메인"} ], } _lock = threading.Lock() def load_config() -> dict: if not CONFIG_PATH.exists(): CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True) save_config(DEFAULT_CONFIG) return json.loads(json.dumps(DEFAULT_CONFIG)) with _lock: with CONFIG_PATH.open("r", encoding="utf-8") as f: return json.load(f) def save_config(cfg: dict) -> None: CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True) tmp = CONFIG_PATH.with_suffix(".json.tmp") with _lock: with tmp.open("w", encoding="utf-8") as f: json.dump(cfg, f, ensure_ascii=False, indent=2) os.replace(tmp, CONFIG_PATH) def touch_restart_signal() -> float: """프록시에 강제 listener restart 를 요청하는 신호 파일 갱신. config 가 바뀌지 않은 상태에서도 listener 를 stop→start 시키고 싶을 때 사용. """ RESTART_SIGNAL.parent.mkdir(parents=True, exist_ok=True) ts = time.time() with _lock: # touch: 파일이 없으면 만들고, 있으면 mtime 만 갱신 RESTART_SIGNAL.touch(exist_ok=True) os.utime(RESTART_SIGNAL, (ts, ts)) return ts