Harden sync and security; add IQ Bible, templates, password reset, backups
Build and Push Docker Image / build-and-push (push) Successful in 7m14s
Build and Push Docker Image / build-and-push (push) Successful in 7m14s
Sync and data safety - Autosave flushes on leaving a study or the tab; storage-full errors are reported - Unsynced studies, deletes and ink are queued and retried when back online - Deletes propagate across devices (with a "Keep it" undo) and no longer resurrect - Server assigns study versions (no client-clock conflicts); "Combine both" merges a conflicted study chunk by chunk - Reading-plan progress made offline is merged, not overwritten; plans, ink and marks are cleared from the device on sign-out Security - Block SSRF through the ntfy reminder URL; failure-only rate limiting per IP and account; constant-time login; TOTP replay protection; input validation; SESSION_SECRET required in production; admin user delete fixed - Forgot password: one-time code sent to the account's ntfy topic Features - IQ Bible proxy (route allow-list, text-only caching per its terms): more commentaries, lexicons, study aids, daily verse and devotional, plan calendar export, narrations, podcast background - Study templates, reading-plan catch-up, account export, daily database snapshots Tests and tooling - ESLint config; App tests split into src/test; tests for sync queues, library hook, merge, templates, IQ Bible UI and reset form Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6bb392933a
commit
f274bc9c8a
67 files changed
+4734
-1569
No files matched your search
@@ -0,0 +1,16 @@
|
||||
// <option>s for IQ Bible's narrated Bibles, to add inside an audio narrator <select>. Playing a
|
||||
// chapter this way uses one of the account's monthly audio streams, so the group says so.
|
||||
import { useIq, useIqAvailable } from '../../hooks/useIq.js';
|
||||
import { listOf } from '../../utils/iqbible.js';
|
||||
|
||||
export default function IqNarratorOptions() {
|
||||
const on = useIqAvailable();
|
||||
const { data } = useIq(on ? 'audio' : null);
|
||||
const sets = listOf(data).filter((s) => s.active !== false);
|
||||
if (!on || sets.length === 0) return null;
|
||||
return (
|
||||
<optgroup label="IQ Bible narrations (uses monthly streams)">
|
||||
{sets.map((s) => <option key={s.audio_id} value={`iq:${s.audio_id}`}>{s.audio_id}{s.narrator ? ` · ${s.narrator}` : ''}</option>)}
|
||||
</optgroup>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user