diff --git a/server.js b/server.js index 8f666c9..60f04f7 100644 --- a/server.js +++ b/server.js @@ -9,6 +9,12 @@ dotenv.config(); const app = express(); app.use(cors()); app.use(express.json({ limit: '50mb' })); +app.use('/uploads', express.static('uploads')); // 업로드 파일 정적 서빙 + +// uploads 폴더가 없으면 생성 +if (!fs.existsSync('uploads')) { + fs.mkdirSync('uploads'); +} // MySQL Pool Configuration const pool = mysql.createPool({ @@ -327,6 +333,30 @@ app.post('/api/maps/save', (req, res) => { } catch (err) { handleError(res, err, 'SAVE MAPS'); } }); +// 7. File Upload API (Base64) +app.post('/api/upload', (req, res) => { + try { + const { fileName, fileData } = req.body; + if (!fileName || !fileData) return res.status(400).json({ error: 'FileName and FileData are required' }); + + // base64 데이터에서 실제 바이너리 추출 + const base64Data = fileData.replace(/^data:.*;base64,/, ""); + const buffer = Buffer.from(base64Data, 'base64'); + + // 고유한 파일명 생성 (타임스탬프 결합) + const timestamp = Date.now(); + const safeFileName = `${timestamp}_${fileName.replace(/[^a-zA-Z0-9._-]/g, '_')}`; + const filePath = `uploads/${safeFileName}`; + + fs.writeFileSync(filePath, buffer); + + console.log(`파일 업로드 성공: ${filePath}`); + res.json({ success: true, filePath: `/${filePath}`, fileName: safeFileName }); + } catch (err) { + handleError(res, err, 'FILE UPLOAD'); + } +}); + app.listen(3000, '0.0.0.0', () => { console.log('📡 ITAM BACKEND SERVER RUNNING ON PORT 3000 (V3 Normalized)'); }); diff --git a/src/components/Modal/HWModal.ts b/src/components/Modal/HWModal.ts index b06fd96..29a22f4 100644 --- a/src/components/Modal/HWModal.ts +++ b/src/components/Modal/HWModal.ts @@ -10,7 +10,6 @@ import { } from './ModalUtils'; import { CORP_LIST, LOCATION_DATA, CATEGORY_TYPE_MAP, HW_STATUS_LIST, ORG_LIST, IMAGE_LOCATIONS, TYPE_PREFIX_MAP } from './SharedData'; import { BaseModal } from './BaseModal'; -import { createIcons, X, History, Plus, Save, Paperclip, Calendar, Monitor, Cpu, Network, ShieldCheck } from 'lucide'; class HwAssetModal extends BaseModal { private dynamicMapConfig: Record = {}; @@ -20,12 +19,15 @@ class HwAssetModal extends BaseModal { } protected renderFrameHTML(): string { + // CSS 명세(modal.css)의 input 패딩(0.625rem)과 일치시켜 정렬을 완벽하게 잡는 스타일 + const standardBtnStyle = 'height: auto !important; padding: 0.625rem 1.25rem; font-size: 0.875rem; line-height: 1.2; display: inline-flex; align-items: center; justify-content: center;'; + return `