diff --git a/docs/CCTV_MODULE_GUIDE.md b/docs/CCTV_MODULE_GUIDE.md
index 80993c2..d7f48a3 100644
--- a/docs/CCTV_MODULE_GUIDE.md
+++ b/docs/CCTV_MODULE_GUIDE.md
@@ -1,6 +1,6 @@
# CCTV 모바일 영상 및 카메라 설정 가이드
-본 문서는 SmartAsset CCTV 모니터링 모듈의 고급 설정 기능을 설명합니다.
+본 문서는 Smart IMS CCTV 모니터링 모듈의 고급 설정 기능을 설명합니다.
## 1. 전송 방식 (Transport Mode)
오래된 카메라나 네트워크 환경에 따라 RTSP 데이터 전송 방식을 선택할 수 있습니다.
diff --git a/docs/DEPLOYMENT_GUIDE.md b/docs/DEPLOYMENT_GUIDE.md
index e85d901..47d2f4b 100644
--- a/docs/DEPLOYMENT_GUIDE.md
+++ b/docs/DEPLOYMENT_GUIDE.md
@@ -1,6 +1,6 @@
# 시스템 배포 가이드 (Deployment Guide)
-본 문서는 빌드된 스마트어셋(SmartAsset) 솔루션을 실서버(Synology NAS 등)에 배포할 때 필요한 절차와 주의사항을 설명합니다.
+본 문서는 빌드된 스마트 IMS(Smart IMS) 솔루션을 실서버(Synology NAS 등)에 배포할 때 필요한 절차와 주의사항을 설명합니다.
## 1. 배포 대상 폴더 및 파일
서버에 업로드해야 하는 핵심 구성 요소는 다음과 같습니다.
@@ -52,7 +52,7 @@
업로드가 완료된 후 서버 터미널(SSH)에서 다음 명령을 실행합니다.
1. **의존성 설치**: `cd server` 이동 후 `npm install`
-2. **서비스 시작**: `pm2 start index.js --name "smartasset"`
+2. **서비스 시작**: `pm2 start index.js --name "smartims"`
3. **상태 확인**: `pm2 status`를 통해 서버가 `online` 상태인지 확인합니다.
---
diff --git a/docs/LICENSE_MANAGER_MANUAL.md b/docs/LICENSE_MANAGER_MANUAL.md
index 4bcd0e6..62eb422 100644
--- a/docs/LICENSE_MANAGER_MANUAL.md
+++ b/docs/LICENSE_MANAGER_MANUAL.md
@@ -1,6 +1,6 @@
# 라이선스 관리자 매뉴얼 (최종본)
-본 문서는 스마트어셋(SmartAsset) 시스템의 라이선스 관리 기능을 사용하는 방법을 설명합니다. 모든 라이선스 관리는 **RSA 비대칭 암호화** 방식을 따르며, `tools/license_manager.cjs` CLI 도구를 통해 수행됩니다.
+본 문서는 스마트 IMS(Smart IMS) 시스템의 라이선스 관리 기능을 사용하는 방법을 설명합니다. 모든 라이선스 관리는 **RSA 비대칭 암호화** 방식을 따르며, `tools/license_manager.cjs` CLI 도구를 통해 수행됩니다.
## 1. 사전 준비 및 구독자 설정 (중요)
라이선스 키를 활성화하기 전에 서버가 어떤 고객사(구독자)에게 속해 있는지 식별하기 위해 **구독자 ID**를 설정해야 합니다.
diff --git a/docs/task.md b/docs/task.md
index c3d0972..484eb40 100644
--- a/docs/task.md
+++ b/docs/task.md
@@ -1,4 +1,4 @@
-# SmartAsset Project - Task & Feature Roadmap
+# Smart IMS Project - Task & Feature Roadmap
## 1. Core Platform & Security
- [x] **Project Initialization**: Created Vite + React + TypeScript base.
diff --git a/index.html b/index.html
index bf078b8..00318cc 100644
--- a/index.html
+++ b/index.html
@@ -3,9 +3,10 @@
+
- SmartAsset - 통합 자산관리 플랫폼
+ Smart IMS - 통합 자산/공정 관리 플랫폼
diff --git a/package-lock.json b/package-lock.json
index 468993b..21375aa 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
- "name": "temp_app",
- "version": "0.0.0",
+ "name": "smartims",
+ "version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "temp_app",
- "version": "0.0.0",
+ "name": "smartims",
+ "version": "0.1.0",
"dependencies": {
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
diff --git a/package.json b/package.json
index 8bfd6aa..2597af3 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
{
- "name": "temp_app",
+ "name": "smartims",
"private": true,
"version": "0.1.0",
"type": "module",
diff --git a/server/add_is_active_column.js b/server/add_is_active_column.js
index ba895b3..45cb412 100644
--- a/server/add_is_active_column.js
+++ b/server/add_is_active_column.js
@@ -5,7 +5,7 @@ async function addColumn() {
process.env.DB_HOST = process.env.DB_HOST || 'localhost';
process.env.DB_USER = process.env.DB_USER || 'root';
process.env.DB_PASSWORD = process.env.DB_PASSWORD || 'password'; // Fallback or assume env is loaded
- process.env.DB_NAME = process.env.DB_NAME || 'smartasset_db';
+ process.env.DB_NAME = process.env.DB_NAME || 'sokuree_platform_dev';
console.log(`Connecting to database: ${process.env.DB_NAME} at ${process.env.DB_HOST}`);
diff --git a/server/config/public_key.pem b/server/config/public_key.pem
new file mode 100644
index 0000000..b6bea89
--- /dev/null
+++ b/server/config/public_key.pem
@@ -0,0 +1,8 @@
+-----BEGIN RSA PUBLIC KEY-----
+MIIBCgKCAQEAjwHtoUUGe+Ib7KA9a2KV00c3B+4a0UTeDu9+jCg+fzyo6qD1ii0p
+hJoASYkEVpHm5tMGJNMp/fJGyi3glPvfI8OFzs3JDgXWB6f4ao38fgAPtGE/x4Q2
+wsIHNvIY4scdFgokWYA1+k9//c4kszavOL8fRc85jOlkzUqzQLm3R1x34AVJEFdu
+oo29vuHlFSzOqJ3c36cNUDJsya+h6Um96zPfM3UA2LLYsYVclr7Bem0jvSNv/sKt
+hVcdG1QtlHTPAjQI4HZZf/51uIY/K2uXrVq3w4dplqMlLwIqDNb97Ire+Q+VaIe1
+n+GJBR5Jfa5soYsVjTwRId8VFvjcG0EJCQIDAQAB
+-----END RSA PUBLIC KEY-----
diff --git a/server/index.js b/server/index.js
index 27912be..2e67dd1 100644
--- a/server/index.js
+++ b/server/index.js
@@ -62,19 +62,33 @@ app.use('/uploads', express.static(uploadDir));
// Session Middleware
app.use(session({
- key: 'smartasset_sid',
- secret: process.env.SESSION_SECRET || 'smartasset_session_secret_key',
+ key: 'smartims_sid',
+ secret: process.env.SESSION_SECRET || 'smartims_session_secret_key',
store: sessionStore,
resave: false,
- saveUninitialized: true, // Save new sessions even if empty (helps with some client handshake issues)
+ saveUninitialized: false,
cookie: {
- httpOnly: true, // Prevent JS access
+ httpOnly: true,
secure: false, // Set true if using HTTPS
- maxAge: 1000 * 60 * 60 * 24, // 1 day
- sameSite: 'lax' // Recommended for better CSRF protection and reliability
+ maxAge: null, // Browser session by default
+ sameSite: 'lax'
}
}));
+// Dynamic Session Timeout Middleware
+app.use(async (req, res, next) => {
+ if (req.session && req.session.user) {
+ try {
+ const [rows] = await db.query("SELECT setting_value FROM system_settings WHERE setting_key = 'session_timeout'");
+ const timeoutMinutes = rows.length > 0 ? parseInt(rows[0].setting_value) : 60;
+ req.session.cookie.maxAge = timeoutMinutes * 60 * 1000;
+ } catch (err) {
+ console.error('Session timeout fetch error:', err);
+ }
+ }
+ next();
+});
+
// Apply CSRF Protection
app.use(csrfProtection);
diff --git a/server/modules/cctv/streamRelay.js b/server/modules/cctv/streamRelay.js
index 656fad0..dc3a6f9 100644
--- a/server/modules/cctv/streamRelay.js
+++ b/server/modules/cctv/streamRelay.js
@@ -127,7 +127,7 @@ class StreamRelay {
const transportMode = process.env.CCTV_TRANSPORT_OVERRIDE || camera.transport_mode || 'tcp';
// 3. Unique User-Agent to prevent session conflicts
- const userAgent = `SmartAsset-Relay-v1.0.8-${transportMode}`;
+ const userAgent = `SmartIMS-Relay-v1.0.8-${transportMode}`;
// Quality Scaling
let scaleFilter = [];
diff --git a/server/package-lock.json b/server/package-lock.json
index 6fff1f1..edf362f 100644
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "server",
- "version": "1.0.0",
+ "version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "server",
- "version": "1.0.0",
+ "version": "0.1.0",
"license": "ISC",
"dependencies": {
"cors": "^2.8.5",
diff --git a/server/routes/auth.js b/server/routes/auth.js
index 8a4de01..e7be500 100644
--- a/server/routes/auth.js
+++ b/server/routes/auth.js
@@ -10,7 +10,7 @@ const { generateToken } = require('../middleware/csrfMiddleware');
// Key must be 32 bytes for aes-256-cbc
// 'my_super_secret_key_manage_asset' is 32 chars?
// let's use a simpler approach to ensure length on startup or fallback
-const SECRET_KEY = process.env.ENCRYPTION_KEY || 'smartasset_secret_key_0123456789'; // 32 chars needed
+const SECRET_KEY = process.env.ENCRYPTION_KEY || 'smartims_secret_key_0123456789'; // 32 chars needed
// Ideally use a buffer from hex, but string is okay if 32 chars.
// Let's pad it to ensure stability if env is missing.
const keyBuffer = crypto.scryptSync(SECRET_KEY, 'salt', 32);
@@ -127,7 +127,7 @@ router.post('/logout', (req, res) => {
console.error('Logout error:', err);
return res.status(500).json({ success: false, message: 'Logout failed' });
}
- res.clearCookie('smartasset_sid'); // matching key in index.js
+ res.clearCookie('smartims_sid'); // matching key in index.js
res.json({ success: true, message: 'Logged out' });
});
});
diff --git a/server/routes/system.js b/server/routes/system.js
index 0866eec..6e38e79 100644
--- a/server/routes/system.js
+++ b/server/routes/system.js
@@ -8,7 +8,7 @@ const { generateLicense, verifyLicense } = require('../utils/licenseManager');
const { checkRemoteKey } = require('../utils/remoteLicense');
// Load Public Key for Verification
-const publicKeyPath = path.join(__dirname, '../public_key.pem');
+const publicKeyPath = path.join(__dirname, '../config/public_key.pem');
let publicKey = null;
try {
if (fs.existsSync(publicKeyPath)) {
@@ -20,25 +20,34 @@ try {
console.error('Error loading public key:', e);
}
-// 0. Server Configuration (Subscriber ID)
-router.get('/config', isAuthenticated, hasRole('admin'), async (req, res) => {
+// 0. Server Configuration (Subscriber ID & Session Timeout)
+router.get('/settings', isAuthenticated, hasRole('admin'), async (req, res) => {
try {
- const [rows] = await db.query("SELECT setting_value FROM system_settings WHERE setting_key = 'subscriber_id'");
- res.json({ subscriber_id: rows.length > 0 ? rows[0].setting_value : '' });
+ const [rows] = await db.query("SELECT setting_key, setting_value FROM system_settings WHERE setting_key IN ('subscriber_id', 'session_timeout')");
+ const settings = {};
+ rows.forEach(r => settings[r.setting_key] = r.setting_value);
+
+ res.json({
+ subscriber_id: settings.subscriber_id || '',
+ session_timeout: parseInt(settings.session_timeout) || 60 // Default 60 min
+ });
} catch (err) {
console.error(err);
res.status(500).json({ error: 'Database error' });
}
});
-router.post('/config', isAuthenticated, hasRole('admin'), async (req, res) => {
- const { subscriber_id } = req.body;
- if (!subscriber_id) return res.status(400).json({ error: 'Subscriber ID is required' });
+router.post('/settings', isAuthenticated, hasRole('admin'), async (req, res) => {
+ const { subscriber_id, session_timeout } = req.body;
try {
- const sql = `INSERT INTO system_settings (setting_key, setting_value) VALUES ('subscriber_id', ?) ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)`;
- await db.query(sql, [subscriber_id]);
- res.json({ message: 'Subscriber ID saved' });
+ if (subscriber_id !== undefined) {
+ await db.query(`INSERT INTO system_settings (setting_key, setting_value) VALUES ('subscriber_id', ?) ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)`, [subscriber_id]);
+ }
+ if (session_timeout !== undefined) {
+ await db.query(`INSERT INTO system_settings (setting_key, setting_value) VALUES ('session_timeout', ?) ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)`, [session_timeout.toString()]);
+ }
+ res.json({ message: 'Settings saved' });
} catch (err) {
console.error(err);
res.status(500).json({ error: 'Database error' });
@@ -150,10 +159,6 @@ router.post('/modules/:code/activate', isAuthenticated, hasRole('admin'), async
await db.query(sql, [code, names[code] || code, licenseKey, result.type, result.expiryDate, result.subscriberId]);
- // 5. Update Issued License Status (if exists in issued_licenses table)
- // This ensures CLI 'list' command reflects activation even if done via UI
- await db.query("UPDATE issued_licenses SET status = 'ACTIVE' WHERE license_key = ?", [licenseKey]);
-
res.json({ success: true, message: 'Module activated', type: result.type, expiry: result.expiryDate });
} catch (err) {
diff --git a/src/app/App.tsx b/src/app/App.tsx
index f4ae2bc..3317b8c 100644
--- a/src/app/App.tsx
+++ b/src/app/App.tsx
@@ -8,6 +8,7 @@ import { AssetRegisterPage } from '../modules/asset/pages/AssetRegisterPage';
import { AssetSettingsPage } from '../modules/asset/pages/AssetSettingsPage';
import { AssetDetailPage } from '../modules/asset/pages/AssetDetailPage';
import { UserManagementPage } from '../platform/pages/UserManagementPage';
+import { BasicSettingsPage } from '../platform/pages/BasicSettingsPage';
import { ProductionPage } from '../production/pages/ProductionPage';
import { MonitoringPage } from '../modules/cctv/pages/MonitoringPage';
import { AuthProvider } from '../shared/auth/AuthContext';
@@ -59,8 +60,9 @@ function App() {
{/* Admin Routes */}
} />
+ } />
} />
- Admin (Coming Soon)} />
+ } />
{/* Fallback */}
diff --git a/src/pages/auth/LoginPage.tsx b/src/pages/auth/LoginPage.tsx
index da46577..88fc3d0 100644
--- a/src/pages/auth/LoginPage.tsx
+++ b/src/pages/auth/LoginPage.tsx
@@ -37,11 +37,11 @@ export function LoginPage() {
- SmartAsset
- 통합 자산관리 시스템
+ Smart IMS
+ 통합 자산/공정 관리 플랫폼
-
-
© 2026 SmartAsset System. All rights reserved.
+
© 2026 Smart IMS System. All rights reserved.
diff --git a/src/platform/pages/BasicSettingsPage.tsx b/src/platform/pages/BasicSettingsPage.tsx
new file mode 100644
index 0000000..a52fffd
--- /dev/null
+++ b/src/platform/pages/BasicSettingsPage.tsx
@@ -0,0 +1,108 @@
+import { useState, useEffect } from 'react';
+import { Card } from '../../shared/ui/Card';
+import { Button } from '../../shared/ui/Button';
+import { Input } from '../../shared/ui/Input';
+import { apiClient } from '../../shared/api/client';
+import { Save, Clock, Info } from 'lucide-react';
+
+export function BasicSettingsPage() {
+ const [settings, setSettings] = useState({
+ subscriber_id: '',
+ session_timeout: 60
+ });
+ const [loading, setLoading] = useState(true);
+ const [saving, setSaving] = useState(false);
+
+ useEffect(() => {
+ fetchSettings();
+ }, []);
+
+ const fetchSettings = async () => {
+ try {
+ const res = await apiClient.get('/system/settings');
+ setSettings(res.data);
+ } catch (error) {
+ console.error('Failed to fetch settings', error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleSave = async () => {
+ setSaving(true);
+ try {
+ await apiClient.post('/system/settings', settings);
+ alert('설정이 저장되었습니다.');
+ } catch (error) {
+ console.error('Save failed', error);
+ alert('저장 중 오류가 발생했습니다.');
+ } finally {
+ setSaving(false);
+ }
+ };
+
+ if (loading) return 로딩 중...
;
+
+ return (
+
+
+
시스템 관리 - 기본 설정
+
플랫폼의 기본 운영 환경을 설정합니다.
+
+
+
+
+
+
+ 식별 정보
+
+
+
+
구독자 ID (서버 식별자)
+
setSettings({ ...settings, subscriber_id: e.target.value })}
+ placeholder="예: SOKR-2024-001"
+ />
+
라이선스 활성화 시 사용되는 서버 고유 식별자입니다.
+
+
+
+
+
+
+
+ 보안 설정
+
+
+
+
세션 타임아웃 (분)
+
+ setSettings({ ...settings, session_timeout: parseInt(e.target.value) })}
+ />
+ 분 동안 활동이 없으면 자동으로 로그아웃됩니다.
+
+
(최소 5분 ~ 최대 24시간)
+
+
+
+
+
+ }
+ >
+ {saving ? '저장 중...' : '설정 저장'}
+
+
+
+
+ );
+}
diff --git a/src/widgets/layout/MainLayout.tsx b/src/widgets/layout/MainLayout.tsx
index be0dd00..bd96b7a 100644
--- a/src/widgets/layout/MainLayout.tsx
+++ b/src/widgets/layout/MainLayout.tsx
@@ -28,7 +28,7 @@ export function MainLayout() {
@@ -49,6 +49,10 @@ export function MainLayout() {
{expandedModules.includes('sys_mgmt') && (
+
+
+ 기본 설정
+
사용자 관리
diff --git a/tools/license_manager.cjs b/tools/license_manager.cjs
index b8ea0ac..528dfcb 100644
--- a/tools/license_manager.cjs
+++ b/tools/license_manager.cjs
@@ -1,5 +1,5 @@
/**
- * SmartAsset License Management CLI
+ * SmartIMS License Management CLI
*
* Usage: node tools/license_manager.cjs [args]
*
@@ -33,7 +33,7 @@ const dbConfig = {
host: process.env.DB_HOST || 'localhost',
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD,
- database: process.env.DB_NAME || 'smartasset_db',
+ database: process.env.DB_NAME || 'sokuree_platform_dev',
port: process.env.DB_PORT || 3306
};
@@ -105,7 +105,7 @@ async function main() {
function printUsage() {
console.log(`
-SmartAsset License Manager
+SmartIMS License Manager
Usage: node tools/license_manager.cjs [args]
Commands: