From fe86d02a7292005453ecb4e43eb5a012b25228f5 Mon Sep 17 00:00:00 2001 From: caadiq Date: Mon, 29 Dec 2025 17:46:07 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=86=8C=EC=BC=93=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=A7=88=EC=9D=B8=ED=81=AC=EB=9E=98=ED=94=84=ED=8A=B8=20?= =?UTF-8?q?=EC=84=9C=EB=B2=84=20=EC=83=81=ED=83=9C=20=EC=8B=A4=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20=EA=B0=90=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 백엔드: 3초마다 서버 상태 확인, 변경 시 브로드캐스트 - 프론트엔드: minecraft_servers 이벤트로 UI 자동 업데이트 - 외부에서 서버 시작/종료해도 UI에 반영 --- backend/routes/admin.js | 2 ++ backend/server.js | 43 +++++++++++++++++++++++++++++++++++- frontend/src/pages/Admin.jsx | 7 ++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/backend/routes/admin.js b/backend/routes/admin.js index bdcbbd0..73201b7 100644 --- a/backend/routes/admin.js +++ b/backend/routes/admin.js @@ -1346,4 +1346,6 @@ router.get("/servers/status/:serverPath(*)", async (req, res) => { } }); +// 서버 상태 함수들 export (소켓 브로드캐스트용) +export { getServerList, getRunningServer, getContainerStatus }; export default router; diff --git a/backend/server.js b/backend/server.js index 019c39d..385c614 100644 --- a/backend/server.js +++ b/backend/server.js @@ -17,7 +17,11 @@ import { import apiRoutes from "./routes/api.js"; import authRoutes from "./routes/auth.js"; import linkRoutes from "./routes/link.js"; -import adminRoutes from "./routes/admin.js"; +import adminRoutes, { + getServerList, + getRunningServer, + getContainerStatus, +} from "./routes/admin.js"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -153,11 +157,48 @@ async function refreshLogs() { } } +// 마인크래프트 서버 상태 캐시 (변경 감지용) +let lastServerStatusHash = ""; + +// 마인크래프트 서버 상태 갱신 및 브로드캐스트 +async function refreshServerStatus() { + try { + const servers = await getServerList(); + const runningContainer = await getRunningServer(); + + // 각 서버의 상태 확인 + const serversWithStatus = await Promise.all( + servers.map(async (server) => { + const status = await getContainerStatus(server.path); + return { + ...server, + running: status === "running", + status, + }; + }) + ); + + // 상태 변경 감지 (해시 비교) + const currentHash = JSON.stringify({ serversWithStatus, runningContainer }); + if (currentHash !== lastServerStatusHash) { + lastServerStatusHash = currentHash; + io.emit("minecraft_servers", { + servers: serversWithStatus, + runningContainer, + }); + } + } catch (error) { + // 오류 무시 (서버 디렉토리가 없을 수 있음) + } +} + // 1초마다 데이터 갱신 setInterval(refreshAndBroadcast, 1000); setInterval(refreshLogs, 1000); +setInterval(refreshServerStatus, 3000); // 서버 상태는 3초마다 refreshAndBroadcast(); refreshLogs(); +refreshServerStatus(); // SPA 라우팅 - 모든 경로에 대해 index.html 제공 app.get("*", (req, res) => { diff --git a/frontend/src/pages/Admin.jsx b/frontend/src/pages/Admin.jsx index f7b85fb..7d19b16 100644 --- a/frontend/src/pages/Admin.jsx +++ b/frontend/src/pages/Admin.jsx @@ -1272,6 +1272,13 @@ export default function Admin({ isMobile = false }) { } }); + // 마인크래프트 서버 상태 실시간 업데이트 + socket.on("minecraft_servers", (data) => { + if (data?.servers) { + setServers(data.servers); + } + }); + // 월드 정보 요청 socket.emit("get_worlds");