merge: merge origin/main into HW_Dashboard and resolve conflicts
This commit is contained in:
136
server.js
136
server.js
@@ -4,7 +4,7 @@ import cors from 'cors';
|
||||
import dotenv from 'dotenv';
|
||||
import fs from 'fs';
|
||||
|
||||
dotenv.config();
|
||||
dotenv.config({ override: true });
|
||||
|
||||
const app = express();
|
||||
app.use(cors());
|
||||
@@ -39,7 +39,7 @@ const CATEGORY_TABLE_MAP = {
|
||||
pc: 'asset_pc',
|
||||
server: 'asset_server',
|
||||
storage: 'asset_storage',
|
||||
network: 'asset_network',
|
||||
network: 'asset_remote',
|
||||
equipment: 'asset_equipment',
|
||||
officeSupplies: 'asset_office_supplies',
|
||||
survey: 'asset_survey',
|
||||
@@ -53,7 +53,7 @@ const CATEGORY_TABLE_MAP = {
|
||||
};
|
||||
|
||||
const ASSET_TABLES = [
|
||||
'asset_pc', 'asset_server', 'asset_storage', 'asset_network',
|
||||
'asset_pc', 'asset_server', 'asset_storage', 'asset_remote',
|
||||
'asset_equipment', 'asset_office_supplies', 'asset_survey', 'asset_vip'
|
||||
];
|
||||
|
||||
@@ -116,7 +116,10 @@ app.get('/api/assets/master', async (req, res) => {
|
||||
s.hw_status, s.model_name, s.mainboard, s.os, s.cpu, s.ram, s.gpu,
|
||||
s.monitoring, s.price, s.monitor_inch, s.serial_num,
|
||||
l.location, l.location_detail, l.location_photo, l.loc_x, l.loc_y,
|
||||
n.ip_address, n.mac_address, n.remote_tool, n.remote_id, n.remote_pw,
|
||||
(
|
||||
SELECT JSON_ARRAYAGG(JSON_OBJECT('type', net_type, 'name', net_name, 'val1', net_value1, 'val2', net_value2))
|
||||
FROM asset_remote WHERE asset_id = c.id AND is_active = 1
|
||||
) as remotes,
|
||||
(
|
||||
SELECT JSON_ARRAYAGG(JSON_OBJECT('type', disk_type, 'capacity', capacity, 'unit', unit, 'slot', slot_no))
|
||||
FROM asset_volume WHERE asset_id = c.id
|
||||
@@ -128,11 +131,6 @@ app.get('/api/assets/master', async (req, res) => {
|
||||
WHERE asset_id = c.id AND is_active = 1
|
||||
ORDER BY created_at DESC LIMIT 1
|
||||
)
|
||||
LEFT JOIN asset_network n ON n.id = (
|
||||
SELECT id FROM asset_network
|
||||
WHERE asset_id = c.id AND is_active = 1
|
||||
ORDER BY created_at DESC LIMIT 1
|
||||
)
|
||||
`);
|
||||
|
||||
const catMap = {
|
||||
@@ -173,6 +171,86 @@ app.post('/api/asset/:category/save', async (req, res) => {
|
||||
connection = await pool.getConnection();
|
||||
await connection.beginTransaction();
|
||||
|
||||
// 3.0 History Tracking & Auto Field Update
|
||||
const [oldCoreRows] = await connection.query('SELECT * FROM asset_core WHERE id = ?', [asset.id]);
|
||||
const [oldSpecRows] = await connection.query('SELECT * FROM asset_spec WHERE asset_id = ?', [asset.id]);
|
||||
const oldCore = oldCoreRows[0] || {};
|
||||
const oldSpec = oldSpecRows[0] || {};
|
||||
|
||||
console.log(`🔍 [History Check] ID: ${asset.id}`);
|
||||
console.log(` - Dept: [${oldCore.current_dept}] -> [${asset.current_dept}]`);
|
||||
console.log(` - User: [${oldCore.user_current}] -> [${asset.user_current}]`);
|
||||
|
||||
const historyLogs = [];
|
||||
const logDate = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
|
||||
const logUser = '관리자';
|
||||
|
||||
// 조직 변동 감지 (null/undefined/empty string 세이프 처리)
|
||||
const oldDept = oldCore.current_dept || '';
|
||||
const newDept = asset.current_dept || '';
|
||||
if (newDept !== '' && oldDept !== newDept) {
|
||||
asset.previous_dept = oldDept;
|
||||
historyLogs.push({
|
||||
event_type: 'DEPT_CHANGE',
|
||||
old_dept: oldDept || null,
|
||||
new_dept: newDept,
|
||||
details: `[조직 변동] ${oldDept || '(없음)'} -> ${newDept}`
|
||||
});
|
||||
}
|
||||
|
||||
// 사용자 변동 감지
|
||||
const oldUser = oldCore.user_current || '';
|
||||
const newUser = asset.user_current || '';
|
||||
if (newUser !== '' && oldUser !== newUser) {
|
||||
asset.previous_user = oldUser;
|
||||
historyLogs.push({
|
||||
event_type: 'USER_CHANGE',
|
||||
old_user: oldUser || null,
|
||||
new_user: newUser,
|
||||
details: `[사용자 변동] ${oldUser || '(없음)'} -> ${newUser}`
|
||||
});
|
||||
}
|
||||
|
||||
// 유형/용도 변경 감지
|
||||
const oldType = oldCore.asset_type || '';
|
||||
const newType = asset.asset_type || '';
|
||||
if (newType !== '' && oldType !== newType) {
|
||||
historyLogs.push({
|
||||
event_type: 'ROLE_CHANGE',
|
||||
details: `[유형 변경] ${oldType || '(없음)'} -> ${newType}`
|
||||
});
|
||||
}
|
||||
|
||||
const oldRole = oldCore.current_role || '';
|
||||
const newRole = asset.current_role || '';
|
||||
if (newRole !== '' && oldRole !== newRole) {
|
||||
historyLogs.push({
|
||||
event_type: 'ROLE_CHANGE',
|
||||
details: `[용도 변경] ${oldRole || '(없음)'} -> ${newRole}`
|
||||
});
|
||||
}
|
||||
|
||||
// 상태 변경 감지
|
||||
const oldStatus = oldSpec.hw_status || '';
|
||||
const newStatus = asset.hw_status || '';
|
||||
if (newStatus !== '' && oldStatus !== newStatus) {
|
||||
historyLogs.push({
|
||||
event_type: 'STATUS_CHANGE',
|
||||
details: `[상태 변경] ${oldStatus || '(없음)'} -> ${newStatus}`
|
||||
});
|
||||
}
|
||||
|
||||
console.log(` - Logs Generated: ${historyLogs.length}`);
|
||||
|
||||
// 로그 일괄 삽입
|
||||
for (const log of historyLogs) {
|
||||
await connection.query(
|
||||
`INSERT INTO asset_history (asset_id, event_type, old_dept, new_dept, old_user, new_user, details, log_date, log_user)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[asset.id, log.event_type, log.old_dept || null, log.new_dept || null, log.old_user || null, log.new_user || null, log.details, logDate, logUser]
|
||||
);
|
||||
}
|
||||
|
||||
// 3.1 asset_core
|
||||
const coreFields = ['id', 'asset_code', 'category', 'asset_type', 'current_role', 'asset_purpose', 'service_type', 'purchase_corp', 'purchase_date', 'purchase_amount', 'purchase_vendor', 'approval_document', 'memo', 'manager_primary', 'manager_secondary', 'current_dept', 'previous_dept', 'user_current', 'previous_user', 'emp_no', 'user_position'];
|
||||
const coreData = {};
|
||||
@@ -224,14 +302,36 @@ app.post('/api/asset/:category/save', async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 3.5 asset_network
|
||||
if (asset.ip_address || asset.mac_address || asset.remote_tool) {
|
||||
const [netActive] = await connection.query('SELECT * FROM asset_network WHERE asset_id = ? AND is_active = 1', [asset.id]);
|
||||
const isChanged = netActive.length === 0 || netActive[0].ip_address !== asset.ip_address || netActive[0].mac_address !== asset.mac_address || netActive[0].remote_tool !== asset.remote_tool || netActive[0].remote_id !== asset.remote_id || netActive[0].remote_pw !== asset.remote_pw;
|
||||
if (isChanged) {
|
||||
await connection.query('UPDATE asset_network SET is_active = 0, deactivated_at = NOW() WHERE asset_id = ? AND is_active = 1', [asset.id]);
|
||||
await connection.query(`INSERT INTO asset_network (asset_id, ip_address, mac_address, remote_tool, remote_id, remote_pw, is_active) VALUES (?, ?, ?, ?, ?, ?, 1)`,
|
||||
[asset.id, asset.ip_address, asset.mac_address, asset.remote_tool, asset.remote_id, asset.remote_pw]);
|
||||
// 3.5 asset_remote (Dynamic Array Logic)
|
||||
if (asset.remotes) {
|
||||
try {
|
||||
let nets = typeof asset.remotes === 'string' ? JSON.parse(asset.remotes) : asset.remotes;
|
||||
if (Array.isArray(nets)) {
|
||||
await connection.query('UPDATE asset_remote SET is_active = 0, deactivated_at = NOW() WHERE asset_id = ? AND is_active = 1', [asset.id]);
|
||||
for (const n of nets) {
|
||||
if (n.type) {
|
||||
await connection.query(
|
||||
'INSERT INTO asset_remote (asset_id, net_type, net_name, net_value1, net_value2, is_active) VALUES (?, ?, ?, ?, ?, 1)',
|
||||
[asset.id, n.type, n.name || '', n.val1 || '', n.val2 || '']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(e) { console.error('Remote data parse error', e); }
|
||||
} else {
|
||||
// Fallback for UI that hasn't sent the networks array yet
|
||||
if (asset.ip_address || asset.mac_address || asset.remote_tool) {
|
||||
const [netActive] = await connection.query('SELECT * FROM asset_remote WHERE asset_id = ? AND is_active = 1', [asset.id]);
|
||||
const isChanged = netActive.length === 0 || netActive[0].net_value1 !== asset.ip_address || netActive[0].net_value2 !== asset.mac_address || netActive[0].net_name !== asset.remote_tool;
|
||||
if (isChanged) {
|
||||
await connection.query('UPDATE asset_remote SET is_active = 0, deactivated_at = NOW() WHERE asset_id = ? AND is_active = 1', [asset.id]);
|
||||
if (asset.ip_address || asset.mac_address) {
|
||||
await connection.query('INSERT INTO asset_remote (asset_id, net_type, net_name, net_value1, net_value2, is_active) VALUES (?, ?, ?, ?, ?, 1)', [asset.id, 'IP', '기본망', asset.ip_address, asset.mac_address]);
|
||||
}
|
||||
if (asset.remote_tool || asset.remote_id || asset.remote_pw) {
|
||||
await connection.query('INSERT INTO asset_remote (asset_id, net_type, net_name, net_value1, net_value2, is_active) VALUES (?, ?, ?, ?, ?, 1)', [asset.id, 'REMOTE', asset.remote_tool, asset.remote_id, asset.remote_pw]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,7 +438,7 @@ app.delete('/api/asset/:category/:id', async (req, res) => {
|
||||
|
||||
try {
|
||||
const connection = await pool.getConnection();
|
||||
// For asset_core, ON DELETE CASCADE will handle spec, location, network, volume
|
||||
// For asset_core, ON DELETE CASCADE will handle spec, location, remote, volume
|
||||
await connection.query(`DELETE FROM ${table} WHERE id = ?`, [id]);
|
||||
connection.release();
|
||||
console.log(`🗑️ [ASSET DELETE] Category: ${category}, ID: ${id}`);
|
||||
|
||||
Reference in New Issue
Block a user