- blocks 테이블을 items 테이블로 통합 (type 컬럼 추가) - 아이콘 조회 시 DB에서 먼저 검색하여 모드 아이템 지원 - S3 경로 통일: icons/items/<modid>_<name>.png - 번역 업로드/삭제 API 수정 - 번역 캐시 로드 로직 수정
115 lines
3.4 KiB
JavaScript
115 lines
3.4 KiB
JavaScript
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 };
|