import express from "express"; import { getTranslations, getIcons, getGamerules, dbPool } from "../lib/db.js"; import { getIconUrl } from "../lib/icons.js"; import { MOD_API_URL, fetchModStatus, fetchMotd, formatStatusForClient, fetchAllPlayers, fetchPlayerDetail, getCachedStatus, getCachedPlayers, } from "../lib/minecraft.js"; const router = express.Router(); // 서버 상태 API router.get("/status", async (req, res) => { const cached = getCachedStatus(); if (cached) { res.json(cached); } else { const [modStatus, motdData] = await Promise.all([ fetchModStatus(), fetchMotd(), ]); res.json(formatStatusForClient(modStatus, motdData)); } }); // 플레이어 목록 API router.get("/players", async (req, res) => { const cached = getCachedPlayers(); if (cached.length > 0) { res.json(cached); } else { const players = await fetchAllPlayers(); res.json(players); } }); // 번역 데이터 API router.get("/translations", (req, res) => { res.json(getTranslations()); }); // 아이콘 캐시 API router.get("/icons", (req, res) => { res.json(getIcons()); }); // 온디맨드 아이콘 API router.get("/icon/:type/:name", async (req, res) => { const { type, name } = req.params; if (!["block", "item", "entity"].includes(type)) { return res.status(400).json({ error: "Invalid type" }); } const iconUrl = await getIconUrl(name, type); if (iconUrl) { res.json({ icon: iconUrl }); } else { res.status(404).json({ error: "Icon not found" }); } }); // 게임룰 API router.get("/gamerules", (req, res) => { res.json(getGamerules()); }); // 플레이어 통계 API router.get("/player/:uuid/stats", async (req, res) => { const { uuid } = req.params; try { const response = await fetch(`${MOD_API_URL}/player/${uuid}/stats`); if (response.ok) { res.json(await response.json()); } else { res.status(404).json({ error: "통계를 불러올 수 없습니다" }); } } catch (error) { console.error("[ModAPI] 통계 조회 실패:", error.message); res.status(500).json({ error: "서버 오류" }); } }); // 플레이어 상세 정보 API router.get("/player/:uuid", async (req, res) => { const { uuid } = req.params; const player = await fetchPlayerDetail(uuid); if (player) { res.json(player); } else { res.status(404).json({ error: "플레이어 데이터를 찾을 수 없습니다" }); } }); // 월드 정보 API router.get("/worlds", async (req, res) => { try { const response = await fetch(`${MOD_API_URL}/worlds`); if (response.ok) { res.json(await response.json()); } else { res.json({ worlds: [] }); } } catch (error) { console.error("[ModAPI] 월드 조회 실패:", error.message); res.json({ worlds: [] }); } }); // 모드팩 목록 조회 API router.get("/modpacks", async (req, res) => { try { const [rows] = await dbPool.query(` SELECT id, name, version, minecraft_version, mod_loader, changelog, file_size, contents_json, created_at, updated_at FROM modpacks ORDER BY created_at DESC `); // contents_json을 파싱하여 반환 const modpacks = rows.map((row) => ({ ...row, contents: row.contents_json ? JSON.parse(row.contents_json) : null, contents_json: undefined, // 원본 JSON 문자열은 제거 })); res.json(modpacks); } catch (error) { console.error("[API] 모드팩 목록 조회 실패:", error.message); res.status(500).json({ error: "서버 오류" }); } }); // 모드팩 다운로드 API (S3 리디렉션) router.get("/modpacks/:id/download", async (req, res) => { try { const { id } = req.params; const [rows] = await dbPool.query(`SELECT * FROM modpacks WHERE id = ?`, [ id, ]); if (rows.length === 0) { return res.status(404).json({ error: "모드팩을 찾을 수 없습니다" }); } const modpack = rows[0]; // file_key의 각 세그먼트를 인코딩 const encodedKey = modpack.file_key .split("/") .map((s) => encodeURIComponent(s)) .join("/"); const downloadUrl = `https://s3.caadiq.co.kr/minecraft/${encodedKey}`; // file_key에서 파일명 추출 (인코딩 안 된 원본) const filename = modpack.file_key.split("/").pop(); // S3에서 파일 가져오기 const s3Response = await fetch(downloadUrl); if (!s3Response.ok) { return res.status(404).json({ error: "파일을 찾을 수 없습니다" }); } // 헤더 설정 res.setHeader("Content-Type", "application/zip"); res.setHeader("Content-Length", modpack.file_size); res.setHeader( "Content-Disposition", `attachment; filename*=UTF-8''${encodeURIComponent(filename)}` ); // 스트리밍 전달 const { Readable } = await import("stream"); Readable.fromWeb(s3Response.body).pipe(res); } catch (error) { console.error("[API] 모드팩 다운로드 실패:", error.message); res.status(500).json({ error: "다운로드 실패" }); } }); export default router;