diora-web/static/js/app/56-cloud-import.js

154 lines
4.6 KiB
JavaScript
Raw Normal View History

'use strict';
// ---------------------------------------------------------------------------
// Cloud import — browse a user-configured WebDAV server and pull a book in.
//
// The download is proxied by the server because the remote host is a different
// origin and sends no CORS headers. It still lands here as raw bytes and goes
// through _importEbookBuffer like any local file, so what gets stored is
// ciphertext the server cannot read.
// ---------------------------------------------------------------------------
let _cloudSources = [];
let _cloudSourceId = null;
let _cloudPath = '';
async function toggleCloudImport() {
const panel = $('cloud-browser');
if (!panel) return;
if (panel.style.display !== 'none') { closeCloudImport(); return; }
try {
const res = await fetch('/books/cloud/sources/');
_cloudSources = (await res.json()).sources || [];
} catch (e) {
await customAlert('Cloud-Verbindungen konnten nicht geladen werden: ' + e.message);
return;
}
if (!_cloudSources.length) {
await customAlert(
'Noch keine Cloud-Verbindung eingerichtet. Unter Einstellungen → '
+ '„Cloud-Verbindungen für Bücher“ kannst du eine hinzufügen.'
);
return;
}
const select = $('cloud-source-select');
if (select) {
select.innerHTML = '';
for (const source of _cloudSources) {
const option = document.createElement('option');
option.value = source.id;
option.textContent = source.label;
select.appendChild(option);
}
select.style.display = _cloudSources.length > 1 ? '' : 'none';
}
panel.style.display = '';
_cloudSourceId = _cloudSources[0].id;
_cloudBrowse('');
}
function closeCloudImport() {
const panel = $('cloud-browser');
if (panel) panel.style.display = 'none';
}
function _onCloudSourceChange(value) {
_cloudSourceId = parseInt(value, 10);
_cloudBrowse('');
}
function _cloudParentPath(path) {
const parts = path.split('/').filter(Boolean);
parts.pop();
return parts.join('/');
}
function _formatBytes(n) {
if (!n) return '';
if (n < 1024) return n + ' B';
if (n < 1024 * 1024) return Math.round(n / 1024) + ' KB';
return (n / 1024 / 1024).toFixed(1) + ' MB';
}
function _cloudEntryRow(label, onClick, sizeText) {
const li = document.createElement('li');
li.className = 'cloud-entry';
const button = document.createElement('button');
button.type = 'button';
button.className = 'cloud-entry-name';
button.textContent = label;
button.addEventListener('click', onClick);
li.appendChild(button);
if (sizeText) {
const size = document.createElement('span');
size.className = 'cloud-entry-size';
size.textContent = sizeText;
li.appendChild(size);
}
return li;
}
async function _cloudBrowse(path) {
const listEl = $('cloud-entries');
const pathEl = $('cloud-path');
const statusEl = $('cloud-status');
if (!listEl) return;
if (statusEl) statusEl.textContent = 'Lade…';
listEl.innerHTML = '';
let data;
try {
const res = await fetch(`/books/cloud/${_cloudSourceId}/browse/?path=${encodeURIComponent(path)}`);
data = await res.json();
if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`);
} catch (e) {
if (statusEl) statusEl.textContent = 'Fehler: ' + e.message;
return;
}
_cloudPath = data.path || '';
if (pathEl) pathEl.textContent = '/' + _cloudPath;
if (statusEl) statusEl.textContent = '';
if (_cloudPath) {
listEl.appendChild(_cloudEntryRow('⬑ ..', () => _cloudBrowse(_cloudParentPath(_cloudPath))));
}
const entries = (data.entries || []).filter(e => e.is_dir || e.is_book);
if (!entries.length && statusEl) {
statusEl.textContent = 'Keine Bücher oder Ordner in diesem Verzeichnis.';
}
for (const entry of entries) {
listEl.appendChild(entry.is_dir
? _cloudEntryRow('📁 ' + entry.name, () => _cloudBrowse(entry.path))
: _cloudEntryRow('📖 ' + entry.name, () => _cloudImport(entry), _formatBytes(entry.size)));
}
}
async function _cloudImport(entry) {
const statusEl = $('cloud-status');
if (statusEl) statusEl.textContent = `Lade „${entry.name}“…`;
try {
const res = await fetch(`/books/cloud/${_cloudSourceId}/fetch/?path=${encodeURIComponent(entry.path)}`);
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(err.error || `HTTP ${res.status}`);
}
const buf = await res.arrayBuffer();
if (await _importEbookBuffer(buf, entry.name, statusEl) && statusEl) {
statusEl.textContent = `✓ „${entry.name}“ importiert`;
}
} catch (e) {
if (statusEl) statusEl.textContent = 'Import fehlgeschlagen: ' + e.message;
}
}