pull/164/head
Alex Kern 12 months ago
parent 9e7d78c755
commit de881ae4fb
No known key found for this signature in database
GPG Key ID: EF051FACCACBEE25

@ -5,33 +5,28 @@ import { setTurnCredentials } from '../../../coturn'
const turnHost = process.env.TURN_HOST || '127.0.0.1'
export async function POST(): Promise<NextResponse> {
if (!process.env.COTURN_ENABLED) {
return NextResponse.json({
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' }
]
})
}
if (!process.env.COTURN_ENABLED) {
return NextResponse.json({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }],
})
}
// Generate ephemeral credentials
const username = crypto.randomBytes(8).toString('hex')
const password = crypto.randomBytes(8).toString('hex')
const ttl = 86400 // 24 hours
// Generate ephemeral credentials
const username = crypto.randomBytes(8).toString('hex')
const password = crypto.randomBytes(8).toString('hex')
const ttl = 86400 // 24 hours
// Store credentials in Redis
await setTurnCredentials(username, password, ttl)
// Store credentials in Redis
await setTurnCredentials(username, password, ttl)
return NextResponse.json({
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' },
{
urls: [
`turn:${turnHost}:3478`,
`turns:${turnHost}:5349`
],
username,
credential: password
}
]
})
return NextResponse.json({
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' },
{
urls: [`turn:${turnHost}:3478`, `turns:${turnHost}:5349`],
username,
credential: password,
},
],
})
}

