Add turn server

This commit is contained in:
Joey Yakimowich-Payne 2026-01-19 11:13:13 -07:00
commit d38aeb2f44
No known key found for this signature in database
GPG key ID: DDF6AF5B21B407D4
6 changed files with 163 additions and 9 deletions

View file

@ -4,7 +4,7 @@ import { useAuth } from 'react-oidc-context';
import { Quiz, Player, GameState, GameRole, NetworkMessage, AnswerOption, Question, GenerateQuizOptions, ProcessedDocument, GameConfig, DEFAULT_GAME_CONFIG, PointsBreakdown } from '../types';
import { generateQuiz } from '../services/geminiService';
import { QUESTION_TIME, QUESTION_TIME_MS, PLAYER_COLORS, calculatePointsWithBreakdown, getPlayerRank } from '../constants';
import { Peer, DataConnection } from 'peerjs';
import { Peer, DataConnection, PeerOptions } from 'peerjs';
import { uniqueNamesGenerator, adjectives, animals } from 'unique-names-generator';
const BACKEND_URL = import.meta.env.VITE_BACKEND_URL || 'http://localhost:3001';
@ -12,6 +12,42 @@ const SESSION_STORAGE_KEY = 'kaboot_session';
const DRAFT_QUIZ_KEY = 'kaboot_draft_quiz';
const STATE_SYNC_INTERVAL = 5000;
// ICE server configuration for WebRTC NAT traversal
// TURN servers are required for peers behind restrictive NATs/firewalls
const getIceServers = (): RTCIceServer[] => {
const servers: RTCIceServer[] = [
// Google's public STUN servers (free, for NAT discovery)
{ urls: 'stun:stun.l.google.com:19302' },
{ urls: 'stun:stun1.l.google.com:19302' },
];
// Add TURN server if configured (required for restrictive NATs)
const turnUrl = import.meta.env.VITE_TURN_URL;
const turnUsername = import.meta.env.VITE_TURN_USERNAME;
const turnCredential = import.meta.env.VITE_TURN_CREDENTIAL;
if (turnUrl) {
servers.push({
urls: turnUrl,
username: turnUsername || '',
credential: turnCredential || '',
});
}
return servers;
};
const getPeerOptions = (id?: string): PeerOptions => {
const options: PeerOptions = {
config: {
iceServers: getIceServers(),
iceCandidatePoolSize: 10,
},
debug: import.meta.env.DEV ? 2 : 0, // Errors + warnings in dev
};
return options;
};
interface StoredSession {
pin: string;
role: GameRole;
@ -313,7 +349,7 @@ export const useGame = () => {
const handleClientDataRef = useRef<(data: NetworkMessage) => void>(() => {});
const setupHostPeer = (pin: string, onReady: (peerId: string) => void) => {
const peer = new Peer(`kaboot-${pin}`);
const peer = new Peer(`kaboot-${pin}`, getPeerOptions());
peerRef.current = peer;
peer.on('open', (id) => {
@ -332,7 +368,7 @@ export const useGame = () => {
peer.on('error', (err) => {
if (err.type === 'unavailable-id') {
peer.destroy();
const newPeer = new Peer();
const newPeer = new Peer(getPeerOptions());
peerRef.current = newPeer;
newPeer.on('open', (id) => {
@ -499,7 +535,7 @@ export const useGame = () => {
return;
}
const peer = new Peer();
const peer = new Peer(getPeerOptions());
peerRef.current = peer;
peer.on('open', (id) => {
@ -1126,7 +1162,7 @@ export const useGame = () => {
return;
}
const peer = new Peer();
const peer = new Peer(getPeerOptions());
peerRef.current = peer;
peer.on('open', (id) => {
@ -1173,7 +1209,7 @@ export const useGame = () => {
peerRef.current.destroy();
}
const peer = new Peer();
const peer = new Peer(getPeerOptions());
peerRef.current = peer;
peer.on('open', (id) => {