225 lines
6.6 KiB
JavaScript
225 lines
6.6 KiB
JavaScript
import express from 'express';
|
|
import mysql from 'mysql2/promise';
|
|
import cors from 'cors';
|
|
import dotenv from 'dotenv';
|
|
|
|
dotenv.config();
|
|
|
|
const app = express();
|
|
const PORT = process.env.PORT || 3000;
|
|
|
|
app.use(cors());
|
|
app.use(express.json({ limit: '50mb' }));
|
|
|
|
// DB 연결 풀 생성
|
|
const pool = mysql.createPool({
|
|
host: process.env.DB_HOST,
|
|
user: process.env.DB_USER,
|
|
password: process.env.DB_PASS,
|
|
database: process.env.DB_NAME,
|
|
port: parseInt(process.env.DB_PORT || '3306'),
|
|
waitForConnections: true,
|
|
connectionLimit: 10,
|
|
queueLimit: 0
|
|
});
|
|
|
|
// --- API Routes ---
|
|
|
|
// 1. 하드웨어 자산 조회
|
|
app.get('/api/hw', async (req, res) => {
|
|
try {
|
|
const [rows] = await pool.query('SELECT * FROM hw_assets');
|
|
// DB 컬럼명을 프론트엔드 인터페이스(한글)에 맞게 매핑
|
|
const mapped = rows.map(r => ({
|
|
id: r.id,
|
|
type: r.type,
|
|
법인: r.corp,
|
|
자산코드: r.asset_code,
|
|
명칭: r.asset_name,
|
|
위치: r.location,
|
|
현사용조직: r.current_org,
|
|
이전사용조직: r.prev_org,
|
|
담당자_정: r.manager_main,
|
|
관리자: r.manager_main,
|
|
담당자_부: r.manager_sub,
|
|
IP주소: r.ip_address,
|
|
IP2: r.ip_address2,
|
|
MACaddress: r.mac_address,
|
|
OS: r.os,
|
|
CPU: r.cpu,
|
|
RAM: r.ram,
|
|
SSD1: r.storage1,
|
|
SSD2: r.storage2,
|
|
모델명: r.model_name,
|
|
구매일: r.purchase_date,
|
|
금액: r.price,
|
|
납품업체: r.vendor,
|
|
품의서명: r.doc_name,
|
|
용도: r.asset_name, // 서버의 경우 명칭을 용도로 사용
|
|
상세: r.remarks,
|
|
원격접속: r.remote_tool,
|
|
서버ID: r.server_id,
|
|
서버PW: r.server_pw,
|
|
모니터링: r.monitoring,
|
|
비고: r.remarks
|
|
}));
|
|
res.json(mapped);
|
|
} catch (err) {
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
// 2. 하드웨어 자산 일괄 저장 (항상 덮어쓰기)
|
|
app.post('/api/hw/batch', async (req, res) => {
|
|
const assets = req.body;
|
|
const connection = await pool.getConnection();
|
|
try {
|
|
await connection.beginTransaction();
|
|
|
|
await connection.query('DELETE FROM hw_assets');
|
|
|
|
if (assets.length > 0) {
|
|
const sql = `
|
|
INSERT INTO hw_assets (
|
|
id, type, corp, asset_code, asset_name, location, current_org, prev_org,
|
|
manager_main, manager_sub, ip_address, ip_address2, mac_address, os,
|
|
cpu, ram, storage1, storage2, model_name, purchase_date, price,
|
|
vendor, doc_name, remote_tool, server_id, server_pw, monitoring, remarks
|
|
) VALUES ?
|
|
`;
|
|
const values = assets.map(a => [
|
|
a.id, a.type, a.법인, a.자산코드, a.명칭 || a.용도, a.위치, a.현사용조직, a.이전사용조직,
|
|
a.담당자_정 || a.관리자, a.담당자_부, a.IP주소, a.IP2, a.MACaddress, a.OS,
|
|
a.CPU, a.RAM, a.SSD1, a.SSD2, a.모델명, a.구매일, a.금액,
|
|
a.납품업체, a.품의서명, a.원격접속, a.서버ID, a.서버PW, a.모니터링, a.비고 || a.상세
|
|
]);
|
|
await connection.query(sql, [values]);
|
|
}
|
|
|
|
await connection.commit();
|
|
res.json({ success: true, count: assets.length, mode: 'overwrite' });
|
|
} catch (err) {
|
|
await connection.rollback();
|
|
res.status(500).json({ error: err.message });
|
|
} finally {
|
|
connection.release();
|
|
}
|
|
});
|
|
|
|
// 3. 소프트웨어 자산 조회
|
|
app.get('/api/sw', async (req, res) => {
|
|
try {
|
|
const [rows] = await pool.query('SELECT * FROM sw_assets');
|
|
const mapped = rows.map(r => ({
|
|
id: r.id,
|
|
type: r.type,
|
|
분야: r.category,
|
|
법인: r.corp,
|
|
부서: r.dept,
|
|
제품명: r.product_name,
|
|
구매일: r.purchase_date,
|
|
구독일: r.subscription_date,
|
|
유지보수여부: !!r.maintenance_status,
|
|
금액: r.price,
|
|
수량: r.quantity,
|
|
계정명: r.account_id,
|
|
납품업체: r.vendor,
|
|
비고: r.remarks
|
|
}));
|
|
res.json(mapped);
|
|
} catch (err) {
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
// 4. 소프트웨어 자산 일괄 저장 (항상 덮어쓰기)
|
|
app.post('/api/sw/batch', async (req, res) => {
|
|
const assets = req.body;
|
|
const connection = await pool.getConnection();
|
|
try {
|
|
await connection.beginTransaction();
|
|
|
|
await connection.query('DELETE FROM sw_assets');
|
|
|
|
if (assets.length > 0) {
|
|
const sql = `
|
|
INSERT INTO sw_assets (
|
|
id, type, category, corp, dept, product_name, purchase_date,
|
|
subscription_date, maintenance_status, price, quantity,
|
|
account_id, vendor, remarks
|
|
) VALUES ?
|
|
`;
|
|
const values = assets.map(a => [
|
|
a.id, a.type, a.분야, a.법인, a.부서, a.제품명, a.구매일,
|
|
a.구독일, a.유지보수여부 ? 1 : 0, a.금액, a.수량,
|
|
a.계정명, a.납품업체, a.비고
|
|
]);
|
|
await connection.query(sql, [values]);
|
|
}
|
|
|
|
await connection.commit();
|
|
res.json({ success: true, count: assets.length, mode: 'overwrite' });
|
|
} catch (err) {
|
|
await connection.rollback();
|
|
res.status(500).json({ error: err.message });
|
|
} finally {
|
|
connection.release();
|
|
}
|
|
});
|
|
|
|
// 5. SW 사용자 매핑 조회
|
|
app.get('/api/sw-users', async (req, res) => {
|
|
try {
|
|
const [rows] = await pool.query('SELECT * FROM sw_users');
|
|
const mapped = rows.map(r => ({
|
|
id: r.id,
|
|
swId: r.sw_id,
|
|
법인: r.corp,
|
|
부서: r.dept,
|
|
팀: r.team,
|
|
직위: r.position,
|
|
이름: r.name,
|
|
사용기간: r.usage_period,
|
|
신청서명: r.doc_name
|
|
}));
|
|
res.json(mapped);
|
|
} catch (err) {
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
// 6. SW 사용자 일괄 저장 (항상 덮어쓰기)
|
|
app.post('/api/sw-users/batch', async (req, res) => {
|
|
const users = req.body;
|
|
const connection = await pool.getConnection();
|
|
try {
|
|
await connection.beginTransaction();
|
|
|
|
await connection.query('DELETE FROM sw_users');
|
|
|
|
if (users.length > 0) {
|
|
const sql = `
|
|
INSERT INTO sw_users (
|
|
id, sw_id, corp, dept, team, position, name, usage_period, doc_name
|
|
) VALUES ?
|
|
`;
|
|
const values = users.map(u => [
|
|
u.id, u.swId, u.법인, u.부서, u.팀, u.직위, u.이름, u.사용기간, u.신청서명
|
|
]);
|
|
await connection.query(sql, [values]);
|
|
}
|
|
|
|
await connection.commit();
|
|
res.json({ success: true, count: users.length, mode: 'overwrite' });
|
|
} catch (err) {
|
|
await connection.rollback();
|
|
res.status(500).json({ error: err.message });
|
|
} finally {
|
|
connection.release();
|
|
}
|
|
});
|
|
|
|
app.listen(PORT, () => {
|
|
console.log(`📡 ITAM API Server running on http://localhost:${PORT}`);
|
|
});
|