|
|
|
|
@ -2,12 +2,25 @@ import { NextResponse } from 'next/server'
|
|
|
|
|
import crypto from 'crypto'
|
|
|
|
|
import { setTurnCredentials } from '../../../coturn'
|
|
|
|
|
|
|
|
|
|
const ICE_RATE_LIMIT_WINDOW_MS = 60_000
|
|
|
|
|
const ICE_RATE_LIMIT_MAX_REQUESTS = 10
|
|
|
|
|
|
|
|
|
|
type IceRateLimitEntry = {
|
|
|
|
|
count: number
|
|
|
|
|
expiresAt: number
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const iceRateLimitStore =
|
|
|
|
|
(globalThis as typeof globalThis & { __iceRateLimitStore?: Map<string, IceRateLimitEntry> }).__iceRateLimitStore ||
|
|
|
|
|
new Map<string, IceRateLimitEntry>()
|
|
|
|
|
;(globalThis as typeof globalThis & { __iceRateLimitStore?: Map<string, IceRateLimitEntry> }).__iceRateLimitStore = iceRateLimitStore
|
|
|
|
|
|
|
|
|
|
const turnHost = process.env.TURN_HOST || '127.0.0.1'
|
|
|
|
|
const stunServer = process.env.STUN_SERVER || 'stun:stun.l.google.com:19302'
|
|
|
|
|
const peerjsHost = process.env.PEERJS_HOST || '0.peerjs.com'
|
|
|
|
|
const peerjsPath = process.env.PEERJS_PATH || '/'
|
|
|
|
|
|
|
|
|
|
export async function POST(): Promise<NextResponse> {
|
|
|
|
|
export async function POST(request: Request): Promise<NextResponse> {
|
|
|
|
|
if (!process.env.COTURN_ENABLED) {
|
|
|
|
|
return NextResponse.json({
|
|
|
|
|
host: peerjsHost,
|
|
|
|
|
@ -16,12 +29,31 @@ export async function POST(): Promise<NextResponse> {
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate ephemeral credentials
|
|
|
|
|
const expectedToken = process.env.ICE_API_TOKEN
|
|
|
|
|
const providedToken = request.headers.get('authorization')?.replace(/^Bearer\s+/i, '').trim()
|
|
|
|
|
if (!expectedToken || providedToken !== expectedToken) {
|
|
|
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const forwardedFor = request.headers.get('x-forwarded-for')
|
|
|
|
|
const realIp = request.headers.get('x-real-ip')
|
|
|
|
|
const ip = forwardedFor?.split(',')[0]?.trim() || realIp || 'unknown'
|
|
|
|
|
const now = Date.now()
|
|
|
|
|
const rateLimitEntry = iceRateLimitStore.get(ip)
|
|
|
|
|
|
|
|
|
|
if (!rateLimitEntry || rateLimitEntry.expiresAt <= now) {
|
|
|
|
|
iceRateLimitStore.set(ip, { count: 1, expiresAt: now + ICE_RATE_LIMIT_WINDOW_MS })
|
|
|
|
|
} else if (rateLimitEntry.count >= ICE_RATE_LIMIT_MAX_REQUESTS) {
|
|
|
|
|
return NextResponse.json({ error: 'Too many requests' }, { status: 429 })
|
|
|
|
|
} else {
|
|
|
|
|
rateLimitEntry.count += 1
|
|
|
|
|
iceRateLimitStore.set(ip, rateLimitEntry)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const username = crypto.randomBytes(8).toString('hex')
|
|
|
|
|
const password = crypto.randomBytes(8).toString('hex')
|
|
|
|
|
const ttl = 86400 // 24 hours
|
|
|
|
|
const ttl = 600
|
|
|
|
|
|
|
|
|
|
// Store credentials in Redis
|
|
|
|
|
await setTurnCredentials(username, password, ttl)
|
|
|
|
|
|
|
|
|
|
return NextResponse.json({
|
|
|
|
|
|