import express from 'express'; import cors from 'cors'; import mysql from 'mysql2/promise'; import dotenv from 'dotenv'; import path from 'path'; import { fileURLToPath } from 'url'; dotenv.config(); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const app = express(); app.use(cors()); app.use(express.json({ limit: '50mb' })); // MySQL Connection Pool const pool = mysql.createPool({ host: process.env.DB_HOST || 'localhost', user: process.env.DB_USER || 'itam_user', password: process.env.DB_PASSWORD || 'itam_pw', database: process.env.DB_NAME || 'itam_db', waitForConnections: true, connectionLimit: 10, queueLimit: 0 }); // Helper for DB updates async function ensureTables() { try { // Cloud_Assets Table await pool.query(` CREATE TABLE IF NOT EXISTS cloud_assets ( id VARCHAR(50) PRIMARY KEY, platform_name VARCHAR(100), corp VARCHAR(50), dept VARCHAR(100), purpose VARCHAR(255), account_name VARCHAR(100), payment_method VARCHAR(50), payment_date VARCHAR(50), card_number VARCHAR(50), monthly_fee VARCHAR(50), note TEXT ) `); // Logs Table await pool.query(` CREATE TABLE IF NOT EXISTS asset_logs ( id VARCHAR(50) PRIMARY KEY, asset_id VARCHAR(50), date VARCHAR(20), details TEXT, user VARCHAR(50), INDEX(asset_id) ) `); console.log('✅ Cloud & Logs tables ensured.'); } catch (err) { console.error('❌ Error ensuring tables:', err); } } ensureTables(); // --- API Endpoints --- // Get Master Data (Multi-tab) app.get('/api/master-data', async (req, res) => { try { const [pc] = await pool.query('SELECT * FROM pc_assets'); const [server] = await pool.query('SELECT * FROM server_assets'); const [storage] = await pool.query('SELECT * FROM storage_assets'); const [equip] = await pool.query('SELECT * FROM equip_assets'); const [mobile] = await pool.query('SELECT * FROM mobile_assets'); const [subSw] = await pool.query('SELECT * FROM sw_sub_assets'); const [permSw] = await pool.query('SELECT * FROM sw_perm_assets'); const [cloud] = await pool.query('SELECT * FROM cloud_assets'); const [swUsers] = await pool.query('SELECT * FROM sw_users'); const [logs] = await pool.query('SELECT * FROM asset_logs ORDER BY date DESC'); res.json({ pc, server, storage, equip, mobile, subSw, permSw, cloud, swUsers, logs }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Save Hardware Asset (PC, Server, Storage, etc.) app.post('/api/hardware/save', async (req, res) => { const asset = req.body; const type = asset.type; let table = ''; if (type === '개인PC' || type === 'PC') table = 'pc_assets'; else if (type === '서버') table = 'server_assets'; else if (type === '스토리지') table = 'storage_assets'; else if (type === '모바일' || type === '모바일기기') table = 'mobile_assets'; else table = 'equip_assets'; try { // Check if exists const [rows] = await pool.query(`SELECT id FROM ${table} WHERE id = ?`, [asset.id]); const data = { ...asset }; delete data.id; delete data.type; if (rows.length > 0) { await pool.query(`UPDATE ${table} SET ? WHERE id = ?`, [data, asset.id]); } else { await pool.query(`INSERT INTO ${table} SET ?`, [{ id: asset.id, ...data }]); } res.json({ success: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Save Software Asset app.post('/api/software/save', async (req, res) => { const asset = req.body; const table = asset.type === '구독SW' ? 'sw_sub_assets' : (asset.type === '영구SW' ? 'sw_perm_assets' : 'cloud_assets'); try { const [rows] = await pool.query(`SELECT id FROM ${table} WHERE id = ?`, [asset.id]); const data = { ...asset }; delete data.id; delete data.type; if (rows.length > 0) { await pool.query(`UPDATE ${table} SET ? WHERE id = ?`, [data, asset.id]); } else { await pool.query(`INSERT INTO ${table} SET ?`, [{ id: asset.id, ...data }]); } res.json({ success: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Delete Asset app.delete('/api/asset/:type/:id', async (req, res) => { const { type, id } = req.params; let table = ''; if (type === '개인PC' || type === 'PC') table = 'pc_assets'; else if (type === '서버') table = 'server_assets'; else if (type === '스토리지') table = 'storage_assets'; else if (type === '모바일' || type === '모바일기기') table = 'mobile_assets'; else if (type === '전산비품' || type === '기타자산') table = 'equip_assets'; else if (type === '구독SW') table = 'sw_sub_assets'; else if (type === '영구SW') table = 'sw_perm_assets'; else if (type === '클라우드') table = 'cloud_assets'; try { await pool.query(`DELETE FROM ${table} WHERE id = ?`, [id]); // Also delete logs and users if needed if (table.includes('sw')) await pool.query('DELETE FROM sw_users WHERE sw_id = ?', [id]); await pool.query('DELETE FROM asset_logs WHERE asset_id = ?', [id]); res.json({ success: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Log Save app.post('/api/logs/save', async (req, res) => { const log = req.body; try { const [rows] = await pool.query('SELECT id FROM asset_logs WHERE id = ?', [log.id]); if (rows.length > 0) { await pool.query('UPDATE asset_logs SET ? WHERE id = ?', [log, log.id]); } else { await pool.query('INSERT INTO asset_logs SET ?', [log]); } res.json({ success: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // SW User Save app.post('/api/sw-users/save', async (req, res) => { const user = req.body; try { const [rows] = await pool.query('SELECT id FROM sw_users WHERE id = ?', [user.id]); if (rows.length > 0) { await pool.query('UPDATE sw_users SET ? WHERE id = ?', [user, user.id]); } else { await pool.query('INSERT INTO sw_users SET ?', [user]); } res.json({ success: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); app.post('/api/sw-users/batch', async (req, res) => { const { swId, users } = req.body; try { await pool.query('DELETE FROM sw_users WHERE sw_id = ?', [swId]); for (const u of users) { await pool.query('INSERT INTO sw_users SET ?', { ...u, sw_id: swId }); } res.json({ success: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // 자산번호 생성 API app.get('/api/generate-asset-code', async (req, res) => { const { prefix } = req.query; if (!prefix) return res.status(400).json({ error: 'Prefix is required' }); try { const tables = [ 'pc_assets', 'server_assets', 'storage_assets', 'equip_assets', 'mobile_assets', 'sw_sub_assets', 'sw_perm_assets' ]; let maxNum = 0; for (const table of tables) { const [rows] = await pool.query(`SELECT asset_code FROM ${table} WHERE asset_code LIKE ?`, [`${prefix}%`]); rows.forEach(r => { const numPart = r.asset_code.replace(prefix, ''); const num = parseInt(numPart); if (!isNaN(num) && num > maxNum) maxNum = num; }); } const nextCode = `${prefix}${(maxNum + 1).toString().padStart(3, '0')}`; res.json({ nextCode }); } catch (err) { res.status(500).json({ error: err.message }); } }); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`🚀 ITAM Dedicated API Server running on http://localhost:${PORT}`); });