From 1c9c2301a519c8d820917a0bfedb14664314c7f7 Mon Sep 17 00:00:00 2001 From: Till-JS <101404291+Till-JS@users.noreply.github.com> Date: Sat, 14 Feb 2026 10:59:08 +0100 Subject: [PATCH] fix(matrix-tts-bot): add API key authentication for mana-tts service - Add TTS_API_KEY config option to configuration.ts - Send X-API-Key header in TtsService requests - Update docker-compose.macmini.yml with TTS_INTERNAL_API_KEY env var - Update .env.example and CLAUDE.md documentation The mana-tts service requires authentication, but the TTS bot was not sending the required X-API-Key header, causing 401 errors. Co-Authored-By: Claude Opus 4.5 --- docker-compose.macmini.yml | 1 + services/matrix-tts-bot/.env.example | 1 + services/matrix-tts-bot/CLAUDE.md | 1 + .../matrix-tts-bot/src/config/configuration.ts | 1 + services/matrix-tts-bot/src/tts/tts.service.ts | 16 ++++++++++++++-- 5 files changed, 18 insertions(+), 2 deletions(-) diff --git a/docker-compose.macmini.yml b/docker-compose.macmini.yml index 60945c714..135e5cba8 100644 --- a/docker-compose.macmini.yml +++ b/docker-compose.macmini.yml @@ -973,6 +973,7 @@ services: MATRIX_ACCESS_TOKEN: ${MATRIX_TTS_BOT_TOKEN} MATRIX_ALLOWED_ROOMS: ${MATRIX_TTS_BOT_ROOMS:-} TTS_URL: http://host.docker.internal:3022 + TTS_API_KEY: ${TTS_INTERNAL_API_KEY:-} DEFAULT_VOICE: af_heart DEFAULT_SPEED: 1.0 MAX_TEXT_LENGTH: 500 diff --git a/services/matrix-tts-bot/.env.example b/services/matrix-tts-bot/.env.example index 973351e18..1cd9b5a49 100644 --- a/services/matrix-tts-bot/.env.example +++ b/services/matrix-tts-bot/.env.example @@ -9,6 +9,7 @@ MATRIX_STORAGE_PATH=./data/bot-storage.json # TTS Service (mana-tts) TTS_URL=http://localhost:3022 +TTS_API_KEY=sk-internal-xxx # Internal API key for mana-tts service # TTS Defaults DEFAULT_VOICE=af_heart diff --git a/services/matrix-tts-bot/CLAUDE.md b/services/matrix-tts-bot/CLAUDE.md index 729338bc3..2f55c6653 100644 --- a/services/matrix-tts-bot/CLAUDE.md +++ b/services/matrix-tts-bot/CLAUDE.md @@ -77,6 +77,7 @@ MATRIX_STORAGE_PATH=./data/bot-storage.json # TTS Service TTS_URL=http://localhost:3022 +TTS_API_KEY=sk-internal-xxx # Defaults DEFAULT_VOICE=af_heart diff --git a/services/matrix-tts-bot/src/config/configuration.ts b/services/matrix-tts-bot/src/config/configuration.ts index 69f4a9ac2..c5b34c80b 100644 --- a/services/matrix-tts-bot/src/config/configuration.ts +++ b/services/matrix-tts-bot/src/config/configuration.ts @@ -8,6 +8,7 @@ export default () => ({ }, tts: { url: process.env.TTS_URL || 'http://localhost:3022', + apiKey: process.env.TTS_API_KEY || '', defaultVoice: process.env.DEFAULT_VOICE || 'af_heart', defaultSpeed: parseFloat(process.env.DEFAULT_SPEED || '1.0'), maxTextLength: parseInt(process.env.MAX_TEXT_LENGTH || '500', 10), diff --git a/services/matrix-tts-bot/src/tts/tts.service.ts b/services/matrix-tts-bot/src/tts/tts.service.ts index 2dfb40754..05f801e46 100644 --- a/services/matrix-tts-bot/src/tts/tts.service.ts +++ b/services/matrix-tts-bot/src/tts/tts.service.ts @@ -17,9 +17,11 @@ export interface VoicesResponse { export class TtsService { private readonly logger = new Logger(TtsService.name); private readonly ttsUrl: string; + private readonly apiKey: string; constructor(private configService: ConfigService) { this.ttsUrl = this.configService.get('tts.url', 'http://localhost:3022'); + this.apiKey = this.configService.get('tts.apiKey', ''); } /** @@ -32,9 +34,14 @@ export class TtsService { `Synthesizing: "${text.substring(0, 50)}..." with voice=${voice}, speed=${speed}` ); + const headers: Record = { 'Content-Type': 'application/json' }; + if (this.apiKey) { + headers['X-API-Key'] = this.apiKey; + } + const response = await fetch(url, { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers, body: JSON.stringify({ text, voice, @@ -61,7 +68,12 @@ export class TtsService { async getVoices(): Promise { const url = `${this.ttsUrl}/voices`; - const response = await fetch(url); + const headers: Record = {}; + if (this.apiKey) { + headers['X-API-Key'] = this.apiKey; + } + + const response = await fetch(url, { headers }); if (!response.ok) { throw new Error(`Failed to get voices: ${response.status}`);