refactor(admin): group rate limit and upload settings in dedicated section

move TTS and PDF parsing rate limit toggles and max upload size input into a new
"Rate limiting" section in the admin panel for improved organization. update
documentation to reflect the new grouping and clarify the relationship between
these settings.
This commit is contained in:
Richard R 2026-05-30 14:59:45 -06:00
parent 3de33e2f1e
commit 8b45a4f6cd
2 changed files with 41 additions and 21 deletions

View file

@ -77,14 +77,6 @@ Runtime-editable settings, one row per key:
| `enableAudiobookExport` | Show the audiobook export entry points on PDF/EPUB pages. |
| `enableDocxConversion` | Accept .docx uploads (converted to PDF server-side). |
| `enableDestructiveDeleteActions` | Show "Delete all data" buttons in the Documents tab (auth-disabled mode). |
| `disableTtsRateLimit` | Disable the per-user/IP daily TTS character limits. When `false`, the daily-limit fields below it apply. |
| `disableComputeRateLimit` | Disable per-user PDF parsing rate limiting. When `false`, the burst/sustained limit fields below it apply. |
| `maxUploadMb` | Maximum size (MB) accepted for a single document upload. Enforced server-side and signed into the presigned S3 PUT. |
The **Disable TTS daily rate limiting** and **Disable PDF parsing rate limiting** toggles each reveal a collapsible group of numeric inputs when set to `false`:
- TTS: anonymous/authenticated per-user daily limits and anonymous/authenticated IP daily backstops.
- PDF parsing: burst limit + window (seconds) and sustained limit + window (seconds). The sustained window doubles as a concurrency cap.
Word-by-word highlighting and PDF layout parsing capability are controlled by compute-worker server env configuration, not an admin runtime flag.
@ -98,6 +90,21 @@ Each row shows a source badge:
Turning `restrictUserApiKeys` off allows user-supplied API keys to flow through this server. Use this only for trusted/self-hosted deployments where that tradeoff is acceptable.
:::
## Rate limiting
A dedicated **Rate limiting** group (within the same admin panel) collects the daily quotas, the PDF parsing throttle, and the upload size cap:
| Key | What it controls |
| --- | --- |
| `disableTtsRateLimit` | Disable the per-user/IP daily TTS character limits. When `false`, the daily-limit fields below it apply. |
| `disableComputeRateLimit` | Disable per-user PDF parsing rate limiting. When `false`, the burst/sustained limit fields below it apply. |
| `maxUploadMb` | Maximum size (MB) accepted for a single document upload. Enforced server-side and signed into the presigned S3 PUT. |
The **Disable TTS daily rate limiting** and **Disable PDF parsing rate limiting** toggles each reveal a collapsible group of numeric inputs when set to `false`:
- TTS: anonymous/authenticated per-user daily limits and anonymous/authenticated IP daily backstops.
- PDF parsing: burst limit + window (seconds) and sustained limit + window (seconds). The sustained window doubles as a concurrency cap.
## Migrating off env vars
The future-direction goal is to remove `RUNTIME_SEED_*` / `API_KEY` / `API_BASE` from your `.env` entirely. To do that safely:

View file

@ -280,6 +280,13 @@ export function AdminFeaturesPanel() {
right={renderSource('showAllProviderModels')}
variant="flat"
/>
</Section>
<Section
title="Rate limiting"
subtitle="Daily TTS quotas, PDF parsing throttle, and upload size."
action={<Badge tone="foreground">Limits</Badge>}
>
<ToggleRow
label="Disable TTS daily rate limiting"
description="When on, per-user/IP daily character quotas are not enforced."
@ -418,20 +425,26 @@ export function AdminFeaturesPanel() {
</div>
) : null}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2.5 px-0.5 py-1.5">
<div className="space-y-1">
<div className="flex items-center justify-between gap-2">
<label className="text-xs font-medium text-foreground">Max upload size (MB)</label>
{renderSource('maxUploadMb')}
<div className="px-0.5 pt-1 pb-2 border-b border-offbase last:border-b-0">
<div className="flex items-center gap-2.5">
<div className="flex-1 min-w-0 space-y-0.5">
<span className="block text-sm font-medium leading-5 text-foreground">Max upload size</span>
<span className="block text-xs leading-4 text-muted">Largest single document upload accepted.</span>
</div>
<div className="shrink-0 self-start pl-1.5">{renderSource('maxUploadMb')}</div>
<div className="shrink-0 flex items-center gap-1.5">
<input
type="number"
min={1}
step={1}
inputMode="numeric"
aria-label="Max upload size in megabytes"
className="w-20 rounded-md bg-background border border-offbase px-2.5 py-1.5 text-sm text-foreground text-right focus:outline-none focus:ring-2 focus:ring-accent"
value={String(draft.maxUploadMb ?? '')}
onChange={(event) => updatePositiveIntDraft('maxUploadMb', event.target.value)}
/>
<span className="text-xs text-muted">MB</span>
</div>
<input
type="number"
min={1}
step={1}
className={inputClass}
value={String(draft.maxUploadMb ?? '')}
onChange={(event) => updatePositiveIntDraft('maxUploadMb', event.target.value)}
/>
</div>
</div>
</Section>