From 96376e2324075f76a41ca924f8a428f9ab2e13f3 Mon Sep 17 00:00:00 2001 From: Richard Roberson Date: Sun, 23 Feb 2025 13:20:42 -0700 Subject: [PATCH] Environment changes, set in Docker run --- .env.template | 9 ++-- README.md | 16 ++++++- src/app/api/tts/route.ts | 12 ++--- src/app/api/tts/voices/route.ts | 12 ++--- src/components/SettingsModal.tsx | 67 ++++++++++++++++++++------- src/contexts/ConfigContext.tsx | 63 +++++++++++++------------ src/contexts/TTSContext.tsx | 2 +- src/hooks/audio/useVoiceManagement.ts | 7 ++- src/utils/indexedDB.ts | 4 ++ 9 files changed, 119 insertions(+), 73 deletions(-) diff --git a/.env.template b/.env.template index 9a3d88f..5a24657 100644 --- a/.env.template +++ b/.env.template @@ -1,9 +1,8 @@ +NEXT_PUBLIC_NODE_ENV=development + # OpenAI API Key for Text-to-Speech functionality -NEXT_PUBLIC_OPENAI_API_KEY=your_openai_api_key_here +API_KEY=api_key_here_if_needed # OpenAI API Base URL (default) # To use a local TTS model server, I suggest using https://github.com/remsky/Kokoro-FastAPI -NEXT_PUBLIC_OPENAI_API_BASE=https://api.openai.com/v1 - -# Add other environment variables below as needed -NEXT_PUBLIC_NODE_ENV=development \ No newline at end of file +API_BASE=https://api.openai.com/v1 \ No newline at end of file diff --git a/README.md b/README.md index 3089f72..64e2885 100644 --- a/README.md +++ b/README.md @@ -42,11 +42,21 @@ docker run --name openreader-webui \ -v openreader_docstore:/app/docstore \ richardr1126/openreader-webui:latest ``` -> **Note:** The `openreader_docstore` volume is used to store server-side documents. You can mount a local directory instead. Or remove it if you don't need server-side documents. + +(Optionally): Set the TTS `API_BASE` URL and/or `API_KEY` to be default for all devices +```bash +docker run --name openreader-webui \ + -e API_BASE=http://host.docker.internal:8880/v1 \ + -p 3003:3003 \ + -v openreader_docstore:/app/docstore \ + richardr1126/openreader-webui:latest +``` + +> Requesting audio from the TTS API happens on the Next.js server not the client. So the base URL for the TTS API should be accessible and relative to the Next.js server. If it is in a Docker you may need to use `host.docker.internal` to access the host machine, instead of `localhost`. Visit [http://localhost:3003](http://localhost:3003) to run the app and set your settings. -> Requesting audio from the TTS API happens on the Next.js server not the client. So the base URL for the TTS API should be accessible and relative to the Next.js server. +> **Note:** The `openreader_docstore` volume is used to store server-side documents. You can mount a local directory instead. Or remove it if you don't need server-side documents. ### ⬆️ Update Docker Image ```bash @@ -64,6 +74,8 @@ services: openreader-webui: container_name: openreader-webui image: richardr1126/openreader-webui:latest + environment: + - API_BASE=http://host.docker.internal:8880/v1 ports: - "3003:3003" volumes: diff --git a/src/app/api/tts/route.ts b/src/app/api/tts/route.ts index f629339..9666b98 100644 --- a/src/app/api/tts/route.ts +++ b/src/app/api/tts/route.ts @@ -3,14 +3,14 @@ import OpenAI from 'openai'; export async function POST(req: NextRequest) { try { - // Get API credentials from headers - const openApiKey = req.headers.get('x-openai-key'); - const openApiBaseUrl = req.headers.get('x-openai-base-url'); + // Get API credentials from headers or fall back to environment variables + const openApiKey = req.headers.get('x-openai-key') || process.env.API_KEY || 'none'; + const openApiBaseUrl = req.headers.get('x-openai-base-url') || process.env.API_BASE; const { text, voice, speed } = await req.json(); console.log('Received TTS request:', text, voice, speed); - if (!openApiKey || !openApiBaseUrl) { - return NextResponse.json({ error: 'Missing API credentials' }, { status: 401 }); + if (!openApiKey) { + return NextResponse.json({ error: 'Missing OpenAI API key' }, { status: 401 }); } if (!text || !voice || !speed) { @@ -20,7 +20,7 @@ export async function POST(req: NextRequest) { // Initialize OpenAI client with abort signal const openai = new OpenAI({ apiKey: openApiKey, - baseURL: openApiBaseUrl, + baseURL: openApiBaseUrl || 'https://api.openai.com/v1', }); // Request audio from OpenAI and pass along the abort signal diff --git a/src/app/api/tts/voices/route.ts b/src/app/api/tts/voices/route.ts index 461a98b..a67b3cd 100644 --- a/src/app/api/tts/voices/route.ts +++ b/src/app/api/tts/voices/route.ts @@ -4,16 +4,12 @@ const DEFAULT_VOICES = ['alloy', 'ash', 'coral', 'echo', 'fable', 'onyx', 'nova' export async function GET(req: NextRequest) { try { - // Get API credentials from headers - const openApiKey = req.headers.get('x-openai-key'); - const openApiBaseUrl = req.headers.get('x-openai-base-url'); - - if (!openApiKey || !openApiBaseUrl) { - return NextResponse.json({ error: 'Missing API credentials' }, { status: 401 }); - } + // Get API credentials from headers or fall back to environment variables + const openApiKey = req.headers.get('x-openai-key') || process.env.API_KEY || 'none'; + const openApiBaseUrl = req.headers.get('x-openai-base-url') || process.env.API_BASE; // Request voices from OpenAI - const response = await fetch(`${openApiBaseUrl}/audio/voices`, { + const response = await fetch(`${openApiBaseUrl || 'https://api.openai.com/v1'}/audio/voices`, { headers: { 'Authorization': `Bearer ${openApiKey}`, 'Content-Type': 'application/json', diff --git a/src/components/SettingsModal.tsx b/src/components/SettingsModal.tsx index b311424..5066d11 100644 --- a/src/components/SettingsModal.tsx +++ b/src/components/SettingsModal.tsx @@ -91,6 +91,14 @@ export function SettingsModal() { setShowClearServerConfirm(false); }; + const handleInputChange = (type: 'apiKey' | 'baseUrl', value: string) => { + if (type === 'apiKey') { + setLocalApiKey(value === '' ? '' : value); + } else { + setLocalBaseUrl(value === '' ? '' : value); + } + }; + return (