Fix ebooks: show wrong-key warning, close overlay on open failure
- 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:
parent
4f413c673e
commit
1649eb27a0
1 changed files with 22 additions and 8 deletions
|
|
@ -2611,13 +2611,15 @@ async function loadBookList() {
|
||||||
listEl.innerHTML = '<p class="muted">Loading…</p>';
|
listEl.innerHTML = '<p class="muted">Loading…</p>';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/books/');
|
const res = await fetch('/books/', {cache: 'no-store'});
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
|
console.error('loadBookList: server returned', res.status);
|
||||||
listEl.innerHTML = `<p class="muted">Server error ${res.status} loading books.</p>`;
|
listEl.innerHTML = `<p class="muted">Server error ${res.status} loading books.</p>`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const books = await res.json();
|
const books = await res.json();
|
||||||
if (!Array.isArray(books)) {
|
if (!Array.isArray(books)) {
|
||||||
|
console.error('loadBookList: unexpected response', books);
|
||||||
listEl.innerHTML = `<p class="muted">Unexpected response from server.</p>`;
|
listEl.innerHTML = `<p class="muted">Unexpected response from server.</p>`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -2626,22 +2628,32 @@ async function loadBookList() {
|
||||||
return;
|
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 = [];
|
const decrypted = [];
|
||||||
for (const b of books) {
|
for (const b of books) {
|
||||||
try {
|
try {
|
||||||
const metaBuf = await decryptBytes(key, b.meta_iv, b.meta_ct);
|
const metaBuf = await decryptBytes(key, b.meta_iv, b.meta_ct);
|
||||||
const meta = JSON.parse(new TextDecoder().decode(metaBuf));
|
const meta = JSON.parse(new TextDecoder().decode(metaBuf));
|
||||||
bookMetaCache[b.id] = {title: meta.title || '?', author: meta.author || '', type: meta.type || 'epub'};
|
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) {
|
} catch (e) {
|
||||||
|
console.warn(`loadBookList: could not decrypt book #${b.id}:`, e.message);
|
||||||
bookMetaCache[b.id] = {title: `Book #${b.id}`, author: '', type: 'epub'};
|
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);
|
renderBookList(decrypted);
|
||||||
} catch (e) {
|
} 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 = '';
|
let html = '';
|
||||||
for (const b of books) {
|
for (const b of books) {
|
||||||
const pct = Math.round((b.scroll_fraction || 0) * 100);
|
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">
|
html += `<div class="book-item">
|
||||||
<div class="book-item-info">
|
<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>
|
<span class="muted book-author">${escapeHtml(b.author)}</span>
|
||||||
${pct > 0 ? `<span class="muted book-progress">${pct}% read</span>` : ''}
|
${pct > 0 ? `<span class="muted book-progress">${pct}% read</span>` : ''}
|
||||||
</div>
|
</div>
|
||||||
<div class="book-item-actions">
|
<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>
|
<button class="btn btn-sm btn-danger" onclick="deleteBook(${b.id})">Delete</button>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
@ -3076,7 +3089,8 @@ async function openBook(bookId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} 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}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue