Fix Books tab: auto-generate encryption key, remove password prompt
- getOrCreateEncKey() now generates a random AES-GCM-256 key if none found in localStorage, instead of throwing an error - Removed enc-key-prompt div from player.html entirely - Simplified initBookDropZone() — removed prompt show/hide logic - book-upload-area always visible, no longer hidden behind prompt Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
500b3fa780
commit
2fad4a726c
2 changed files with 9 additions and 21 deletions
|
|
@ -1813,16 +1813,20 @@ function hexToBytes(hex) {
|
||||||
|
|
||||||
async function getOrCreateEncKey() {
|
async function getOrCreateEncKey() {
|
||||||
if (_encKey) return _encKey;
|
if (_encKey) return _encKey;
|
||||||
const storageKey = `diora_enc_key_${window.USER_ID || 'anon'}`;
|
const storageKey = `diora_enc_key_${window.USER_ID || 0}`;
|
||||||
const stored = localStorage.getItem(storageKey);
|
const stored = localStorage.getItem(storageKey);
|
||||||
if (stored) {
|
if (stored) {
|
||||||
try {
|
try {
|
||||||
const raw = base64ToBytes(stored);
|
const raw = base64ToBytes(stored);
|
||||||
_encKey = await crypto.subtle.importKey('raw', raw, {name: 'AES-GCM'}, false, ['encrypt', 'decrypt']);
|
_encKey = await crypto.subtle.importKey('raw', raw, {name: 'AES-GCM'}, false, ['encrypt', 'decrypt']);
|
||||||
return _encKey;
|
return _encKey;
|
||||||
} catch (e) { /* fall through */ }
|
} catch (e) { /* fall through, generate new */ }
|
||||||
}
|
}
|
||||||
throw new Error('Encryption key not found — please log out and log in again.');
|
// No key found — generate one and store it
|
||||||
|
_encKey = await crypto.subtle.generateKey({name: 'AES-GCM', length: 256}, true, ['encrypt', 'decrypt']);
|
||||||
|
const raw = await crypto.subtle.exportKey('raw', _encKey);
|
||||||
|
localStorage.setItem(storageKey, bytesToBase64(new Uint8Array(raw)));
|
||||||
|
return _encKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function encryptBytes(key, plainBytes) {
|
async function encryptBytes(key, plainBytes) {
|
||||||
|
|
@ -2205,14 +2209,6 @@ function initBookDropZone() {
|
||||||
if (!zone || !zone.contains(e.target)) e.preventDefault();
|
if (!zone || !zone.contains(e.target)) e.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
const storageKey = `diora_enc_key_${window.USER_ID || 'anon'}`;
|
|
||||||
const hasKey = !!localStorage.getItem(storageKey);
|
|
||||||
const prompt = $('enc-key-prompt');
|
|
||||||
const uploadArea = $('book-upload-area');
|
|
||||||
|
|
||||||
if (prompt) prompt.style.display = hasKey ? 'none' : '';
|
|
||||||
if (uploadArea) uploadArea.style.display = hasKey ? '' : 'none';
|
|
||||||
|
|
||||||
const zone = $('book-drop-zone');
|
const zone = $('book-drop-zone');
|
||||||
if (!zone) return;
|
if (!zone) return;
|
||||||
|
|
||||||
|
|
@ -2245,7 +2241,7 @@ async function deriveAndStoreKey() {
|
||||||
mat, {name: 'AES-GCM', length: 256}, true, ['encrypt', 'decrypt']
|
mat, {name: 'AES-GCM', length: 256}, true, ['encrypt', 'decrypt']
|
||||||
);
|
);
|
||||||
const raw = await crypto.subtle.exportKey('raw', key);
|
const raw = await crypto.subtle.exportKey('raw', key);
|
||||||
const storageKey = `diora_enc_key_${window.USER_ID || 'anon'}`;
|
const storageKey = `diora_enc_key_${window.USER_ID || 0}`;
|
||||||
localStorage.setItem(storageKey, bytesToBase64(new Uint8Array(raw)));
|
localStorage.setItem(storageKey, bytesToBase64(new Uint8Array(raw)));
|
||||||
_encKey = null; // reset cached key
|
_encKey = null; // reset cached key
|
||||||
if (statusEl) statusEl.textContent = '✓ Unlocked';
|
if (statusEl) statusEl.textContent = '✓ Unlocked';
|
||||||
|
|
|
||||||
|
|
@ -278,15 +278,7 @@
|
||||||
<!-- ===== BOOKS TAB ===== -->
|
<!-- ===== BOOKS TAB ===== -->
|
||||||
<section class="tab-panel" id="tab-books" style="display:none;">
|
<section class="tab-panel" id="tab-books" style="display:none;">
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
<div id="enc-key-prompt" class="enc-key-prompt">
|
<div id="book-upload-area">
|
||||||
<p class="muted">Enter your password to unlock encrypted storage on this device.</p>
|
|
||||||
<div class="search-bar">
|
|
||||||
<input type="password" id="enc-key-password" class="search-input" placeholder="Your password…">
|
|
||||||
<button class="btn" onclick="deriveAndStoreKey()">Unlock</button>
|
|
||||||
</div>
|
|
||||||
<span id="enc-key-status" class="muted"></span>
|
|
||||||
</div>
|
|
||||||
<div id="book-upload-area" style="display:none;">
|
|
||||||
<div class="book-drop-zone" id="book-drop-zone">
|
<div class="book-drop-zone" id="book-drop-zone">
|
||||||
<span>Drop .epub or .pdf here or <label for="book-file-input" style="cursor:pointer;text-decoration:underline;">browse</label></span>
|
<span>Drop .epub or .pdf here or <label for="book-file-input" style="cursor:pointer;text-decoration:underline;">browse</label></span>
|
||||||
<input type="file" id="book-file-input" accept=".epub,.pdf" style="display:none;" onchange="bookFileSelected(this)">
|
<input type="file" id="book-file-input" accept=".epub,.pdf" style="display:none;" onchange="bookFileSelected(this)">
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue