UNPKG

debug-time-machine-cli

Version:

πŸš€ Debug Time Machine CLI - μ™„μ „ μžλ™ν™”λœ React 디버깅 도ꡬ

278 lines (238 loc) β€’ 7.39 kB
#!/usr/bin/env node // λ²ˆλ“€λœ λ°±μ—”λ“œ μ„œλ²„ (apps/backendλ₯Ό 기반으둜 함) const express = require('express'); const http = require('http'); const WebSocket = require('ws'); const cors = require('cors'); const helmet = require('helmet'); const compression = require('compression'); const path = require('path'); const app = express(); const port = process.env.PORT || 4000; // 미듀웨어 μ„€μ • app.use(helmet()); app.use(compression()); app.use(cors({ origin: true, credentials: true })); app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true, limit: '10mb' })); // ν΄λΌμ΄μ–ΈνŠΈ μ—°κ²° 관리 const clients = new Map(); // Health check μ—”λ“œν¬μΈνŠΈ app.get('/health', (req, res) => { res.json({ status: 'ok', timestamp: Date.now(), clients: clients.size, uptime: process.uptime() }); }); // Info μ—”λ“œν¬μΈνŠΈ app.get('/info', (req, res) => { res.json({ name: 'Debug Time Machine Backend', version: '1.0.0', environment: process.env.NODE_ENV || 'development', port: port, clients: clients.size, uptime: process.uptime() }); }); // HTTP μ„œλ²„ 생성 const server = http.createServer(app); // WebSocket μ„œλ²„ μ„€μ • const wss = new WebSocket.Server({ server, path: '/ws' }); // ν΄λΌμ΄μ–ΈνŠΈ ID 생성 function generateClientId() { return `client_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`; } // λΈŒλ‘œλ“œμΊμŠ€νŠΈ ν•¨μˆ˜ function broadcast(message, excludeClient = null) { const messageStr = typeof message === 'string' ? message : JSON.stringify(message); clients.forEach((clientInfo, ws) => { if (ws !== excludeClient && ws.readyState === WebSocket.OPEN) { try { ws.send(messageStr); } catch (error) { console.error('λΈŒλ‘œλ“œμΊμŠ€νŠΈ 였λ₯˜:', error.message); clients.delete(ws); } } }); } // WebSocket μ—°κ²° 처리 wss.on('connection', (ws, req) => { const clientId = generateClientId(); const clientIP = req.headers['x-forwarded-for'] || req.connection.remoteAddress; const clientInfo = { id: clientId, ip: clientIP, connectedAt: new Date(), lastSeen: new Date(), userAgent: req.headers['user-agent'], messagesReceived: 0, messagesSent: 0 }; clients.set(ws, clientInfo); console.log(`πŸ”— ν΄λΌμ΄μ–ΈνŠΈ μ—°κ²°: ${clientId} (${clientIP})`); console.log(`πŸ“Š 총 μ—°κ²°λœ ν΄λΌμ΄μ–ΈνŠΈ: ${clients.size}`); // Welcome λ©”μ‹œμ§€ 전솑 const welcomeMessage = { type: 'CONNECTION', payload: { type: 'welcome', clientId: clientId, serverTime: Date.now(), message: 'Debug Time Machine에 μ—°κ²°λ˜μ—ˆμŠ΅λ‹ˆλ‹€!' }, timestamp: Date.now() }; try { ws.send(JSON.stringify(welcomeMessage)); clientInfo.messagesSent++; } catch (error) { console.error('Welcome λ©”μ‹œμ§€ 전솑 μ‹€νŒ¨:', error.message); } // λ©”μ‹œμ§€ μˆ˜μ‹  처리 ws.on('message', (data) => { try { const message = JSON.parse(data.toString()); clientInfo.lastSeen = new Date(); clientInfo.messagesReceived++; console.log(`πŸ“¨ [${clientId}] λ©”μ‹œμ§€ μˆ˜μ‹ : ${message.type}`); // λ©”μ‹œμ§€ νƒ€μž…λ³„ 처리 switch (message.type) { case 'CONNECTION': console.log(`🀝 [${clientId}] μ—°κ²° 확인:`, message.payload?.type); break; case 'USER_ACTION': console.log(`πŸ‘† [${clientId}] μ‚¬μš©μž μ•‘μ…˜:`, message.payload?.actionType); // λ‹€λ₯Έ ν΄λΌμ΄μ–ΈνŠΈλ“€μ—κ²Œ λΈŒλ‘œλ“œμΊμŠ€νŠΈ broadcast({ type: 'USER_ACTION_BROADCAST', payload: message.payload, from: clientId, timestamp: Date.now() }, ws); break; case 'STATE_CHANGE': console.log(`πŸ”„ [${clientId}] μƒνƒœ λ³€κ²½:`, message.payload?.componentName); broadcast({ type: 'STATE_CHANGE_BROADCAST', payload: message.payload, from: clientId, timestamp: Date.now() }, ws); break; case 'ERROR': console.log(`πŸ”΄ [${clientId}] μ—λŸ¬:`, message.payload?.message); broadcast({ type: 'ERROR_BROADCAST', payload: message.payload, from: clientId, timestamp: Date.now() }, ws); break; case 'PING': // PONG 응닡 ws.send(JSON.stringify({ type: 'PONG', payload: {}, timestamp: Date.now() })); clientInfo.messagesSent++; break; case 'PONG': console.log(`πŸ“ [${clientId}] PONG μˆ˜μ‹ `); break; default: console.log(`❓ [${clientId}] μ•Œ 수 μ—†λŠ” λ©”μ‹œμ§€ νƒ€μž…: ${message.type}`); } } catch (error) { console.error(`❌ [${clientId}] λ©”μ‹œμ§€ νŒŒμ‹± 였λ₯˜:`, error.message); console.error('원본 데이터:', data.toString()); } }); // μ—°κ²° μ’…λ£Œ 처리 ws.on('close', (code, reason) => { clients.delete(ws); console.log(`πŸ”Œ ν΄λΌμ΄μ–ΈνŠΈ μ—°κ²° ν•΄μ œ: ${clientId} (μ½”λ“œ: ${code}, 이유: ${reason || 'μ—†μŒ'})`); console.log(`πŸ“Š 남은 ν΄λΌμ΄μ–ΈνŠΈ: ${clients.size}`); // μ—°κ²° ν•΄μ œ μ•Œλ¦Ό λΈŒλ‘œλ“œμΊμŠ€νŠΈ broadcast({ type: 'CLIENT_DISCONNECTED', payload: { clientId: clientId, timestamp: Date.now() } }); }); // μ—λŸ¬ 처리 ws.on('error', (error) => { console.error(`❌ [${clientId}] WebSocket 였λ₯˜:`, error.message); clients.delete(ws); }); }); // Ping 타이머 (30μ΄ˆλ§ˆλ‹€) setInterval(() => { const pingMessage = { type: 'PING', payload: {}, timestamp: Date.now() }; clients.forEach((clientInfo, ws) => { if (ws.readyState === WebSocket.OPEN) { try { ws.send(JSON.stringify(pingMessage)); clientInfo.messagesSent++; } catch (error) { console.error('PING 전솑 μ‹€νŒ¨:', error.message); clients.delete(ws); } } else { clients.delete(ws); } }); if (clients.size > 0) { console.log(`πŸ“ PING 전솑 (${clients.size}개 ν΄λΌμ΄μ–ΈνŠΈ)`); } }, 30000); // μ„œλ²„ μ‹œμž‘ server.listen(port, () => { console.log(''); console.log('πŸš€ Debug Time Machine Backend Server'); console.log(`πŸ“ Port: ${port}`); console.log(`πŸ”— Health: http://localhost:${port}/health`); console.log(`πŸ”— WebSocket: ws://localhost:${port}/ws`); console.log(`πŸ• Started: ${new Date().toISOString()}`); console.log(''); }); // Graceful shutdown process.on('SIGTERM', () => { console.log('πŸ›‘ SIGTERM μ‹ ν˜Έ μˆ˜μ‹ , μ„œλ²„ μ’…λ£Œ 쀑...'); // λͺ¨λ“  ν΄λΌμ΄μ–ΈνŠΈμ—κ²Œ μ’…λ£Œ μ•Œλ¦Ό broadcast({ type: 'SERVER_SHUTDOWN', payload: { message: 'μ„œλ²„κ°€ μ’…λ£Œλ©λ‹ˆλ‹€.', timestamp: Date.now() } }); // μ—°κ²° μ’…λ£Œ clients.forEach((_, ws) => { ws.close(1001, 'Server shutdown'); }); server.close(() => { console.log('βœ… μ„œλ²„κ°€ μ •μƒμ μœΌλ‘œ μ’…λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); process.exit(0); }); }); process.on('SIGINT', () => { console.log('\nπŸ›‘ SIGINT μ‹ ν˜Έ μˆ˜μ‹ , μ„œλ²„ μ’…λ£Œ 쀑...'); process.emit('SIGTERM'); });