minecraft-web/backend/lib/db.js
caadiq 10c27eecba feat: blocks/items 테이블 통합 및 아이콘 시스템 개선
- blocks 테이블을 items 테이블로 통합 (type 컬럼 추가)
- 아이콘 조회 시 DB에서 먼저 검색하여 모드 아이템 지원
- S3 경로 통일: icons/items/<modid>_<name>.png
- 번역 업로드/삭제 API 수정
- 번역 캐시 로드 로직 수정
2025-12-26 19:52:35 +09:00

137 lines
4 KiB
JavaScript

import mysql from "mysql2/promise";
// MariaDB 연결 풀 - 환경변수에서 로드
const dbPool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
});
// 캐시 데이터
let translationsCache = {
blocks: {}, // 블록 번역
items: {}, // 아이템 번역
entities: {}, // 엔티티 번역
itemsAndBlocks: {}, // 아이템 통계용 (items + blocks)
};
let iconsCache = {}; // { "type:name": iconUrl } - 타입별로 구분
let gamerulesCache = {};
/**
* 번역 및 아이콘 데이터 로드 (items, entities 테이블에서)
*/
async function loadTranslations() {
try {
// items 테이블에서 모든 항목 로드 (type 컬럼으로 block/item 구분)
const [items] = await dbPool.query(
"SELECT name, name_ko, mod_id, icon, type FROM items"
);
const [entities] = await dbPool.query(
"SELECT name, name_ko, mod_id, icon FROM entities"
);
const [gamerules] = await dbPool.query(
"SELECT name, name_ko, description_ko FROM gamerules WHERE mod_id = 'minecraft'"
);
// 캐시 초기화
translationsCache = {
blocks: {},
items: {},
entities: {},
itemsAndBlocks: {},
};
iconsCache = {};
let blockCount = 0;
let itemCount = 0;
// items 테이블에서 type에 따라 분리
items.forEach((row) => {
if (row.type === "block") {
translationsCache.blocks[row.name] = row.name_ko;
if (row.icon) iconsCache[`block:${row.name}`] = row.icon;
blockCount++;
} else {
translationsCache.items[row.name] = row.name_ko;
if (row.icon) iconsCache[`item:${row.name}`] = row.icon;
itemCount++;
}
});
entities.forEach((row) => {
translationsCache.entities[row.name] = row.name_ko;
if (row.icon) iconsCache[`entity:${row.name}`] = row.icon;
});
// 아이템 통계용 캐시 (blocks + items, 아이템 우선)
Object.assign(translationsCache.itemsAndBlocks, translationsCache.blocks);
Object.assign(translationsCache.itemsAndBlocks, translationsCache.items);
// gamerules 캐시
gamerulesCache = {};
gamerules.forEach((row) => {
gamerulesCache[row.name] = {
name: row.name_ko || row.name,
description: row.description_ko || "",
};
});
const iconCount = Object.keys(iconsCache).length;
console.log(
`[DB] 번역 데이터 로드 완료: blocks ${blockCount}개, items ${itemCount}개, entities ${entities.length}개, icons ${iconCount}`
);
} catch (error) {
console.error("[DB] 번역 데이터 로드 실패:", error.message);
}
}
// 캐시 접근 함수
const getTranslations = () => translationsCache;
const getIcons = () => iconsCache;
const getGamerules = () => gamerulesCache;
const setIconCache = (key, value) => {
iconsCache[key] = value;
};
/**
* 모드팩 테이블 초기화
*/
async function initModpacksTable() {
try {
await dbPool.query(`
CREATE TABLE IF NOT EXISTS modpacks (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
version VARCHAR(50) NOT NULL,
minecraft_version VARCHAR(20),
mod_loader VARCHAR(50),
changelog TEXT,
file_key VARCHAR(500) NOT NULL,
file_size BIGINT DEFAULT 0,
original_filename VARCHAR(255),
contents_json LONGTEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY unique_version (name, version)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
`);
console.log("[DB] modpacks 테이블 초기화 완료");
} catch (error) {
console.error("[DB] modpacks 테이블 초기화 실패:", error.message);
}
}
export {
dbPool,
dbPool as pool,
loadTranslations,
getTranslations,
getIcons,
getGamerules,
setIconCache,
initModpacksTable,
};