diff --git a/.env.docker b/.env.docker index dd4c61d..9cf773e 100644 --- a/.env.docker +++ b/.env.docker @@ -1,9 +1,9 @@ # Docker Environment Configuration -SERVER_PORT=3000 +SERVER_PORT=3001 SERVER_HOST=0.0.0.0 SERVER_PROTOCOL=http -VITE_API_BASE_URL=http://localhost:3000 -FRONTEND_URL=http://localhost:5173 +VITE_API_BASE_URL=http://localhost:3001 +FRONTEND_URL=http://localhost:5174 OPENSUBTITLES_API_URL=https://rest.opensubtitles.org SUBTITLE_SEEKER_API_URL=https://api.subtitleseeker.com NODE_ENV=development diff --git a/DOCKER.md b/DOCKER.md index c3dd700..b3f5b63 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -229,6 +229,20 @@ docker-compose exec seedbox-backend sh docker-compose exec seedbox-frontend sh ``` +### Notes on `.well-known` and uTP + +- `.well-known` exceptions: the frontend nginx config explicitly allows requests to `/.well-known/` (for ACME challenges, browser metadata like `/ .well-known/appspecific/...`, etc.) before the rule that denies access to hidden files. This is intentional — it permits necessary metadata while still blocking other dot-files. If you change this behavior, be aware of the trade-off between letting specific well-known resources through and exposing hidden files. + +- uTP (utp-native) and stability: WebTorrent optionally uses the native `utp-native` addon for uTP transport. On some minimal/musl (Alpine) images this native addon can cause segmentation faults (container exit code 139). To maximize stability in Docker images, uTP is disabled by default; enable it only if you understand the environment and need uTP: + +```bash +# Enable uTP (may require additional build tools or a glibc-based image) +WEBTORRENT_ENABLE_UTP=true docker-compose up --build +``` + +If you need uTP and run into native build issues, consider using a glibc-based image (e.g., Debian/Ubuntu node images) or building `utp-native` with the appropriate toolchain. + + ## 🔄 Updates & Maintenance ### Update Application diff --git a/client/nginx.conf b/client/nginx.conf index 254c0c3..d7e3141 100644 --- a/client/nginx.conf +++ b/client/nginx.conf @@ -82,6 +82,16 @@ http { add_header Content-Type text/plain; } + # Allow ACME and browser metadata under /.well-known + # This must come before the hidden-files deny rule so that + # legitimate requests like /.well-known/acme-challenge/* and + # browser metadata (e.g. /.well-known/appspecific/...) are served. + location ^~ /.well-known/ { + access_log off; + # Serve files if present, otherwise return 404 + try_files $uri $uri/ =404; + } + # Disable access to hidden files location ~ /\. { deny all; diff --git a/server/index.js b/server/index.js index b716c15..947acac 100644 --- a/server/index.js +++ b/server/index.js @@ -153,12 +153,27 @@ const client = new WebTorrent({ // Avoid going offline by keeping connections alive keepSeeding: true, - + // Throttle UDP traffic to avoid triggering anti-DoS mechanisms - utp: true // Use uTP protocol which is more network-friendly + // NOTE: do NOT enable uTP here by default because the native + // `utp-native` addon can cause segmentation faults in some + // musl/alpine environments. uTP is now opt-in via the + // WEBTORRENT_ENABLE_UTP environment variable. }) }); +// Determine whether to enable uTP from environment (opt-in) +const enableUtp = process.env.WEBTORRENT_ENABLE_UTP === 'true'; + +console.log(`WebTorrent uTP enabled: ${enableUtp ? 'yes' : 'no'} (use WEBTORRENT_ENABLE_UTP=true to enable)`); + +// If uTP is disabled, override client options to avoid loading native bindings +if (!enableUtp) { + // Some WebTorrent internals may try to load utp-native. Setting + // `utp: false` here avoids that. + client.utp = false; +} + // UNIVERSAL STORAGE SYSTEM - Multiple ways to find torrents const torrents = {}; // Active torrent objects by infoHash const torrentIds = {}; // Original torrent IDs by infoHash @@ -1703,11 +1718,17 @@ app.get('/api/torrents/:identifier/imdb', async (req, res) => { // Add a timeout to prevent hanging requests from external APIs const requestTimeout = setTimeout(() => { console.log(`âąī¸ IMDB request timed out for: ${identifier}`); - if (!res.headersSent) { - res.status(503).json({ - error: 'Request timeout', - message: 'IMDB data request timed out, try again later' - }); + try { + if (!res.headersSent) { + res.status(503).json({ + error: 'Request timeout', + message: 'IMDB data request timed out, try again later' + }); + } else { + console.log('âąī¸ IMDB timeout: response already sent, skipping timeout response'); + } + } catch (e) { + console.log('âš ī¸ Error sending timeout response for IMDB endpoint:', e.message); } }, 15000); // 15 second timeout for API calls @@ -1729,7 +1750,9 @@ app.get('/api/torrents/:identifier/imdb', async (req, res) => { if (!torrent) { clearTimeout(requestTimeout); if (debugLevel) console.log(`❌ Torrent not found for identifier: ${identifier}`); - return res.status(404).json({ error: 'Torrent not found' }); + if (!res.headersSent) return res.status(404).json({ error: 'Torrent not found' }); + console.log('❌ Torrent not found but response already sent'); + return; } if (debugLevel) console.log(`đŸŽŦ Found torrent: ${torrent.name}, fetching IMDB data...`); @@ -1772,12 +1795,28 @@ app.get('/api/torrents/:identifier/imdb', async (req, res) => { global[`${cacheKey}_time`] = now; clearTimeout(requestTimeout); - res.json(response); + try { + if (!res.headersSent) { + res.json(response); + } else { + console.log('â„šī¸ IMDB handler: response already sent, skipping final json send'); + } + } catch (e) { + console.log('âš ī¸ Error sending IMDB response:', e.message); + } } catch (error) { clearTimeout(requestTimeout); console.error(`❌ IMDB endpoint failed:`, error.message); - res.status(500).json({ error: 'Failed to get IMDB data: ' + error.message }); + try { + if (!res.headersSent) { + res.status(500).json({ error: 'Failed to get IMDB data: ' + error.message }); + } else { + console.log('❌ IMDB endpoint error occurred but response already sent'); + } + } catch (e) { + console.log('âš ī¸ Error sending IMDB error response:', e.message); + } } });