minecraft-web/backend/lib/icons.js

112 lines
3.2 KiB
JavaScript
Raw Normal View History

2025-12-16 08:40:32 +09:00
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 };