@ -32,7 +32,7 @@ let globalPeer: Peer | null = null
async function getOrCreateGlobalPeer(): Promise<Peer> {
if (!globalPeer) {
const response = await fetch('/api/ice', {
method: 'POST'
method: 'POST',
})
const { iceServers } = await response.json()
console.log('[WebRTCProvider] ICE servers:', iceServers)
@ -40,8 +40,8 @@ async function getOrCreateGlobalPeer(): Promise<Peer> {
globalPeer = new Peer({
debug: 3,
config: {
iceServers
}
iceServers,
},
})
}

@ -1,30 +1,34 @@
import crypto from 'crypto'
import { getRedisClient } from './redisClient'
function generateHMACKey(username: string, realm: string, password: string): string {
const str = `${username}:${realm}:${password}`
return crypto.createHash('md5').update(str).digest('hex')
function generateHMACKey(
username: string,
realm: string,
password: string,
): string {
const str = `${username}:${realm}:${password}`
return crypto.createHash('md5').update(str).digest('hex')
}
export async function setTurnCredentials(
username: string,
password: string,
ttl: number
username: string,
password: string,
ttl: number,
): Promise<void> {
if (!process.env.COTURN_ENABLED) {
return
}
if (!process.env.COTURN_ENABLED) {
return
}
const realm = process.env.TURN_REALM || 'file.pizza'
const realm = process.env.TURN_REALM || 'file.pizza'
if (!realm) {
throw new Error('TURN_REALM environment variable not set')
}
if (!realm) {
throw new Error('TURN_REALM environment variable not set')
}
const redis = getRedisClient()
const redis = getRedisClient()
const hmacKey = generateHMACKey(username, realm, password)
const key = `turn/realm/${realm}/user/${username}/key`
const hmacKey = generateHMACKey(username, realm, password)
const key = `turn/realm/${realm}/user/${username}/key`
await redis.setex(key, ttl, hmacKey)
await redis.setex(key, ttl, hmacKey)
}

@ -188,7 +188,10 @@ export function useDownloader(uploaderPeerID: string): {
let nextFileIndex = 0
const startNextFileOrFinish = () => {
if (nextFileIndex >= filesInfo.length) return
console.log('[Downloader] starting next file:', filesInfo[nextFileIndex].fileName)
console.log(
'[Downloader] starting next file:',
filesInfo[nextFileIndex].fileName,
)
dataConnection.send({
type: MessageType.Start,
fileName: filesInfo[nextFileIndex].fileName,

@ -24,20 +24,26 @@ export function useUploaderChannel(
const { isLoading, error, data } = useQuery({
queryKey: ['uploaderChannel', uploaderPeerID],
queryFn: async () => {
console.log('[UploaderChannel] creating new channel for peer', uploaderPeerID)
console.log(
'[UploaderChannel] creating new channel for peer',
uploaderPeerID,
)
const response = await fetch('/api/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ uploaderPeerID }),
})
if (!response.ok) {
console.error('[UploaderChannel] failed to create channel:', response.status)
console.error(
'[UploaderChannel] failed to create channel:',
response.status,
)
throw new Error('Network response was not ok')
}
const data = await response.json()
console.log('[UploaderChannel] channel created successfully:', {
longSlug: data.longSlug,
shortSlug: data.shortSlug
shortSlug: data.shortSlug,
})
return data
},
@ -62,7 +68,10 @@ export function useUploaderChannel(
body: JSON.stringify({ slug: shortSlug, secret: s }),
})
if (!response.ok) {
console.error('[UploaderChannel] failed to renew channel', response.status)
console.error(
'[UploaderChannel] failed to renew channel',
response.status,
)
throw new Error('Network response was not ok')
}
const data = await response.json()
@ -78,7 +87,11 @@ export function useUploaderChannel(
const run = (): void => {
timeout = setTimeout(() => {
console.log('[UploaderChannel] scheduling channel renewal in', renewInterval, 'ms')
console.log(
'[UploaderChannel] scheduling channel renewal in',
renewInterval,
'ms',
)
renewMutation.mutate({ secret })
run()
}, renewInterval)

@ -34,14 +34,20 @@ export function useUploaderConnections(
const [connections, setConnections] = useState<Array<UploaderConnection>>([])
useEffect(() => {
console.log('[UploaderConnections] initializing with', files.length, 'files')
console.log(
'[UploaderConnections] initializing with',
files.length,
'files',
)
const cleanupHandlers: Array<() => void> = []
const listener = (conn: DataConnection) => {
console.log('[UploaderConnections] new connection from peer', conn.peer)
// If the connection is a report, we need to hard-redirect the uploader to the reported page to prevent them from uploading more files.
if (conn.metadata?.type === 'report') {
console.log('[UploaderConnections] received report connection, redirecting')
console.log(
'[UploaderConnections] received report connection, redirecting',
)
// Broadcast report message to all connections
connections.forEach((c) => {
c.dataConnection.send({
@ -85,7 +91,9 @@ export function useUploaderConnections(
console.log('[UploaderConnections] client info:', {
browser: `${message.browserName} ${message.browserVersion}`,
os: `${message.osName} ${message.osVersion}`,
mobile: message.mobileVendor ? `${message.mobileVendor} ${message.mobileModel}` : 'N/A'
mobile: message.mobileVendor
? `${message.mobileVendor} ${message.mobileModel}`
: 'N/A',
})
const newConnectionState = {
browserName: message.browserName,
@ -97,7 +105,9 @@ export function useUploaderConnections(
}
if (password) {
console.log('[UploaderConnections] password required, requesting authentication')
console.log(
'[UploaderConnections] password required, requesting authentication',
)
const request: Message = {
type: MessageType.PasswordRequired,
}
@ -206,7 +216,12 @@ export function useUploaderConnections(
case MessageType.Start: {
const fileName = message.fileName
let offset = message.offset
console.log('[UploaderConnections] starting transfer of', fileName, 'from offset', offset)
console.log(
'[UploaderConnections] starting transfer of',
fileName,
'from offset',
offset,
)
const file = validateOffset(files, fileName, offset)
const sendNextChunkAsync = () => {
@ -226,7 +241,14 @@ export function useUploaderConnections(
updateConnection((draft) => {
offset = end
if (final) {
console.log('[UploaderConnections] completed file', fileName, '- file', draft.completedFiles + 1, 'of', draft.totalFiles)
console.log(
'[UploaderConnections] completed file',
fileName,
'- file',
draft.completedFiles + 1,
'of',
draft.totalFiles,
)
return {
...draft,
status: UploaderConnectionStatus.Ready,
@ -288,7 +310,9 @@ export function useUploaderConnections(
}
case MessageType.Done: {
console.log('[UploaderConnections] transfer completed successfully')
console.log(
'[UploaderConnections] transfer completed successfully',
)
updateConnection((draft) => {
if (draft.status !== UploaderConnectionStatus.Ready) {
return draft

@ -5,8 +5,8 @@ export { Redis }
let redisClient: Redis.Redis | null = null
export function getRedisClient(): Redis.Redis {
if (!redisClient) {
redisClient = new Redis(process.env.REDIS_URL)
}
return redisClient
if (!redisClient) {
redisClient = new Redis(process.env.REDIS_URL)
}
return redisClient
}
Loading…
Cancel
Save