Refine Restreamer Core address resolution and enhance proxy error handling
This commit is contained in:
parent
0ee2475a1e
commit
234f59ff13
37
src/index.js
37
src/index.js
@ -10,14 +10,15 @@ import theme from './theme';
|
||||
import RestreamerUI from './RestreamerUI';
|
||||
|
||||
/**
|
||||
* Resolve the Restreamer Core address using the following priority:
|
||||
* Resolve the Restreamer Core address:
|
||||
*
|
||||
* 1. ?address= query param (explicit override, useful for dev/testing)
|
||||
* 2. window.__RESTREAMER_CONFIG__.CORE_ADDRESS (set in public/config.js — editable at runtime)
|
||||
* 3. Auto-detect from window.location:
|
||||
* a. If served under /ui/ path → strip it (standard production setup inside Core)
|
||||
* b. Otherwise use empty string → all /api/* calls are relative, handled by
|
||||
* CRA proxy (setupProxy.js) in development or the same-origin server in production.
|
||||
* 1. ?address= → explicit URL param (always wins)
|
||||
* 2. window.__RESTREAMER_CONFIG__ → public/config.js (Docker/production runtime)
|
||||
* 3. /ui/ path detection → strip /ui/ suffix (embedded inside Core)
|
||||
* 4. process.env.REACT_APP_CORE_URL → .env / .env.local (CRA injects at build time)
|
||||
* Used both for the proxy (Node.js) AND as the
|
||||
* browser-side address so hostname is never empty.
|
||||
* 5. window.location.origin → same-origin fallback (production same host)
|
||||
*/
|
||||
const urlParams = new URLSearchParams(window.location.search.substring(1));
|
||||
const runtimeConfig = window.__RESTREAMER_CONFIG__ || {};
|
||||
@ -25,17 +26,23 @@ const runtimeConfig = window.__RESTREAMER_CONFIG__ || {};
|
||||
let address;
|
||||
|
||||
if (urlParams.has('address')) {
|
||||
// Priority 1: explicit ?address= param
|
||||
// 1. Explicit override via URL param
|
||||
address = urlParams.get('address');
|
||||
} else if (runtimeConfig.CORE_ADDRESS && runtimeConfig.CORE_ADDRESS.trim() !== '') {
|
||||
// Priority 2: runtime config.js (Docker / production override)
|
||||
address = runtimeConfig.CORE_ADDRESS.trim();
|
||||
} else if (window.location.pathname.endsWith('/ui/')) {
|
||||
// Priority 3a: served inside Core at /ui/
|
||||
address = window.location.protocol + '//' + window.location.host + window.location.pathname.replace(/ui\/$/, '');
|
||||
// 2. Runtime config.js — Docker / production without /ui/ path
|
||||
address = runtimeConfig.CORE_ADDRESS.trim().replace(/\/$/, '');
|
||||
} else if (window.location.pathname.includes('/ui/')) {
|
||||
// 3. Embedded inside Core at /ui/ path
|
||||
address = window.location.protocol + '//' + window.location.host +
|
||||
window.location.pathname.replace(/\/ui\/.*$/, '');
|
||||
} else if (process.env.REACT_APP_CORE_URL && process.env.REACT_APP_CORE_URL.trim() !== '') {
|
||||
// 4. .env / .env.local — CRA injects at build time.
|
||||
// Use window.location.origin so all /api/* and /memfs/* calls go through
|
||||
// the CRA dev proxy (setupProxy.js) which forwards them to REACT_APP_CORE_URL.
|
||||
address = window.location.origin;
|
||||
} else {
|
||||
// Priority 3b: development proxy or same-origin production
|
||||
address = '';
|
||||
// 5. Same-origin production (Core and UI on the same host/port)
|
||||
address = window.location.origin;
|
||||
}
|
||||
|
||||
createRoot(document.getElementById('root')).render(
|
||||
|
||||
@ -1,44 +1,57 @@
|
||||
const { createProxyMiddleware } = require('http-proxy-middleware');
|
||||
|
||||
/**
|
||||
* Development proxy configuration (CRA - only active with `npm start`).
|
||||
* Development proxy (CRA - only active with `npm start`).
|
||||
*
|
||||
* Set in your .env.local:
|
||||
* REACT_APP_CORE_URL=https://restreamer.nextream.sytes.net <- Restreamer Core
|
||||
* REACT_APP_YTDLP_URL=http://192.168.1.20:8282 <- yt-dlp stream extractor
|
||||
* .env / .env.local:
|
||||
* REACT_APP_CORE_URL=https://restreamer.nextream.sytes.net
|
||||
* REACT_APP_YTDLP_URL=http://192.168.1.20:8282
|
||||
*/
|
||||
const CORE_TARGET = process.env.REACT_APP_CORE_URL || 'http://localhost:8080';
|
||||
const YTDLP_TARGET = process.env.REACT_APP_YTDLP_URL || 'http://localhost:8282';
|
||||
|
||||
console.log(`[setupProxy] /api → ${CORE_TARGET}`);
|
||||
console.log(`[setupProxy] /yt-stream → ${YTDLP_TARGET}`);
|
||||
console.log('\n[setupProxy] ─────────────────────────────────────');
|
||||
console.log(`[setupProxy] Core → ${CORE_TARGET}`);
|
||||
console.log(`[setupProxy] yt-dlp → ${YTDLP_TARGET}`);
|
||||
console.log('[setupProxy] ─────────────────────────────────────\n');
|
||||
|
||||
const coreUrl = new URL(CORE_TARGET);
|
||||
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) {
|
||||
// Proxy /api/* → Restreamer Core
|
||||
app.use(
|
||||
'/api',
|
||||
createProxyMiddleware({
|
||||
target: CORE_TARGET,
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
ws: false, // NO WebSocket — Core uses HTTP REST only
|
||||
onProxyReq: (proxyReq) => {
|
||||
// Fix Host header for HTTPS targets (required by some reverse proxies)
|
||||
proxyReq.setHeader('Host', coreUrl.host);
|
||||
},
|
||||
onError: (err, req, res) => {
|
||||
console.error(`[setupProxy] /api error: ${err.message}`);
|
||||
if (!res.headersSent) {
|
||||
res.writeHead(502, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Proxy error', message: err.message }));
|
||||
}
|
||||
},
|
||||
}),
|
||||
);
|
||||
// Restreamer Core REST API
|
||||
app.use('/api', coreProxy);
|
||||
|
||||
// Proxy /yt-stream/* → yt-dlp extractor service → /stream/{VIDEO_ID}
|
||||
// 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}
|
||||
app.use(
|
||||
'/yt-stream',
|
||||
createProxyMiddleware({
|
||||
@ -48,13 +61,12 @@ module.exports = function (app) {
|
||||
ws: false,
|
||||
pathRewrite: { '^/yt-stream': '/stream' },
|
||||
onError: (err, req, res) => {
|
||||
console.error(`[setupProxy] /yt-stream error: ${err.message}`);
|
||||
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', message: err.message }));
|
||||
res.end(JSON.stringify({ error: 'Proxy error', target: YTDLP_TARGET, message: err.message }));
|
||||
}
|
||||
},
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user