Fix ebooks: show wrong-key warning, close overlay on open failure
All checks were successful
Build and push Docker image / build (push) Successful in 13s
Test / test (push) Successful in 16s

- renderBookList now shows a ⚠️ warning and disables the Open button
  for books that couldn't be decrypted (keyOk: false), telling the user
  to import the correct encryption key
- openBook catch block now hides the reader overlay and shows an alert
  instead of leaving the overlay open with a cryptic error message

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marwin Schulz 2026-03-20 09:45:39 +01:00
parent 4f413c673e
commit 1649eb27a0

View file

@ -2611,13 +2611,15 @@ async function loadBookList() {
listEl.innerHTML = '<p class="muted">Loading…</p>';
try {
const res = await fetch('/books/');
const res = await fetch('/books/', {cache: 'no-store'});
if (!res.ok) {
console.error('loadBookList: server returned', res.status);
listEl.innerHTML = `<p class="muted">Server error ${res.status} loading books.</p>`;
return;
}
const books = await res.json();
if (!Array.isArray(books)) {
console.error('loadBookList: unexpected response', books);
listEl.innerHTML = `<p class="muted">Unexpected response from server.</p>`;
return;
}
@ -2626,22 +2628,32 @@ async function loadBookList() {
return;
}
const key = await getOrCreateEncKey();
let key;
try {
key = await getOrCreateEncKey();
} catch (e) {
console.error('loadBookList: getOrCreateEncKey failed', e);
listEl.innerHTML = `<p class="muted">Encryption not available: ${e.message}. Make sure you are on HTTPS.</p>`;
return;
}
const decrypted = [];
for (const b of books) {
try {
const metaBuf = await decryptBytes(key, b.meta_iv, b.meta_ct);
const meta = JSON.parse(new TextDecoder().decode(metaBuf));
bookMetaCache[b.id] = {title: meta.title || '?', author: meta.author || '', type: meta.type || 'epub'};
decrypted.push({id: b.id, title: meta.title || '?', author: meta.author || '', type: meta.type || 'epub', scroll_fraction: b.scroll_fraction, uploaded_at: b.uploaded_at});
decrypted.push({id: b.id, title: meta.title || '?', author: meta.author || '', type: meta.type || 'epub', scroll_fraction: b.scroll_fraction, uploaded_at: b.uploaded_at, keyOk: true});
} catch (e) {
console.warn(`loadBookList: could not decrypt book #${b.id}:`, e.message);
bookMetaCache[b.id] = {title: `Book #${b.id}`, author: '', type: 'epub'};
decrypted.push({id: b.id, title: `Book #${b.id}`, author: '', type: 'epub', scroll_fraction: b.scroll_fraction, uploaded_at: b.uploaded_at});
decrypted.push({id: b.id, title: `Book #${b.id}`, author: '', type: 'epub', scroll_fraction: b.scroll_fraction, uploaded_at: b.uploaded_at, keyOk: false});
}
}
renderBookList(decrypted);
} catch (e) {
if (listEl) listEl.innerHTML = `<p class="muted">Error: ${e.message}</p>`;
console.error('loadBookList error:', e);
if (listEl) listEl.innerHTML = `<p class="muted">Error loading books: ${e.message}</p>`;
}
}
@ -2651,14 +2663,15 @@ function renderBookList(books) {
let html = '';
for (const b of books) {
const pct = Math.round((b.scroll_fraction || 0) * 100);
const keyWarning = b.keyOk === false ? '<span title="Wrong encryption key — import the correct key to open this book" style="color:var(--accent,#e63946);margin-left:4px;">⚠️ wrong key</span>' : '';
html += `<div class="book-item">
<div class="book-item-info">
<strong class="book-title">${escapeHtml(b.title)}</strong>
<strong class="book-title">${escapeHtml(b.title)}${keyWarning}</strong>
<span class="muted book-author">${escapeHtml(b.author)}</span>
${pct > 0 ? `<span class="muted book-progress">${pct}% read</span>` : ''}
</div>
<div class="book-item-actions">
<button class="btn btn-sm" onclick="openBook(${b.id})">Open</button>
<button class="btn btn-sm" onclick="openBook(${b.id})"${b.keyOk === false ? ' disabled title="Import the correct encryption key first"' : ''}>Open</button>
<button class="btn btn-sm btn-danger" onclick="deleteBook(${b.id})">Delete</button>
</div>
</div>`;
@ -3076,7 +3089,8 @@ async function openBook(bookId) {
}
} catch (e) {
contentEl.innerHTML = `<p class="muted">Failed to open book: ${escapeHtml(e.message)}</p>`;
overlay.style.display = 'none';
alert(`Failed to open book: ${e.message}`);
}
}