import path from "path"; import { fileURLToPath } from "url"; import { readFileSync } from "fs"; import https from "https"; import http from "http"; import { dbPool, getIcons, setIconCache } from "./db.js"; import { s3Config, uploadToS3 } from "./s3.js"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // MHF UUID 매핑 로드 const entityToMHF = JSON.parse( readFileSync(path.join(__dirname, "..", "data", "entityToMHF.json"), "utf-8") ); /** * URL에서 이미지 다운로드 */ function downloadImage(url) { return new Promise((resolve, reject) => { const protocol = url.startsWith("https") ? https : http; protocol .get(url, (res) => { if (res.statusCode === 301 || res.statusCode === 302) { return downloadImage(res.headers.location) .then(resolve) .catch(reject); } if (res.statusCode !== 200) { reject(new Error(`HTTP ${res.statusCode}`)); return; } const chunks = []; res.on("data", (chunk) => chunks.push(chunk)); res.on("end", () => resolve(Buffer.concat(chunks))); res.on("error", reject); }) .on("error", reject); }); } /** * 온디맨드 아이콘 가져오기 (없으면 다운로드/저장) * 모드 아이템도 지원 - DB에서 먼저 검색 */ async function getIconUrl(name, type) { const iconsCache = getIcons(); // 캐시 키: type:name 형식 const cacheKey = `${type}:${name}`; // 캐시에 있으면 반환 if (iconsCache[cacheKey]) return iconsCache[cacheKey]; // DB에서 해당 아이템 검색 (icon이 있으면 바로 반환) let table = type === "entity" ? "entities" : "items"; const [rows] = await dbPool.query( `SELECT icon, mod_id FROM ${table} WHERE name = ? LIMIT 1`, [name] ); if (rows.length > 0 && rows[0].icon) { // DB에 icon이 이미 있으면 캐시에 저장하고 반환 setIconCache(cacheKey, rows[0].icon); return rows[0].icon; } // DB에 없거나 icon이 없으면 온디맨드 다운로드 (minecraft 아이템만) const modId = rows.length > 0 ? rows[0].mod_id : "minecraft"; // minecraft가 아닌 모드 아이템은 외부에서 다운로드 불가 if (modId !== "minecraft") { return null; } // 소스 URL 결정 let sourceUrl; let s3Key; if (type === "entity") { const mhfUuid = entityToMHF[name]; if (mhfUuid) { sourceUrl = `https://mc-heads.net/avatar/${mhfUuid}/64`; } else { sourceUrl = `https://mc.nerothe.com/img/1.21/minecraft_${name}_spawn_egg.png`; } s3Key = `icons/entities/${modId}_${name}.png`; } else { // item과 block 모두 icons/items에 저장 sourceUrl = `https://mc.nerothe.com/img/1.21/minecraft_${name}.png`; s3Key = `icons/items/${modId}_${name}.png`; } try { const imageData = await downloadImage(sourceUrl); const publicUrl = await uploadToS3(s3Config.bucket, s3Key, imageData); // DB 업데이트 await dbPool.query( `UPDATE ${table} SET icon = ? WHERE name = ? AND mod_id = ?`, [publicUrl, name, modId] ); // 캐시 업데이트 setIconCache(cacheKey, publicUrl); console.log(`[Icon] 저장 완료: ${cacheKey} → ${publicUrl}`); return publicUrl; } catch (error) { console.error(`[Icon] 다운로드 실패: ${cacheKey} - ${error.message}`); return null; } } export { getIconUrl };