From 5517fd7ca10089d766cfe64db65e1dc9ee396f4c Mon Sep 17 00:00:00 2001 From: choibk Date: Tue, 27 Jan 2026 01:54:53 +0900 Subject: [PATCH] =?UTF-8?q?[FIX]=20v0.4.4.1=20-=20=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EC=84=A0=EC=8A=A4=20=ED=99=9C=EC=84=B1=ED=99=94=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EB=B0=8F=20=EB=AA=A8=EB=93=88=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98=20=EC=9D=B4=EC=8A=88=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/roadmap/INTEGRATED_ROADMAP.md | 6 +++++- package.json | 2 +- server/index.js | 22 ++++++++++++++++++++-- server/package.json | 2 +- server/routes/auth.js | 2 +- server/routes/system.js | 5 ++++- tools/license_manager.cjs | 21 +++++++++++++-------- 7 files changed, 45 insertions(+), 15 deletions(-) diff --git a/docs/roadmap/INTEGRATED_ROADMAP.md b/docs/roadmap/INTEGRATED_ROADMAP.md index b9a3b05..8122934 100644 --- a/docs/roadmap/INTEGRATED_ROADMAP.md +++ b/docs/roadmap/INTEGRATED_ROADMAP.md @@ -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`) 시 카테고리를 하드코딩하지 않고 직접 문자열로 정의 가능하도록 개선 - 사이드바 그룹화 로직을 동적으로 처리하여 신규 카테고리 추가 시 코드 수정 최소화 diff --git a/package.json b/package.json index 86a0845..370e5b9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "smartims", "private": true, - "version": "0.4.4.0", + "version": "0.4.4.1", "type": "module", "scripts": { "dev": "vite", diff --git a/server/index.js b/server/index.js index 664a73f..a56c733 100644 --- a/server/index.js +++ b/server/index.js @@ -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); diff --git a/server/package.json b/server/package.json index 7bd208b..0c48685 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "server", - "version": "0.4.4.0", + "version": "0.4.4.1", "description": "", "main": "index.js", "scripts": { diff --git a/server/routes/auth.js b/server/routes/auth.js index 614e5a4..a089882 100644 --- a/server/routes/auth.js +++ b/server/routes/auth.js @@ -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]); diff --git a/server/routes/system.js b/server/routes/system.js index e87d324..354e7b9 100644 --- a/server/routes/system.js +++ b/server/routes/system.js @@ -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}'` }); diff --git a/tools/license_manager.cjs b/tools/license_manager.cjs index 528dfcb..9e3c5e0 100644 --- a/tools/license_manager.cjs +++ b/tools/license_manager.cjs @@ -111,7 +111,7 @@ Usage: node tools/license_manager.cjs [args] Commands: list Show active modules in Database generate Generate a new license key - Modules: asset, production, monitoring + Modules: asset, production, material, quality, cctv Types: dev, sub, demo Flags: --subscriber (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;