From 5f5c5faa252d66c4c8d607c90677b58932bc7384 Mon Sep 17 00:00:00 2001 From: GoldenFishX Date: Thu, 28 May 2026 21:46:31 +0800 Subject: [PATCH] fix(tts): require authentication for TTS endpoints (#1101) Move TTS routes behind auth middleware and attach JWT to local proxy requests from the frontend. Previously both /api/hermes/tts and /api/tts/proxy/audio/speech were publicly accessible without authentication, allowing unauthenticated callers to consume Edge TTS resources through the server. Changes: - server: move ttsRoutes from public to protected route section - client: auto-attach JWT when baseUrl is a local path (/...) and no external API key is configured - client: import getApiKey() instead of raw localStorage access --- packages/client/src/composables/useSpeech.ts | 5 +++++ packages/server/src/routes/index.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/client/src/composables/useSpeech.ts b/packages/client/src/composables/useSpeech.ts index 7f20826..a335d0c 100644 --- a/packages/client/src/composables/useSpeech.ts +++ b/packages/client/src/composables/useSpeech.ts @@ -1,5 +1,6 @@ import { ref, computed, onUnmounted } from 'vue' import { generateSpeech, playAudioBlob } from '@/api/hermes/tts' +import { getApiKey } from '@/api/client' export interface SpeechOptions { lang?: string // 语言 'zh-CN', 'en-US' 等 @@ -286,6 +287,10 @@ export function useSpeech() { } if (opts.apiKey) { headers['Authorization'] = `Bearer ${opts.apiKey}` + } else if (opts.baseUrl.startsWith('/')) { + // 本地代理请求自动附加 JWT + const jwt = getApiKey() + if (jwt) headers['Authorization'] = `Bearer ${jwt}` } try { diff --git a/packages/server/src/routes/index.ts b/packages/server/src/routes/index.ts index 84cf174..8e0e563 100644 --- a/packages/server/src/routes/index.ts +++ b/packages/server/src/routes/index.ts @@ -43,13 +43,13 @@ export function registerRoutes(app: any, authMiddleware: Array<(ctx: Context, ne app.use(healthRoutes.routes()) app.use(webhookRoutes.routes()) app.use(authPublicRoutes.routes()) - app.use(ttsRoutes.routes()) // TTS proxy/generation — must be before auth // --- Auth middleware: all routes below require authentication --- authMiddleware.forEach((middleware) => app.use(middleware)) // --- Protected routes (auth required) --- app.use(authProtectedRoutes.routes()) + app.use(ttsRoutes.routes()) app.use(uploadRoutes.routes()) app.use(updateRoutes.routes()) // Must be before proxy (proxy catch-all matches everything) app.use(sessionRoutes.routes())