const { createProxyMiddleware } = require('http-proxy-middleware'); const path = require('path'); const fs = require('fs'); /** * Development proxy (CRA - only active with `npm start`). * * .env / .env.local: * REACT_APP_CORE_URL=https://restreamer.nextream.sytes.net * REACT_APP_YTDLP_URL=http://192.168.1.20:8282 * REACT_APP_FB_SERVER_URL=http://localhost:3002 (optional, default shown) */ const CORE_TARGET = process.env.REACT_APP_CORE_URL || 'http://localhost:8080'; const YTDLP_TARGET = process.env.REACT_APP_YTDLP_URL || 'http://localhost:8282'; const FB_SERVER_TARGET = process.env.REACT_APP_FB_SERVER_URL || 'http://localhost:3002'; console.log('\n[setupProxy] ─────────────────────────────────────'); console.log(`[setupProxy] Core → ${CORE_TARGET}`); console.log(`[setupProxy] yt-dlp → ${YTDLP_TARGET}`); console.log(`[setupProxy] fb-server → ${FB_SERVER_TARGET}`); console.log('[setupProxy] ─────────────────────────────────────\n'); let coreUrl; try { coreUrl = new URL(CORE_TARGET); } catch (e) { console.error(`[setupProxy] Invalid REACT_APP_CORE_URL: "${CORE_TARGET}"`); coreUrl = new URL('http://localhost:8080'); } // Shared proxy instance for all Core paths (/api, /memfs, /diskfs) const coreProxy = createProxyMiddleware({ target: CORE_TARGET, changeOrigin: true, secure: false, ws: false, onProxyReq: (proxyReq) => { proxyReq.setHeader('Host', coreUrl.host); }, onError: (err, req, res) => { console.error(`[setupProxy] Core proxy error: ${err.code} — ${err.message}`); if (!res.headersSent) { res.writeHead(502, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Proxy error', target: CORE_TARGET, message: err.message })); } }, }); module.exports = function (app) { // Restreamer Core REST API app.use('/api', coreProxy); // Restreamer Core in-memory FS (HLS playlists, snapshots) app.use('/memfs', coreProxy); // Restreamer Core disk FS app.use('/diskfs', coreProxy); // yt-dlp stream extractor: /yt-stream/{VIDEO_ID} → /stream/{VIDEO_ID} // yt-dlp puede tardar hasta 30-60s en extraer la URL — timeout extendido app.use( '/yt-stream', createProxyMiddleware({ target: YTDLP_TARGET, changeOrigin: true, secure: false, ws: false, proxyTimeout: 120000, // 120s — yt-dlp puede tardar bastante timeout: 120000, pathRewrite: { '^/yt-stream': '/stream' }, onError: (err, req, res) => { console.error(`[setupProxy] yt-dlp proxy error: ${err.code} — ${err.message}`); if (!res.headersSent) { res.writeHead(502, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Proxy error', target: YTDLP_TARGET, message: err.message })); } }, }), ); // Facebook OAuth server: /fb-server/* → http://localhost:3002/* // Routes: /fb/config, /fb/accounts, /fb/exchange, /fb/refresh/:id, /fb/upgrade app.use( '/fb-server', createProxyMiddleware({ target: FB_SERVER_TARGET, changeOrigin: true, secure: false, pathRewrite: { '^/fb-server': '' }, onError: (err, req, res) => { console.error(`[setupProxy] fb-server proxy error: ${err.code} — ${err.message}`); if (!res.headersSent) { res.writeHead(502, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'fb-server unavailable', message: err.message })); } }, }), ); // OAuth2 callback: sirve el HTML de callback para YouTube y otras plataformas app.get('/oauth2callback', (req, res) => { const callbackFile = path.join(__dirname, '..', 'public', 'oauth2callback.html'); if (fs.existsSync(callbackFile)) { res.sendFile(callbackFile); } else { res.status(404).send('oauth2callback.html not found'); } }); // Facebook OAuth2 callback popup app.get('/oauth/facebook/callback.html', (req, res) => { const callbackFile = path.join(__dirname, '..', 'public', 'oauth', 'facebook', 'callback.html'); if (fs.existsSync(callbackFile)) { res.sendFile(callbackFile); } else { res.status(404).send('Facebook callback.html not found'); } }); };