feat(turn): self-hosted coturn support + time-limited creds + failure UX
- /api/ice: when TURN_SECRET is set, mint short-lived HMAC credentials (coturn use-auth-secret) so no permanent password is exposed and the relay can't be abused. Static TURN_USERNAME/CREDENTIAL still supported. - share.html: connection watchdog + clear "couldn't connect on this network" message instead of a blank screen when no path can be established. - deploy/coturn: ready-to-run turnserver.conf + docker-compose + README for hosting our own TURN on a VM we own (flat cost, no per-GB billing). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
+15
-7
@@ -122,16 +122,24 @@ route('GET', '/api/setup-state', async (req, res) => {
|
||||
json(res, 200, { registrationOpen: !anyUser || process.env.ALLOW_REGISTRATION === '1' });
|
||||
});
|
||||
|
||||
// ICE servers for WebRTC. Always includes a public STUN; adds managed TURN if
|
||||
// configured via env (TURN_URLS comma-separated, TURN_USERNAME, TURN_CREDENTIAL).
|
||||
// ICE servers for WebRTC. Always includes a public STUN; adds our TURN relay if
|
||||
// configured. Two credential modes:
|
||||
// - Shared secret (recommended, coturn `use-auth-secret`): set TURN_SECRET and we mint
|
||||
// time-limited credentials per request (no permanent password is ever handed out, so
|
||||
// outsiders can't reuse your relay). Optional TURN_TTL seconds (default 24h).
|
||||
// - Static: set TURN_USERNAME + TURN_CREDENTIAL for a fixed long-term credential.
|
||||
route('GET', '/api/ice', async (req, res) => {
|
||||
const iceServers = [{ urls: 'stun:stun.l.google.com:19302' }];
|
||||
if (process.env.TURN_URLS) {
|
||||
iceServers.push({
|
||||
urls: process.env.TURN_URLS.split(',').map((u) => u.trim()).filter(Boolean),
|
||||
username: process.env.TURN_USERNAME || '',
|
||||
credential: process.env.TURN_CREDENTIAL || '',
|
||||
});
|
||||
const urls = process.env.TURN_URLS.split(',').map((u) => u.trim()).filter(Boolean);
|
||||
let username = process.env.TURN_USERNAME || '';
|
||||
let credential = process.env.TURN_CREDENTIAL || '';
|
||||
if (process.env.TURN_SECRET) {
|
||||
const ttl = parseInt(process.env.TURN_TTL || '86400', 10);
|
||||
username = String(Math.floor(Date.now() / 1000) + ttl); // coturn expects "<expiry>"
|
||||
credential = require('crypto').createHmac('sha1', process.env.TURN_SECRET).update(username).digest('base64');
|
||||
}
|
||||
iceServers.push({ urls, username, credential });
|
||||
}
|
||||
json(res, 200, { iceServers });
|
||||
});
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren