diff --git a/docs/roadmap/INTEGRATED_ROADMAP.md b/docs/roadmap/INTEGRATED_ROADMAP.md index 11aba7d..7b70d61 100644 --- a/docs/roadmap/INTEGRATED_ROADMAP.md +++ b/docs/roadmap/INTEGRATED_ROADMAP.md @@ -35,8 +35,13 @@ - [x] CCTV 설정 파일 DB 저장 시 username/password 필드 암호화 처리 - [x] CCTV 설정 파일 조회 시 username/password 필드 복호화 처리 - **(수정사항)**: `CameraManagementPage` 신설 및 `cryptoUtil`을 통한 RTSP 계정 정보의 DB 암호화 저장 로직 완성 (AES-256) - #### 🏷️ Tag: `v0.4.2.0` +- [ ] **라이선스 관리 오류 수정** +- [ ] **시스템 관리 기본설정 오류 수정** +- [ ] **시스템 업데이트 오류 수정** + + +#### 🏷️ Tag: `v0.4.3.0` - [ ] **소모품 관리** - [ ] 이 기능은 자재/재고 관리 모듈 생성하여 메뉴가 아니라 속성(카테고리)으로 추가할 예정 diff --git a/server/index.js b/server/index.js index 6398f81..16d26b4 100644 --- a/server/index.js +++ b/server/index.js @@ -438,14 +438,12 @@ const initTables = async () => { const [existingModules] = await db.query('SELECT 1 FROM system_modules LIMIT 1'); if (existingModules.length === 0) { const insert = `INSERT INTO system_modules (code, name, is_active, license_type) VALUES (?, ?, ?, ?)`; - await db.query(insert, ['asset', '자산 관리', true, 'dev']); + await db.query(insert, ['asset', '자산 관리', false, null]); await db.query(insert, ['production', '생산 관리', false, null]); - await db.query(insert, ['cctv', 'CCTV', true, 'dev']); + await db.query(insert, ['cctv', 'CCTV', false, null]); } else { - // One-time update: Rename 'monitoring' code to 'cctv' and ensure it's active - await db.query("UPDATE system_modules SET code = 'cctv', is_active = 1 WHERE code = 'monitoring'"); - // Also ensure 'cctv' is active if it already exists but was inactive due to renaming glitches - await db.query("UPDATE system_modules SET is_active = 1 WHERE code = 'cctv'"); + // One-time update: Rename 'monitoring' code to 'cctv' (migration) + await db.query("UPDATE system_modules SET code = 'cctv' WHERE code = 'monitoring'"); } console.log('✅ Tables Initialized'); diff --git a/server/routes/system.js b/server/routes/system.js index 14efba1..576aa87 100644 --- a/server/routes/system.js +++ b/server/routes/system.js @@ -278,7 +278,8 @@ router.get('/modules', isAuthenticated, async (req, res) => { // Get stored subscriber ID const [subRows] = await db.query("SELECT setting_value FROM system_settings WHERE setting_key = 'subscriber_id'"); - const serverSubscriberId = subRows.length > 0 ? subRows[0].setting_value : null; + // Ensure we return null or empty string if not found, DO NOT use any hardcoded fallback + const serverSubscriberId = (subRows.length > 0 && subRows[0].setting_value) ? subRows[0].setting_value : ''; defaults.forEach(code => { const found = rows.find(r => r.code === code); @@ -322,7 +323,10 @@ router.post('/modules/:code/activate', isAuthenticated, hasRole('admin'), async } // 2. Check Module match - if (result.module !== code) { + // Allow legacy 'monitoring' licenses to activate 'cctv' module + const isMatch = result.module === code || (code === 'cctv' && result.module === 'monitoring'); + + if (!isMatch) { return res.status(400).json({ error: `This license is for '${result.module}' module, not '${code}'` }); } @@ -342,7 +346,7 @@ router.post('/modules/:code/activate', isAuthenticated, hasRole('admin'), async } } else { if (result.subscriberId !== serverSubscriberId) { - return res.status(403).json({ + return res.status(400).json({ error: `구독자 ID 불일치: 라이선스 키는 [${result.subscriberId}] 전용이지만, 현재 서버는 [${serverSubscriberId}]로 설정되어 있습니다.` }); } diff --git a/src/pages/auth/LoginPage.tsx b/src/pages/auth/LoginPage.tsx index dc75932..050b61a 100644 --- a/src/pages/auth/LoginPage.tsx +++ b/src/pages/auth/LoginPage.tsx @@ -61,6 +61,8 @@ export function LoginPage() { placeholder="아이디를 입력하세요" required autoComplete="one-time-code" + readOnly + onFocus={(e) => e.target.readOnly = false} /> @@ -77,6 +79,8 @@ export function LoginPage() { placeholder="비밀번호를 입력하세요" required autoComplete="new-password" + readOnly + onFocus={(e) => e.target.readOnly = false} /> diff --git a/src/platform/pages/BasicSettingsPage.tsx b/src/platform/pages/BasicSettingsPage.tsx index 2056404..6e0013e 100644 --- a/src/platform/pages/BasicSettingsPage.tsx +++ b/src/platform/pages/BasicSettingsPage.tsx @@ -452,7 +452,14 @@ export function BasicSettingsPage() {
- * 라이선스 키에 포함된 ID와 일치해야 활성화됩니다. -
+ )} + {isConfigUnlocked && ( + +
+ * 주의: 구독자 ID를 변경하면 기존에 활성화된 모든 모듈의 라이선스 효력이 중지될 수 있습니다.
+ * 반드시 라이선스 키에 포함된 ID와 일치하게 설정하십시오.
+
서버 환경 설정은 시스템의 핵심 식별자입니다.
+보안을 위해 최고관리자(Supervisor) 인증 후 변경할 수 있습니다.
+서버 핵심 설정을 변경하려면 인증이 필요합니다.
+