diff --git a/backend/voice-token-service/server.js b/backend/voice-token-service/server.js index 651625a..0ffaefa 100644 --- a/backend/voice-token-service/server.js +++ b/backend/voice-token-service/server.js @@ -13,19 +13,32 @@ 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 pbRes = await fetch(`${PB_URL}/api/collections/users/auth-refresh`, { + 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 }, + headers: { + Authorization: authHeader, + 'Content-Type': 'application/json', + }, + body: '{}', }) + console.log('PB auth-refresh status:', pbRes.status) if (!pbRes.ok) { - return res.status(401).json({ error: '认证失败' }) + 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) { diff --git a/electron/main.js b/electron/main.js index 6b75434..04af177 100644 --- a/electron/main.js +++ b/electron/main.js @@ -1,4 +1,4 @@ -const { app, BrowserWindow } = require('electron') +const { app, BrowserWindow, session } = require('electron') const path = require('path') const ENV_URLS = { @@ -15,6 +15,11 @@ function getWindowUrl() { return ENV_URLS.dev } +// 在 Chromium 启动前将 HTTP 内网地址标记为安全源, +// 否则 navigator.mediaDevices 在 HTTP 非 localhost 下会被置空 +const insecureOrigins = Object.values(ENV_URLS).join(',') +app.commandLine.appendSwitch('unsafely-treat-insecure-origin-as-secure', insecureOrigins) + function createWindow() { const win = new BrowserWindow({ width: 1280, @@ -27,6 +32,7 @@ function createWindow() { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, webSecurity: false, + allowRunningInsecureContent: true, }, }) @@ -40,7 +46,26 @@ function createWindow() { }) } -app.whenReady().then(createWindow) +app.whenReady().then(() => { + // 自动批准麦克风/摄像头权限请求,无需用户手动确认 + session.defaultSession.setPermissionRequestHandler((_webContents, permission, callback) => { + if (permission === 'media' || permission === 'microphone' || permission === 'camera') { + callback(true) + } else { + callback(false) + } + }) + + // 绕过权限检查,确保 mediaDevices 可用 + session.defaultSession.setPermissionCheckHandler((_webContents, permission) => { + if (permission === 'media' || permission === 'microphone' || permission === 'camera') { + return true + } + return false + }) + + createWindow() +}) app.on('window-all-closed', () => { app.quit() diff --git a/frontend/src/api/voice.ts b/frontend/src/api/voice.ts index d40cc06..c0105c4 100644 --- a/frontend/src/api/voice.ts +++ b/frontend/src/api/voice.ts @@ -12,6 +12,7 @@ export async function fetchVoiceToken(sessionId: string): Promise { if (!user) throw new Error('未登录') const token = pb.authStore.token + console.log('[voice] fetching token for session:', sessionId, 'token prefix:', token?.slice(0, 20)) const res = await fetch(`/voice-api/voice-token/${sessionId}`, { method: 'POST', @@ -21,9 +22,11 @@ export async function fetchVoiceToken(sessionId: string): Promise { }, }) + console.log('[voice] token service response status:', res.status) if (!res.ok) { const data = await res.json().catch(() => ({ error: '语音服务暂不可用' })) - throw new Error(data.error || '语音服务暂不可用') + console.log('[voice] token service error:', data) + throw new Error(data.error || data.detail || '语音服务暂不可用') } const data = await res.json() diff --git a/frontend/src/composables/useVoiceRoom.ts b/frontend/src/composables/useVoiceRoom.ts index d479544..0f42da9 100644 --- a/frontend/src/composables/useVoiceRoom.ts +++ b/frontend/src/composables/useVoiceRoom.ts @@ -34,6 +34,10 @@ export function useVoiceRoom() { error.value = null if (!navigator.mediaDevices?.getUserMedia) { + const isElectron = /Electron/.test(navigator.userAgent) + if (isElectron) { + throw new Error('麦克风权限未获取,请检查 Electron 是否已正确配置安全源和权限处理器。') + } throw new Error('浏览器不允许在 HTTP 下使用麦克风。请在 Chrome 地址栏输入 chrome://flags/#unsafely-treat-insecure-origin-as-secure,启用后将 http://192.168.1.14:7033 加入白名单,然后重启浏览器。') }