Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .env.docker
Original file line number Diff line number Diff line change
@@ -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
14 changes: 14 additions & 0 deletions DOCKER.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions client/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
59 changes: 49 additions & 10 deletions server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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...`);
Expand Down Expand Up @@ -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);
}
}
});

Expand Down