diff --git a/.gitignore b/.gitignore index eb35af6..800f025 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ data/config.json data/logs.db data/logs.db-wal data/logs.db-shm +data/restart.signal # 노드/파이썬 빌드 산출물 node_modules/ diff --git a/README.md b/README.md index 47892ee..1746c5c 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,10 @@ mc-filter-proxy 컨테이너 (25565) - API 가 `data/config.json` 을 atomic rename (`tempfile + os.replace`) 으로 갱신 - 프록시가 2초 간격으로 mtime 폴링, 변경 감지 시 메모리 캐시 재로드 -- `listen_port` 나 `proxy.enabled` 가 바뀌면 리스너 자체를 재시작, 그 외(도메인/백엔드)는 다음 연결부터 즉시 적용 +- `proxy.enabled` 가 바뀌면 리스너를 재시작 (켜기/끄기), 그 외(도메인/백엔드)는 다음 연결부터 즉시 적용 +- `POST /api/proxy/restart` 는 별도 신호 파일(`data/restart.signal`)을 touch 해서 프록시가 listener 를 강제로 stop → start 한다 (config 변경이 없어도 동작) + +> **리스닝 포트 변경:** `proxy.listen_port` 는 docker-compose 의 `ports` 매핑과 짝이라 대시보드에서 편집할 수 없습니다. 바꾸려면 `data/config.json` 과 `docker-compose.yml` 의 `25565:25565` 매핑을 함께 수정한 뒤 `docker compose up -d --force-recreate proxy` 하세요. ## 보안 권장 diff --git a/api/config_io.py b/api/config_io.py index f2df5d0..07593a8 100644 --- a/api/config_io.py +++ b/api/config_io.py @@ -8,10 +8,12 @@ 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}, @@ -41,3 +43,17 @@ def save_config(cfg: dict) -> None: 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 diff --git a/api/routes/status.py b/api/routes/status.py index e839953..29158a0 100644 --- a/api/routes/status.py +++ b/api/routes/status.py @@ -6,7 +6,7 @@ import time from fastapi import APIRouter -from config_io import LOG_DB, load_config, save_config +from config_io import LOG_DB, load_config, touch_restart_signal router = APIRouter() @@ -53,7 +53,10 @@ def status() -> dict: @router.post("/proxy/restart") def restart_proxy() -> dict: - """config 파일 mtime 을 갱신해서 프록시 watcher 가 재로드하게 한다.""" - cfg = load_config() - save_config(cfg) - return {"ok": True, "note": "config touched; proxy will reload"} + """프록시 listener 를 강제로 stop → start 시킨다. + + config 가 바뀌지 않았어도 listener 자체를 재시작하기 때문에 단순 + config reload 와는 다르다. (proxy watcher 가 별도 신호 파일을 폴링) + """ + ts = touch_restart_signal() + return {"ok": True, "restart_signal_ts": ts} diff --git a/frontend/src/pages/Settings.jsx b/frontend/src/pages/Settings.jsx index d993b98..af47c56 100644 --- a/frontend/src/pages/Settings.jsx +++ b/frontend/src/pages/Settings.jsx @@ -31,16 +31,11 @@ export default function Settings() {