Files
gamegroup2/backend/voice-token-service/server.js
T
congsh 3c2b68bbc3 fix(electron): enable mediaDevices on HTTP origins and fix voice auth
- Add --unsafely-treat-insecure-origin-as-secure flag for dev/uat URLs
- Set auto-granted permission handlers for mic/camera in main process
- Adapt useVoiceRoom error message for Electron (no Chrome flags hint)
- Add debug logging to voice-token service and frontend voice API

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 18:08:05 +08:00

88 lines
2.8 KiB
JavaScript

import express from 'express'
import { AccessToken } from 'livekit-server-sdk'
const app = express()
app.use(express.json())
const API_KEY = process.env.LIVEKIT_API_KEY || 'APIyxZGQjM2'
const API_SECRET = process.env.LIVEKIT_API_SECRET || 'secretNmU4ZDU3YjA0OWIxNDM4YjhlNWY3YTFjZGUzOWRi'
const PB_URL = process.env.PB_URL || 'http://gamegroup-pb:8090'
const PORT = process.env.PORT || 7882
app.post('/api/voice-token/:sessionId', async (req, res) => {
try {
const { sessionId } = req.params
const authHeader = req.headers.authorization
console.log('Voice token request:', { sessionId, authHeader: authHeader ? authHeader.slice(0, 20) + '...' : null })
if (!authHeader) {
console.log('Missing auth header')
return res.status(401).json({ error: '未登录' })
}
// 验证用户 token — 调用 PocketBase
const pbRefreshUrl = `${PB_URL}/api/collections/users/auth-refresh`
console.log('Calling PB auth-refresh:', pbRefreshUrl)
const pbRes = await fetch(pbRefreshUrl, {
method: 'POST',
headers: {
Authorization: authHeader,
'Content-Type': 'application/json',
},
body: '{}',
})
console.log('PB auth-refresh status:', pbRes.status)
if (!pbRes.ok) {
const pbBody = await pbRes.text().catch(() => 'unknown')
console.log('PB auth-refresh error body:', pbBody)
return res.status(401).json({ error: '认证失败', detail: pbBody })
}
const userData = await pbRes.json()
console.log('PB auth-refresh success, userId:', userData.record?.id)
const userId = userData.record?.id
const userName = userData.record?.name || userData.record?.username || userId
if (!userId) {
return res.status(401).json({ error: '无效用户' })
}
// 获取 session 并验证成员
const sessionRes = await fetch(`${PB_URL}/api/collections/team_sessions/records/${sessionId}`, {
headers: { Authorization: authHeader },
})
if (!sessionRes.ok) {
return res.status(404).json({ error: '未找到临时小组' })
}
const session = await sessionRes.json()
const members = session.members || []
if (!members.includes(userId)) {
return res.status(403).json({ error: '你不是该小队的成员' })
}
// 签发 LiveKit token
const at = new AccessToken(API_KEY, API_SECRET, {
identity: userId,
name: userName,
})
at.addGrant({
roomJoin: true,
room: `team-${sessionId}`,
canPublish: true,
canSubscribe: true,
})
const token = await at.toJwt()
res.json({ token })
} catch (err) {
console.error('Voice token error:', err)
res.status(500).json({ error: '服务器错误' })
}
})
app.get('/health', (_req, res) => {
res.json({ status: 'ok' })
})
app.listen(PORT, () => {
console.log(`Voice token service listening on :${PORT}`)
})