[FIX] v0.4.4.1 - 라이선스 활성화 오류 및 모듈 마이그레이션 이슈 해결

This commit is contained in:
choibk 2026-01-27 01:54:53 +09:00
parent 763217acaf
commit 5517fd7ca1
7 changed files with 45 additions and 15 deletions

View File

@ -93,7 +93,11 @@
- 미사용 변수 및 린트 에러 수정을 통한 빌드 실패 방지
- 버전 정보 동기화 로직 최적화 (package.json 기반)
#### 🏷️ Tag: `v0.4.4.0` (진행중)
#### 🏷️ Tag: `v0.4.4.1` (완료)
- [x] **라이선스 모듈 활성화 오류 긴급 수정**
- `inventory``material``monitoring``cctv` 레거시 라이선스 호환성 확보
- CLI 도구(`license_manager.cjs`) 내 신규 모듈(`material`, `quality`) 지원 추가
- 사용자 권한(`allowed_modules`) 자동 마이그레이션 로직 보강
- [x] **모듈 카테고리 동적 생성 기능**
- 모듈 정의(`IModuleDefinition`) 시 카테고리를 하드코딩하지 않고 직접 문자열로 정의 가능하도록 개선
- 사이드바 그룹화 로직을 동적으로 처리하여 신규 카테고리 추가 시 코드 수정 최소화

View File

