feat: B2 uit voor veilige archive-downloads

This commit is contained in:
kodi
2026-03-14 14:24:52 +01:00
parent 592b10acc2
commit d463b3977d
24 changed files with 754 additions and 195 deletions
+59 -3
View File
@@ -460,6 +460,40 @@ function markZipDownloadFailed(err) {
});
}
function updateZipDownloadTaskProgress(task) {
if (!downloadProgressState.active) {
return;
}
updateDownloadModalDisplay({
active: true,
targetText: "Preparing download...",
currentFileText: task.current_item ? `Current: ${task.current_item}` : `Selection: ${selectedItemCountLabel(downloadProgressState.totalItems)}`,
countText: task.total_items ? `${task.done_items || 0}/${task.total_items} top-level items` : "Preparing zip download",
statusText: task.status === "ready" ? "Download started" : "Preparing download...",
percent: task.status === "ready" ? 100 : 55,
});
}
function sleep(ms) {
return new Promise((resolve) => window.setTimeout(resolve, ms));
}
async function waitForArchiveDownloadReady(taskId) {
while (true) {
const task = await getTaskRequest(taskId);
if (task.status === "ready") {
return task;
}
if (task.status === "failed") {
const err = new Error(task.error_message || "Archive download failed");
err.code = task.error_code || null;
throw err;
}
updateZipDownloadTaskProgress(task);
await sleep(250);
}
}
function closeDownloadModal() {
if (downloadProgressState.active) {
return;
@@ -643,15 +677,20 @@ async function startDownloadSelected() {
}
try {
const selected = selectedItems[0];
if (zipDownload) {
const created = await createArchiveDownloadTask(selectedPaths);
const task = await waitForArchiveDownloadReady(created.task_id);
startArchiveDownload(task.id, task.destination);
markZipDownloadReady(task.destination);
setStatus(`Download started: ${task.destination}`);
return;
}
const { blob, fileName } = await downloadFileRequest(selectedPaths);
const url = URL.createObjectURL(blob);
const anchor = document.createElement("a");
anchor.href = url;
anchor.download = fileName || selected.name;
document.body.append(anchor);
if (zipDownload) {
markZipDownloadReady(anchor.download);
}
anchor.click();
anchor.remove();
URL.revokeObjectURL(url);
@@ -957,6 +996,23 @@ async function downloadFileRequest(paths) {
};
}
async function createArchiveDownloadTask(paths) {
return apiRequest("POST", "/api/files/download/archive-prepare", { paths });
}
async function getTaskRequest(taskId) {
return apiRequest("GET", `/api/tasks/${encodeURIComponent(taskId)}`);
}
function startArchiveDownload(taskId, fileName) {
const anchor = document.createElement("a");
anchor.href = `/api/files/download/archive/${encodeURIComponent(taskId)}`;
anchor.download = fileName || "";
document.body.append(anchor);
anchor.click();
anchor.remove();
}
async function uploadFileRequest(targetPath, file, overwrite = false) {
const formData = new FormData();
formData.append("target_path", targetPath);