UNPKG

debug-time-machine-cli

Version:

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

320 lines (278 loc) β€’ 11.1 kB
#!/usr/bin/env node /** * Debug Time Machine Auto-Injector - μˆ˜μ •λœ 버전 * * ERR_CONTENT_DECODING_FAILED μ—λŸ¬λ₯Ό ν•΄κ²°ν•˜λŠ” μ™„μ „νžˆ μˆ˜μ •λœ ν”„λ‘μ‹œ μ„œλ²„ */ const http = require('http'); const fs = require('fs'); const path = require('path'); const { URL } = require('url'); const injectorPort = process.env.INJECTOR_PORT || 3001; const userAppPort = process.env.USER_APP_PORT || 3000; console.log('πŸš€ Debug Time Machine Injector Proxy μ‹œμž‘ (μˆ˜μ •λœ 버전)'); console.log(`πŸ“ ν”„λ‘μ‹œ 포트: ${injectorPort}`); console.log(`🎯 μ‚¬μš©μž μ•± 포트: ${userAppPort}`); // Auto-injector 슀크립트 λ‘œλ“œ const autoInjectorPath = path.join(__dirname, 'auto-injector.js'); let injectorScript = ''; if (fs.existsSync(autoInjectorPath)) { injectorScript = fs.readFileSync(autoInjectorPath, 'utf8'); console.log('βœ… Auto-Injector 슀크립트 λ‘œλ“œλ¨'); console.log(`πŸ“„ 슀크립트 크기: ${injectorScript.length} bytes`); } else { console.error('❌ Auto-Injector 슀크립트λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€:', autoInjectorPath); console.error('πŸ“ ν˜„μž¬ 디렉토리:', __dirname); console.error('πŸ“‚ 디렉토리 λ‚΄μš©:', fs.readdirSync(__dirname)); process.exit(1); } // ν”„λ‘μ‹œ μ„œλ²„ 생성 const server = http.createServer((req, res) => { console.log(`πŸ“‘ μš”μ²­: ${req.method} ${req.url}`); // μ‚¬μš©μž μ•±μœΌλ‘œ ν”„λ‘μ‹œ μš”μ²­ 생성 const proxyOptions = { hostname: 'localhost', port: userAppPort, path: req.url, method: req.method, headers: { ...req.headers, host: `localhost:${userAppPort}`, // μ˜¬λ°”λ₯Έ host 헀더 μ„€μ • // μ••μΆ• κ΄€λ ¨ 헀더 제거 (문제 λ°©μ§€) 'accept-encoding': 'identity', // gzip λŒ€μ‹  identity μ‚¬μš© } }; // μ••μΆ• κ΄€λ ¨ 헀더 μ™„μ „ 제거 delete proxyOptions.headers['accept-encoding']; delete proxyOptions.headers['content-encoding']; const proxyReq = http.request(proxyOptions, (proxyRes) => { console.log(`πŸ“₯ 응닡: ${proxyRes.statusCode} ${proxyRes.headers['content-type'] || 'unknown'} ${proxyRes.headers['content-encoding'] || 'no-encoding'}`); const contentType = proxyRes.headers['content-type'] || ''; const isHtml = contentType.toLowerCase().includes('text/html'); const contentEncoding = proxyRes.headers['content-encoding']; // μ••μΆ•λœ 응닡은 μ ˆλŒ€ μ²˜λ¦¬ν•˜μ§€ μ•Šκ³  λ°”λ‘œ 전달 if (contentEncoding && contentEncoding !== 'identity') { console.log('πŸ“¦ μ••μΆ•λœ 응닡 감지 - 슀트림으둜 직접 전달'); // 헀더 κ·ΈλŒ€λ‘œ λ³΅μ‚¬ν•΄μ„œ 전달 res.writeHead(proxyRes.statusCode, proxyRes.headers); // 슀트림으둜 직접 νŒŒμ΄ν”„ proxyRes.pipe(res); return; } // HTML이 μ•„λ‹Œ κ²½μš°μ—λ„ 슀트림으둜 λ°”λ‘œ 전달 if (!isHtml) { console.log('πŸ“„ λΉ„HTML 응닡 - 슀트림으둜 직접 전달'); // 헀더 κ·ΈλŒ€λ‘œ λ³΅μ‚¬ν•΄μ„œ 전달 res.writeHead(proxyRes.statusCode, proxyRes.headers); // 슀트림으둜 직접 νŒŒμ΄ν”„ proxyRes.pipe(res); return; } // HTML μ‘λ‹΅λ§Œ λ²„νΌλ§ν•΄μ„œ 처리 let body = ''; // 응닡 데이터 μˆ˜μ§‘ proxyRes.on('data', (chunk) => { body += chunk.toString('utf8'); // λͺ…μ‹œμ μœΌλ‘œ UTF-8 인코딩 μ‚¬μš© }); proxyRes.on('end', () => { try { // 응닡 헀더 λ³΅μ‚¬ν•˜λ˜ λ¬Έμ œκ°€ 될 수 μžˆλŠ” 헀더듀 μ™„μ „ 제거 const responseHeaders = { ...proxyRes.headers }; // μ••μΆ• 및 인코딩 κ΄€λ ¨ 헀더 μ™„μ „ 제거 delete responseHeaders['content-length']; delete responseHeaders['content-encoding']; delete responseHeaders['transfer-encoding']; delete responseHeaders['content-range']; delete responseHeaders['accept-ranges']; // μΊμ‹œ λ°©μ§€ 헀더 μΆ”κ°€ (개발 ν™˜κ²½μš©) responseHeaders['cache-control'] = 'no-cache, no-store, must-revalidate'; responseHeaders['pragma'] = 'no-cache'; responseHeaders['expires'] = '0'; // Content-Type λͺ…μ‹œμ  μ„€μ • responseHeaders['content-type'] = 'text/html; charset=utf-8'; // HTML에 슀크립트 μ£Όμž… μ‹œλ„ if (body && body.toLowerCase().includes('<head')) { console.log('🎯 HTML 응닡 감지, Auto-Injector μ£Όμž… 쀑...'); const injectedBody = body.replace( /<head[^>]*>/i, `$& <!-- Debug Time Machine Auto-Injector (Fixed Version) --> <script> console.log('πŸš€ Debug Time Machine Auto-Injector λ‘œλ”© (μ‚¬μš©μž μ•± - μˆ˜μ •λœ 버전)...'); try { ${injectorScript} } catch (error) { console.error('❌ Auto-Injector μ΄ˆκΈ°ν™” μ‹€νŒ¨:', error); } </script> <!-- End Debug Time Machine Auto-Injector -->` ); if (injectedBody !== body && injectedBody.length > body.length) { console.log('βœ… Auto-Injector μŠ€ν¬λ¦½νŠΈκ°€ μ‚¬μš©μž 앱에 μ£Όμž…λ˜μ—ˆμŠ΅λ‹ˆλ‹€'); console.log(`πŸ“Š 원본 크기: ${body.length} bytes β†’ μ£Όμž… ν›„: ${injectedBody.length} bytes`); // μˆ˜μ •λœ content-length μ„€μ • const finalBody = injectedBody; responseHeaders['content-length'] = Buffer.byteLength(finalBody, 'utf8').toString(); res.writeHead(proxyRes.statusCode, responseHeaders); res.end(finalBody); } else { console.log('⚠️ 슀크립트 μ£Όμž… μ‹€νŒ¨ - 원본 응닡 전달'); console.log('πŸ“„ 응닡 μ‹œμž‘ λΆ€λΆ„:', body.substring(0, 200)); responseHeaders['content-length'] = Buffer.byteLength(body, 'utf8').toString(); res.writeHead(proxyRes.statusCode, responseHeaders); res.end(body); } } else { // <head> νƒœκ·Έκ°€ μ—†λŠ” HTML console.log('⚠️ <head> νƒœκ·Έκ°€ μ—†λŠ” HTML 응닡 - 원본 전달'); responseHeaders['content-length'] = Buffer.byteLength(body, 'utf8').toString(); res.writeHead(proxyRes.statusCode, responseHeaders); res.end(body); } } catch (error) { console.error('❌ 응닡 처리 쀑 였λ₯˜:', error); // 였λ₯˜ λ°œμƒ μ‹œ 원본 응닡 κ·ΈλŒ€λ‘œ 전달 const safeHeaders = { ...proxyRes.headers }; delete safeHeaders['content-encoding']; res.writeHead(proxyRes.statusCode, safeHeaders); res.end(body); } }); }); // ν”„λ‘μ‹œ μš”μ²­ μ—λŸ¬ 처리 proxyReq.on('error', (error) => { console.error('❌ ν”„λ‘μ‹œ μš”μ²­ μ—λŸ¬:', error.message); if (!res.headersSent) { res.writeHead(500, { 'Content-Type': 'text/html; charset=utf-8' }); res.end(` <!DOCTYPE html> <html> <head> <title>Debug Time Machine - ν”„λ‘μ‹œ μ—λŸ¬</title> <meta charset="utf-8"> <style> body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; text-align: center; padding: 2rem; background: #f5f5f5; margin: 0; } .container { max-width: 600px; margin: 0 auto; background: white; padding: 2rem; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .error { color: #e74c3c; background: #fee; padding: 1rem; border-radius: 5px; margin: 1rem 0; border: 1px solid #fcc; } .info { color: #3498db; background: #eff8ff; padding: 1rem; border-radius: 5px; margin: 1rem 0; border: 1px solid #bde4ff; } code { background: #f8f9fa; padding: 2px 6px; border-radius: 3px; font-family: 'Monaco', 'Menlo', monospace; } </style> </head> <body> <div class="container"> <h1>πŸ•°οΈ Debug Time Machine</h1> <div class="error"> <h3>ν”„λ‘μ‹œ μ—°κ²° μ—λŸ¬</h3> <p>μ‚¬μš©μž μ•±(포트 ${userAppPort})에 μ—°κ²°ν•  수 μ—†μŠ΅λ‹ˆλ‹€.</p> <p><strong>μ—λŸ¬:</strong> ${error.message}</p> </div> <div class="info"> <h3>πŸ”§ ν•΄κ²° 방법</h3> <p>1. μ‚¬μš©μž 앱이 <code>http://localhost:${userAppPort}</code>μ—μ„œ μ‹€ν–‰ 쀑인지 ν™•μΈν•˜μ„Έμš”</p> <p>2. ν„°λ―Έλ„μ—μ„œ λ‹€μŒ λͺ…λ Ήμ–΄λ‘œ 확인: <code>curl http://localhost:${userAppPort}</code></p> <p>3. μ‚¬μš©μž 앱을 λ‹€μ‹œ μ‹œμž‘ν•΄λ³΄μ„Έμš”</p> <p>4. 포트 ${userAppPort}κ°€ λ‹€λ₯Έ μ•±μ—μ„œ μ‚¬μš© 쀑인지 ν™•μΈν•˜μ„Έμš”</p> </div> </div> </body> </html> `); } }); // μš”μ²­ 데이터 전달 req.on('data', (chunk) => { proxyReq.write(chunk); }); req.on('end', () => { proxyReq.end(); }); // ν΄λΌμ΄μ–ΈνŠΈ μš”μ²­ μ—λŸ¬ 처리 req.on('error', (error) => { console.error('❌ ν΄λΌμ΄μ–ΈνŠΈ μš”μ²­ μ—λŸ¬:', error.message); proxyReq.destroy(); }); }); // μ„œλ²„ μ‹œμž‘ server.listen(injectorPort, () => { console.log('πŸŽ‰ Debug Time Machine Injector Proxy μ‹œμž‘λ¨! (μˆ˜μ •λœ 버전)'); console.log(`πŸ“ ν”„λ‘μ‹œ URL: http://localhost:${injectorPort}`); console.log(`🎯 μ‚¬μš©μž μ•± URL: http://localhost:${userAppPort}`); console.log(''); console.log('πŸ’‘ μ‚¬μš©λ²•:'); console.log(` λΈŒλΌμš°μ €μ—μ„œ http://localhost:${injectorPort} λ₯Ό μ—΄λ©΄`); console.log(` μžλ™μœΌλ‘œ Debug Time Machine이 μ£Όμž…λœ μ‚¬μš©μž 앱을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.`); console.log(''); console.log('πŸ”§ μˆ˜μ •μ‚¬ν•­:'); console.log(' - μ••μΆ• 헀더 μ™„μ „ 제거'); console.log(' - UTF-8 인코딩 λͺ…μ‹œ'); console.log(' - μ—λŸ¬ 처리 κ°•ν™”'); console.log(' - 헀더 좩돌 λ°©μ§€'); console.log(''); // μ—°κ²° ν…ŒμŠ€νŠΈ setTimeout(() => { console.log('πŸ” μ‚¬μš©μž μ•± μ—°κ²° ν…ŒμŠ€νŠΈ...'); const testReq = http.get(`http://localhost:${userAppPort}`, (testRes) => { console.log(`βœ… μ‚¬μš©μž μ•± μ—°κ²° 확인됨 (μƒνƒœ: ${testRes.statusCode})`); }).on('error', (error) => { console.log(`⚠️ μ‚¬μš©μž μ•± μ—°κ²° μ‹€νŒ¨: ${error.message}`); console.log(' μ‚¬μš©μž 앱이 아직 μ‹œμž‘λ˜μ§€ μ•Šμ•˜μ„ 수 μžˆμŠ΅λ‹ˆλ‹€.'); }); }, 2000); }); // μ—λŸ¬ 처리 server.on('error', (error) => { if (error.code === 'EADDRINUSE') { console.error(`❌ 포트 ${injectorPort}κ°€ 이미 μ‚¬μš© μ€‘μž…λ‹ˆλ‹€.`); console.error(' λ‹€λ₯Έ 포트λ₯Ό μ‚¬μš©ν•˜κ±°λ‚˜ κΈ°μ‘΄ ν”„λ‘œμ„ΈμŠ€λ₯Ό μ’…λ£Œν•˜μ„Έμš”.'); } else { console.error('❌ μ„œλ²„ μ‹œμž‘ μ—λŸ¬:', error); } process.exit(1); }); // μ’…λ£Œ 처리 process.on('SIGINT', () => { console.log('\n🧹 Debug Time Machine Injector Proxy μ’…λ£Œ 쀑...'); server.close(() => { console.log('βœ… μ„œλ²„κ°€ μ •μƒμ μœΌλ‘œ μ’…λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); process.exit(0); }); }); process.on('SIGTERM', () => { console.log('\n🧹 Debug Time Machine Injector Proxy μ’…λ£Œ 쀑...'); server.close(() => { console.log('βœ… μ„œλ²„κ°€ μ •μƒμ μœΌλ‘œ μ’…λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); process.exit(0); }); });