diff --git a/.env.example b/.env.example index d390755..4418f04 100644 --- a/.env.example +++ b/.env.example @@ -94,6 +94,5 @@ FFMPEG_BIN= # NEXT_PUBLIC_RESTRICT_USER_API_KEYS=true # NEXT_PUBLIC_DEFAULT_TTS_PROVIDER=custom-openai # NEXT_PUBLIC_CHANGELOG_FEED_URL=https://docs.openreader.richardr.dev/changelog/manifest.json -# NEXT_PUBLIC_DEFAULT_TTS_MODEL=kokoro # NEXT_PUBLIC_ENABLE_AUDIOBOOK_EXPORT=true # NEXT_PUBLIC_ENABLE_WORD_HIGHLIGHT=true diff --git a/docs-site/docs/configure/admin-panel.md b/docs-site/docs/configure/admin-panel.md index 7167962..533d500 100644 --- a/docs-site/docs/configure/admin-panel.md +++ b/docs-site/docs/configure/admin-panel.md @@ -58,6 +58,10 @@ On first boot, if `admin_providers` is empty and the legacy `API_KEY` env var is After this seed runs, the legacy `API_KEY` / `API_BASE` env vars are no longer read by the TTS routes — the DB row is authoritative. You can rename, edit, disable, or delete this row like any other from the admin UI, and remove the env vars from your `.env` when convenient. +:::warning Upgrading from v2.2.0 +In v2.2.0 and earlier, `API_KEY` / `API_BASE` were read live by the TTS routes on every request. As of v3.0.0 they are **one-shot seeds** consumed only on the first boot where `admin_providers` is empty. After upgrading, boot the app once and confirm a `default-openai` row exists in **Settings → Admin → Shared providers** with the correct base URL. If it is missing or wrong (e.g. the env vars were not set on first boot, or the table was already non-empty from a pre-release), create or edit the shared provider manually — TTS will not fall back to the env vars. +::: + ## Site features Runtime-editable settings, one row per key: @@ -100,11 +104,15 @@ You can keep the env vars indefinitely if you prefer; they're only read on the f ## How keys are protected -- API keys are encrypted in the `admin_providers` table with AES-256-GCM. The encryption key is derived from `AUTH_SECRET` via `scrypt`. This means rotating `AUTH_SECRET` will invalidate all stored admin keys — re-enter them via the admin UI after rotating. +- API keys are encrypted in the `admin_providers` table with AES-256-GCM. The encryption key is derived from `AUTH_SECRET` via `scrypt`. - The masked-list view (`GET /api/admin/providers`, used by the admin UI itself) returns `••••` + last-4 only — never plaintext or ciphertext. - The public list endpoint (`GET /api/tts/shared-providers`, called by every user's browser) returns only `{ slug, displayName, providerType, defaultModel }`. Keys and base URLs are never exposed to the client. - Non-admin users cannot enumerate admin providers' credentials or base URLs through any API. +:::danger Rotating `AUTH_SECRET` invalidates all stored admin provider keys +Because the encryption key for `admin_providers` is derived from `AUTH_SECRET`, changing `AUTH_SECRET` makes every stored API key undecryptable. After rotating it, shared providers will fail to authenticate upstream until you re-enter each provider's API key in **Settings → Admin → Shared providers** (edit the row and paste the key again). There is no automated re-encryption path. If you must rotate `AUTH_SECRET`, plan to re-enter admin provider keys immediately afterward. +::: + ## Related - [Auth](./auth) — required to use the admin panel. diff --git a/docs-site/docs/configure/migrations.md b/docs-site/docs/configure/migrations.md index 91c50f8..43df512 100644 --- a/docs-site/docs/configure/migrations.md +++ b/docs-site/docs/configure/migrations.md @@ -20,14 +20,21 @@ Startup migration phases: - DB schema migrations (`pnpm migrate`) - Storage/data migration (`pnpm migrate-fs`) for legacy filesystem content into S3 + DB rows -Recent schema addition: - -- `0001_tts_segments` (SQLite + Postgres) creates the `tts_segments` table used by server-side TTS segment caching and alignment metadata. - :::info In most setups, you do not need to run migration commands manually because startup handles this automatically. ::: +### Schema history + +Migrations are applied in order. All of the following ship in v3.0.0; an instance upgrading from v2.2.0 applies `0001`–`0004` in a single startup pass. + +| Migration | Dialects | What it does | +| --- | --- | --- | +| `0001_tts_segments` | SQLite + Postgres | Creates the original single-table `tts_segments` used by server-side TTS segment caching. | +| `0002_add_segment_key_to_tts_segments` | SQLite + Postgres | Adds the `segment_key` column to `tts_segments` for stable locator-independent segment identity. | +| `0003_tts_segments_v2_split` | SQLite + Postgres | Replaces `tts_segments` with a normalized two-table model: `tts_segment_entries` (one row per document segment + locator identity) and `tts_segment_variants` (one row per settings combination, holding the cached audio key, status, and alignment). Drops the original `tts_segments` table — no released build (v2.2.0 or earlier) ever populated it, so there is no production data to migrate. | +| `0004_admin_panel` | SQLite + Postgres | Creates `admin_providers` (encrypted shared TTS provider rows) and `admin_settings` (runtime site-feature config), and adds the `is_admin` column to the `user` table. Backs the [Admin Panel](./admin-panel). | + To skip automatic startup migrations: - Set `RUN_DRIZZLE_MIGRATIONS=false`