@ -1,7 +1,7 @@
{
"name": "smartims",
"private": true,
"version": "0.4.4.0",
"version": "0.4.4.1",
"type": "module",
"scripts": {
"dev": "vite",

View File

@ -263,8 +263,21 @@ const initTables = async () => {
console.log("✅ Added 'allowed_modules' column to users");
// For existing users, grant access to all modules by default
const allModules = JSON.stringify(['asset', 'production', 'cctv', 'material', 'inventory', 'quality']);
const allModules = JSON.stringify(['asset', 'production', 'cctv', 'material', 'quality']);
await db.query("UPDATE users SET allowed_modules = ?", [allModules]);
} else {
// Migration: Ensure existing users have new modules in their allowed_modules if they are missing
try {
// MySQL/MariaDB JSON functions for appending if not exists
// We add material and quality if not present. inventory is removed later or handled via mapping.
await db.query("UPDATE users SET allowed_modules = JSON_ARRAY_APPEND(allowed_modules, '$', 'material') WHERE NOT JSON_CONTAINS(allowed_modules, '\"material\"')");
await db.query("UPDATE users SET allowed_modules = JSON_ARRAY_APPEND(allowed_modules, '$', 'quality') WHERE NOT JSON_CONTAINS(allowed_modules, '\"quality\"')");
// Remove legacy 'monitoring' and 'inventory' if they exist to keep it clean
await db.query("UPDATE users SET allowed_modules = JSON_REMOVE(allowed_modules, JSON_UNQUOTE(JSON_SEARCH(allowed_modules, 'one', 'monitoring'))) WHERE JSON_SEARCH(allowed_modules, 'one', 'monitoring') IS NOT NULL");
await db.query("UPDATE users SET allowed_modules = JSON_REMOVE(allowed_modules, JSON_UNQUOTE(JSON_SEARCH(allowed_modules, 'one', 'inventory'))) WHERE JSON_SEARCH(allowed_modules, 'one', 'inventory') IS NOT NULL");
} catch (e) {
console.warn('⚠️ allowed_modules migration warning (might be old MySQL version):', e.message);
}
}
console.log('✅ Tables Initialized');
@ -464,10 +477,15 @@ const initTables = async () => {
}
}
// One-time update: Rename 'monitoring' code to 'cctv' (migration)
// One-time update: Rename codes (migration)
// 1. monitoring -> cctv
await db.query("UPDATE system_modules SET code = 'cctv' WHERE code = 'monitoring' AND NOT EXISTS (SELECT 1 FROM (SELECT * FROM system_modules) AS tmp WHERE code = 'cctv')");
await db.query("DELETE FROM system_modules WHERE code = 'monitoring' AND EXISTS (SELECT 1 FROM (SELECT * FROM system_modules) AS tmp WHERE code = 'cctv')");
// 2. inventory -> material
await db.query("UPDATE system_modules SET code = 'material' WHERE code = 'inventory' AND NOT EXISTS (SELECT 1 FROM (SELECT * FROM system_modules) AS tmp WHERE code = 'material')");
await db.query("DELETE FROM system_modules WHERE code = 'inventory' AND EXISTS (SELECT 1 FROM (SELECT * FROM system_modules) AS tmp WHERE code = 'material')");
console.log('✅ Tables Initialized');
} catch (err) {
console.error('❌ Table Initialization Failed:', err);

View File

@ -1,6 +1,6 @@
{
"name": "server",
"version": "0.4.4.0",
"version": "0.4.4.1",
"description": "",
"main": "index.js",
"scripts": {

View File

@ -180,7 +180,7 @@ router.post('/users', isAuthenticated, hasRole('admin'), async (req, res) => {
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`;
const modulesJson = allowed_modules ? JSON.stringify(allowed_modules) : JSON.stringify(['asset', 'production', 'cctv']);
const modulesJson = allowed_modules ? JSON.stringify(allowed_modules) : JSON.stringify(['asset', 'production', 'cctv', 'material', 'quality']);
await db.query(sql, [id, hashedPassword, name, department, position, encryptedPhone, role || 'user', session_timeout || 10, modulesJson]);

View File

@ -360,7 +360,10 @@ router.post('/modules/:code/activate', isAuthenticated, hasRole('admin'), async
// 2. Check Module match
// Allow legacy 'monitoring' licenses to activate 'cctv' module
const isMatch = result.module === code || (code === 'cctv' && result.module === 'monitoring');
// Allow legacy 'inventory' licenses to activate 'material' module
const isMatch = result.module === code ||
(code === 'cctv' && result.module === 'monitoring') ||
(code === 'material' && result.module === 'inventory');
if (!isMatch) {
return res.status(400).json({ error: `This license is for '${result.module}' module, not '${code}'` });

View File

@ -111,7 +111,7 @@ Usage: node tools/license_manager.cjs <command> [args]
Commands:
list Show active modules in Database
generate <module> <type> Generate a new license key
Modules: asset, production, monitoring
Modules: asset, production, material, quality, cctv
Types: dev, sub, demo
Flags: --subscriber <id> (REQUIRED)
[--activate]
@ -134,7 +134,7 @@ async function listModules() {
console.log('| Code | Active | Type | Expiry | Subscriber |');
console.log('---------------------------------------------------------------------------------------');
const defaults = ['asset', 'production', 'monitoring'];
const defaults = ['asset', 'production', 'cctv', 'material', 'quality'];
const data = {};
rows.forEach(r => data[r.code] = r);
@ -185,7 +185,7 @@ async function showCmd(identifier) {
const conn = await mysql.createConnection(dbConfig);
try {
// Check if identifier is a known module code
const validModules = ['asset', 'production', 'monitoring'];
const validModules = ['asset', 'production', 'cctv', 'material', 'quality'];
const isModule = validModules.includes(identifier) || validModules.includes(identifier.toLowerCase());
if (isModule) {
@ -253,10 +253,9 @@ async function generateCmd(moduleCode, type) {
}
// ... Validation (Same as before) ...
const validModules = ['asset', 'production', 'monitoring'];
const validModules = ['asset', 'production', 'cctv', 'material', 'quality'];
const validTypes = ['dev', 'sub', 'demo'];
const modMap = { 'cctv': 'monitoring' };
const finalCode = modMap[moduleCode] || moduleCode;
const finalCode = moduleCode;
if (!validModules.includes(finalCode)) {
console.error(`Invalid module. Allowed: ${validModules.join(', ')}`);
@ -422,7 +421,13 @@ async function activateCmd(key) {
}
// 4. Activate New License
const names = { 'asset': '자산 관리', 'production': '생산 관리', 'monitoring': 'CCTV' };
const names = {
'asset': '자산 관리',
'production': '생산 관리',
'cctv': 'CCTV',
'material': '자재/재고 관리',
'quality': '품질 관리'
};
const sql = `
INSERT INTO system_modules (code, name, is_active, license_key, license_type, expiry_date, subscriber_id)
VALUES (?, ?, true, ?, ?, ?, ?)
@ -452,7 +457,7 @@ async function deleteCmd(moduleCode) {
return;
}
const validModules = ['asset', 'production', 'monitoring'];
const validModules = ['asset', 'production', 'cctv', 'material', 'quality'];
if (!validModules.includes(moduleCode)) {
console.error(`Invalid module. Allowed: ${validModules.join(', ')}`);
return;