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); }); } /** * 온디맨드 아이콘 가져오기 (없으면 다운로드/저장) */ async function getIconUrl(name, type, modId = "minecraft") { const iconsCache = getIcons(); // item 타입일 때 실제로 blocks인지 items인지 DB에서 확인 let actualType = type; if (type === "item") { const [itemRows] = await dbPool.query( "SELECT 1 FROM items WHERE name = ? AND mod_id = ? LIMIT 1", [name, modId] ); if (itemRows.length === 0) { const [blockRows] = await dbPool.query( "SELECT 1 FROM blocks WHERE name = ? AND mod_id = ? LIMIT 1", [name, modId] ); if (blockRows.length > 0) { actualType = "block"; } } } // 캐시 키: type:name 형식 const cacheKey = `${actualType}:${name}`; // 캐시에 있으면 반환 if (iconsCache[cacheKey]) return iconsCache[cacheKey]; // 소스 URL 결정 let sourceUrl; let s3Key; if (actualType === "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 = `icon/entities/${modId}_entity_${name}.png`; } else { sourceUrl = `https://mc.nerothe.com/img/1.21/minecraft_${name}.png`; s3Key = `icon/${actualType}s/${modId}_${actualType}_${name}.png`; } try { const imageData = await downloadImage(sourceUrl); const publicUrl = await uploadToS3(s3Config.bucket, s3Key, imageData); // DB 업데이트 const table = actualType === "entity" ? "entities" : `${actualType}s`; 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 };