diff --git a/.env.example b/.env.example index 216d86e..f1b041c 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,3 @@ -# (Optional) Client feature flags (does not work in Docker containers due to being build-time only) -NEXT_PUBLIC_NODE_ENV=development -NEXT_PUBLIC_ENABLE_AUDIOBOOK_EXPORT= -NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT= # Local / OpenAI TTS API Configuration (default) # Suggest using https://github.com/remsky/Kokoro-FastAPI @@ -74,3 +70,12 @@ WHISPER_CPP_BIN=/whisper.cpp/build/bin/whisper-cli # (Optional) Override ffmpeg binary path used for audiobook processing FFMPEG_BIN= + +# (Optional) Client feature flags (does not work in Docker containers due to being build-time only) +# NEXT_PUBLIC_ENABLE_DOCX_CONVERSION=true +# NEXT_PUBLIC_ENABLE_DESTRUCTIVE_DELETE_ACTIONS=true +# NEXT_PUBLIC_DEFAULT_TTS_PROVIDER=custom-openai +# NEXT_PUBLIC_DEFAULT_TTS_MODEL=kokoro +# NEXT_PUBLIC_SHOW_ALL_DEEPINFRA_MODELS=true +# NEXT_PUBLIC_ENABLE_AUDIOBOOK_EXPORT=true +# NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT=false diff --git a/docs-site/docs/deploy/vercel-deployment.md b/docs-site/docs/deploy/vercel-deployment.md index 00a61bd..89a91a9 100644 --- a/docs-site/docs/deploy/vercel-deployment.md +++ b/docs-site/docs/deploy/vercel-deployment.md @@ -18,6 +18,8 @@ This guide covers deploying OpenReader WebUI to Vercel with external Postgres an Recommended production setup (auth enabled): ```bash +API_BASE=https://api.deepinfra.com/v1/openai +API_KEY=your_deepinfra_key POSTGRES_URL=postgres://... USE_EMBEDDED_WEED_MINI=false S3_ACCESS_KEY_ID=... @@ -27,34 +29,29 @@ S3_REGION=us-east-1 S3_PREFIX=openreader BASE_URL=https://your-app.vercel.app AUTH_SECRET=... -NEXT_PUBLIC_NODE_ENV=production -# Optional client/runtime feature overrides: -# NEXT_PUBLIC_ENABLE_AUDIOBOOK_EXPORT=false -# NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT=true +# Optional client/runtime feature defaults: +NEXT_PUBLIC_ENABLE_DOCX_CONVERSION=false +NEXT_PUBLIC_ENABLE_DESTRUCTIVE_DELETE_ACTIONS=false +NEXT_PUBLIC_DEFAULT_TTS_PROVIDER=deepinfra +NEXT_PUBLIC_DEFAULT_TTS_MODEL=hexgrad/Kokoro-82M +NEXT_PUBLIC_SHOW_ALL_DEEPINFRA_MODELS=false +NEXT_PUBLIC_ENABLE_AUDIOBOOK_EXPORT=true +NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT=false # Optional (non-AWS S3-compatible providers): # S3_ENDPOINT=https://... # S3_FORCE_PATH_STYLE=true ``` -:::info `NEXT_PUBLIC_*` feature flags -- `NEXT_PUBLIC_ENABLE_AUDIOBOOK_EXPORT=false`: hides audiobook export UI entry points. -- `NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT=true`: enables word-highlight UI and timestamp alignment requests. -::: +:::info Production Configuration & Feature Flags +We recommend setting these defaults for a production-like environment: -:::warning `NEXT_PUBLIC_NODE_ENV` behavior -Use `NEXT_PUBLIC_NODE_ENV=production` on Vercel unless you explicitly want dev-oriented client behavior. - -With `production`: -- Footer is shown in the app shell -- DOCX upload/conversion option is hidden -- Default provider/model behavior is production-oriented -- DeepInfra model picker is restricted without an API key -- Privacy modal shows hosted-service/operator wording -- Dev-only destructive document actions are hidden - -With unset/non-`production`, the inverse dev behavior applies. - -Full details: [Environment Variables](../reference/environment-variables#next_public_node_env). +- `NEXT_PUBLIC_ENABLE_DOCX_CONVERSION=false`: Disables DOCX upload (requires external tools anyway) +- `NEXT_PUBLIC_ENABLE_DESTRUCTIVE_DELETE_ACTIONS=false`: Hides destructive "Delete All" actions +- `NEXT_PUBLIC_DEFAULT_TTS_PROVIDER=deepinfra`: Points default TTS to a scalable provider +- `NEXT_PUBLIC_DEFAULT_TTS_MODEL=hexgrad/Kokoro-82M`: Uses a high-quality default model +- `NEXT_PUBLIC_SHOW_ALL_DEEPINFRA_MODELS=false`: Restricts usage to free models if no key is provided +- `NEXT_PUBLIC_ENABLE_AUDIOBOOK_EXPORT=true`: (Optional) Controls audiobook export UI +- `NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT=false`: (Optional) Controls word highlighting UI (requires timestamp backend) ::: :::warning Auth recommendation diff --git a/docs-site/docs/reference/environment-variables.md b/docs-site/docs/reference/environment-variables.md index 1c85aa3..50866d2 100644 --- a/docs-site/docs/reference/environment-variables.md +++ b/docs-site/docs/reference/environment-variables.md @@ -9,7 +9,11 @@ This is the single reference page for OpenReader WebUI environment variables. | Variable | Area | Default | When to set | | --- | --- | --- | --- | -| `NEXT_PUBLIC_NODE_ENV` | Runtime mode | treated as `development` unless `production` | Set `production` for production client behavior | +| `NEXT_PUBLIC_ENABLE_DOCX_CONVERSION` | Client feature flags | `true` unless set to `false` | Set `false` to hide DOCX support | +| `NEXT_PUBLIC_ENABLE_DESTRUCTIVE_DELETE_ACTIONS` | Client feature flags | `true` unless set to `false` | Set `false` to hide destructive actions | +| `NEXT_PUBLIC_DEFAULT_TTS_PROVIDER` | Client feature flags | `custom-openai` | Override default TTS provider | +| `NEXT_PUBLIC_DEFAULT_TTS_MODEL` | Client feature flags | `kokoro` | Override default TTS model | +| `NEXT_PUBLIC_SHOW_ALL_DEEPINFRA_MODELS` | Client feature flags | `true` unless set to `false` | Set `false` to restrict DeepInfra models | | `NEXT_PUBLIC_ENABLE_AUDIOBOOK_EXPORT` | Client feature flags | `true` unless set to `false` | Set `false` to hide audiobook export UI | | `NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT` | Client feature flags | `false` unless set to `true` | Set `true` to enable word highlight + alignment | | `API_BASE` | TTS provider | none | Point to your OpenAI-compatible TTS base URL | @@ -50,41 +54,7 @@ This is the single reference page for OpenReader WebUI environment variables. | `WHISPER_CPP_BIN` | Word timing | unset | Set to enable `whisper.cpp` timestamps | | `FFMPEG_BIN` | Audio runtime | auto-detected (`ffmpeg-static`) | Override ffmpeg binary path | -## Client Runtime and Feature Flags -### NEXT_PUBLIC_NODE_ENV - -Controls development vs production behavior in client/server code paths. - -- Typical values: `development`, `production` -- OpenReader `isDev` checks rely on this variable directly -- If this is not `production`, OpenReader treats the client as development mode -- In deployed environments, set `NEXT_PUBLIC_NODE_ENV=production` explicitly for predictable production behavior -- Affects: - - Footer visibility in the app shell - - DOCX upload/conversion availability in upload UI - - Default provider/model behavior for first-run TTS config - - DeepInfra model picker restrictions when no API key is set - - Dev-only destructive document actions in settings - -### NEXT_PUBLIC_ENABLE_AUDIOBOOK_EXPORT - -Controls whether audiobook export UI/actions are shown in the client. - -- Default behavior: enabled unless explicitly set to `false` -- Applies in both development and production -- Affects export entry points in PDF/EPUB pages and document settings UI - -### NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT - -Controls word-by-word highlighting UI and timestamp-alignment behavior. - -- Default behavior: disabled unless set to `true` -- Applies in both development and production -- Requires working timestamp generation (for example `WHISPER_CPP_BIN`) -- Affects: - - Word-highlight toggles in document settings - - Alignment requests during TTS playback ## TTS Provider and Request Behavior @@ -367,3 +337,59 @@ Absolute path or executable name for the ffmpeg binary used by audiobook/process - Resolution order: `FFMPEG_BIN` -> `ffmpeg-static` - Example: `/var/task/node_modules/ffmpeg-static/ffmpeg` + +## Client Runtime and Feature Flags + +### NEXT_PUBLIC_ENABLE_DOCX_CONVERSION + + Controls whether the experimental DOCX-to-PDF conversion and upload feature is enabled. + + - Default: `true` (enabled) + - Set `false` to hide DOCX support in the upload UI + + ### NEXT_PUBLIC_ENABLE_DESTRUCTIVE_DELETE_ACTIONS + + Controls whether the "Delete all user docs" and other bulk-delete buttons are shown in Settings. + + - Default: `true` (enabled) + - Set `false` to hide destructive actions (recommended for production) + + ### NEXT_PUBLIC_DEFAULT_TTS_PROVIDER + + Sets the default TTS provider for new users. + + - Default: `custom-openai` + - Example values: `deepinfra`, `openai`, `custom-openai` + + ### NEXT_PUBLIC_DEFAULT_TTS_MODEL + + Sets the default TTS model for new users. + + - Default: `kokoro` + - Example values: `hexgrad/Kokoro-82M`, `tts-1` + + ### NEXT_PUBLIC_SHOW_ALL_DEEPINFRA_MODELS + + Controls whether the DeepInfra model list shows all models or just the free tier when no API key is set. + + - Default: `true` (show all) + - Set `false` to restrict to free tier models when no API key is provided + +### NEXT_PUBLIC_ENABLE_AUDIOBOOK_EXPORT + +Controls whether audiobook export UI/actions are shown in the client. + +- Default behavior: enabled unless explicitly set to `false` +- Applies in both development and production +- Affects export entry points in PDF/EPUB pages and document settings UI + +### NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT + +Controls word-by-word highlighting UI and timestamp-alignment behavior. + +- Default behavior: disabled unless set to `true` +- Applies in both development and production +- Requires working timestamp generation (for example `WHISPER_CPP_BIN`) +- Affects: + - Word-highlight toggles in document settings + - Alignment requests during TTS playback diff --git a/src/components/DocumentUploader.tsx b/src/components/DocumentUploader.tsx index df7cce1..0a714d2 100644 --- a/src/components/DocumentUploader.tsx +++ b/src/components/DocumentUploader.tsx @@ -6,7 +6,8 @@ import { UploadIcon } from '@/components/icons/Icons'; import { useDocuments } from '@/contexts/DocumentContext'; import { uploadDocxAsPdf } from '@/lib/client-documents'; -const isDev = process.env.NEXT_PUBLIC_NODE_ENV !== 'production' || process.env.NODE_ENV == null; +const enableDocx = process.env.NEXT_PUBLIC_ENABLE_DOCX_CONVERSION !== 'false'; + interface DocumentUploaderProps { className?: string; @@ -14,8 +15,8 @@ interface DocumentUploaderProps { } export function DocumentUploader({ className = '', variant = 'default' }: DocumentUploaderProps) { - const { - addPDFDocument: addPDF, + const { + addPDFDocument: addPDF, addEPUBDocument: addEPUB, addHTMLDocument: addHTML, refreshDocuments, @@ -38,7 +39,7 @@ export function DocumentUploader({ className = '', variant = 'default' }: Docume await addEPUB(file); } else if (file.type === 'text/plain' || file.type === 'text/markdown' || file.name.endsWith('.md')) { await addHTML(file); - } else if (isDev && file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') { + } else if (enableDocx && file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') { // Preserve prior UX: show "Converting DOCX..." state rather than generic uploading. setIsUploading(false); setIsConverting(true); @@ -65,7 +66,7 @@ export function DocumentUploader({ className = '', variant = 'default' }: Docume 'application/epub+zip': ['.epub'], 'text/plain': ['.txt'], 'text/markdown': ['.md'], - ...(isDev ? { + ...(enableDocx ? { 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['.docx'] } : {}) }, @@ -111,7 +112,7 @@ export function DocumentUploader({ className = '', variant = 'default' }: Docume {isDragActive ? 'Drop your file(s) here' : 'Drop your file(s) here, or click to select'}
- {isDev ? 'PDF, EPUB, TXT, MD, or DOCX files are accepted' : 'PDF, EPUB, TXT, or MD files are accepted'} + {enableDocx ? 'PDF, EPUB, TXT, MD, or DOCX files are accepted' : 'PDF, EPUB, TXT, or MD files are accepted'}
{error &&{error}
} > diff --git a/src/components/SettingsModal.tsx b/src/components/SettingsModal.tsx index 05b28d2..d043fd6 100644 --- a/src/components/SettingsModal.tsx +++ b/src/components/SettingsModal.tsx @@ -40,7 +40,9 @@ import { deleteDocuments, mimeTypeForDoc, uploadDocuments } from '@/lib/client-d import { cacheStoredDocumentFromBytes, clearDocumentCache } from '@/lib/document-cache'; import { clearAllDocumentPreviewCaches, clearInMemoryDocumentPreviewCache } from '@/lib/document-preview-cache'; -const isDev = process.env.NEXT_PUBLIC_NODE_ENV !== 'production' || process.env.NODE_ENV == null; +const enableDestructiveDelete = process.env.NEXT_PUBLIC_ENABLE_DESTRUCTIVE_DELETE_ACTIONS !== 'false'; +const showAllDeepInfra = process.env.NEXT_PUBLIC_SHOW_ALL_DEEPINFRA_MODELS !== 'false'; + const themes = THEMES.map(id => ({ id, @@ -108,7 +110,7 @@ export function SettingsModal({ className = '' }: { className?: string }) { ]; case 'deepinfra': // In production without an API key, limit to free tier model - if (!isDev && !localApiKey) { + if (!showAllDeepInfra && !localApiKey) { return [ { id: 'hexgrad/Kokoro-82M', name: 'hexgrad/Kokoro-82M' } ]; @@ -561,7 +563,7 @@ export function SettingsModal({ className = '' }: { className?: string }) { type="password" value={localApiKey} onChange={(e) => handleInputChange('apiKey', e.target.value)} - placeholder={!isDev && localTTSProvider === 'deepinfra' ? "Deepinfra free or use your API key" : "Using environment variable"} + placeholder={!showAllDeepInfra && localTTSProvider === 'deepinfra' ? "Deepinfra free or use your API key" : "Using environment variable"} className="w-full rounded-lg bg-background py-1.5 px-3 text-foreground shadow-sm focus:outline-none focus:ring-2 focus:ring-accent" /> @@ -782,7 +784,7 @@ export function SettingsModal({ className = '' }: { className?: string }) { > Clear cache - {isDev && ( + {enableDestructiveDelete && (