From d7bee5a7b8cb402072e04dcd03dd7db6bb3a3696 Mon Sep 17 00:00:00 2001 From: amcmanu3 Date: Mon, 27 May 2024 19:12:31 -0400 Subject: [PATCH] Refactor uploads to same JS file --- .../static/assets/js/shared/upload.js | 243 +++++++++++++----- .../templates/panel/server_files.html | 147 ++--------- 2 files changed, 195 insertions(+), 195 deletions(-) diff --git a/app/frontend/static/assets/js/shared/upload.js b/app/frontend/static/assets/js/shared/upload.js index 0606e03e..7ea043af 100644 --- a/app/frontend/static/assets/js/shared/upload.js +++ b/app/frontend/static/assets/js/shared/upload.js @@ -1,90 +1,207 @@ -async function uploadFile(type) { - file = $("#file")[0].files[0] +async function uploadFile(type, file = null, path = null, file_num = 0, _onProgress) { + if (file == null) { + try { + file = $("#file")[0].files[0]; + } catch { + bootbox.alert("Please select a file first.") + return; + } + + } const fileId = uuidv4(); - const token = getCookie("_xsrf") - document.getElementById("upload_input").innerHTML = '
 
' - if (!file) { - alert("Please select a file first."); - return; + const token = getCookie("_xsrf"); + if (type !== "server_upload") { + document.getElementById("upload_input").innerHTML = '
 
'; } + let url = `` + if (type === "server_upload") { + url = `/api/v2/servers/${serverId}/files/upload/`; + } else if (type === "background") { + url = `/api/v2/crafty/admin/upload/` + } else if (type === "import") { + url = `/api/v2/servers/import/upload/` + } + console.log(url) const chunkSize = 1024 * 1024; // 1MB const totalChunks = Math.ceil(file.size / chunkSize); + const file_hash = await calculateFileHash(file); const uploadPromises = []; - let res = await fetch(`/api/v2/servers/import/upload/`, { - method: 'POST', - headers: { - 'X-XSRFToken': token, - 'chunked': true, - 'fileSize': file.size, - 'type': type, - 'total_chunks': totalChunks, - 'filename': file.name, - 'fileId': fileId, - }, - body: null, - }); - - let responseData = await res.json(); - - let file_id = "" - if (responseData.status === "ok") { - file_id = responseData.data["file-id"] - } - for (let i = 0; i < totalChunks; i++) { - const start = i * chunkSize; - const end = Math.min(start + chunkSize, file.size); - const chunk = file.slice(start, end); - - const uploadPromise = fetch(`/api/v2/servers/import/upload/`, { + let errors = []; // Array to store errors + try { + let res = await fetch(url, { method: 'POST', - body: chunk, headers: { - 'Content-Range': `bytes ${start}-${end - 1}/${file.size}`, - 'Content-Length': chunk.size, - 'fileSize': file.size, + 'X-XSRFToken': token, 'chunked': true, + 'fileHash': file_hash, + 'fileSize': file.size, 'type': type, 'total_chunks': totalChunks, 'filename': file.name, + 'location': path, 'fileId': fileId, - 'chunkId': i, }, - }).then(response => response.json()) - .then(data => { - if (data.status === "completed") { - $("#upload_input").html(`
🔒
`); - if (type === "import") { - document.getElementById("lower_half").style.visibility = "visible"; - document.getElementById("lower_half").hidden = false; - } else if (type === "background") { - setTimeout(function () { - location.href = `/panel/custom_login` - }, 2000) + body: null, + }); + if (!res.ok) { + let errorResponse = await res.json(); + throw new Error(JSON.stringify(errorResponse)); + } + + let responseData = await res.json(); + + if (responseData.status !== "ok") { + throw new Error(JSON.stringify(responseData)); + } + + for (let i = 0; i < totalChunks; i++) { + const start = i * chunkSize; + const end = Math.min(start + chunkSize, file.size); + const chunk = file.slice(start, end); + const chunk_hash = await calculateFileHash(chunk); + + const uploadPromise = fetch(url, { + method: 'POST', + body: chunk, + headers: { + 'Content-Range': `bytes ${start}-${end - 1}/${file.size}`, + 'Content-Length': chunk.size, + 'fileSize': file.size, + 'fileHash': file_hash, + 'chunkHash': chunk_hash, + 'chunked': true, + 'type': type, + 'total_chunks': totalChunks, + 'filename': file.name, + 'location': path, + 'fileId': fileId, + 'chunkId': i, + }, + }) + .then(async response => { + if (!response.ok) { + const errorData = await response.json(); + throw new Error(JSON.stringify(errorData) || 'Unknown error occurred'); } - } else if (data.status !== "partial") { - throw new Error(data.message); - } - // Update progress bar - const progress = (i + 1) / totalChunks * 100; - updateProgressBar(Math.round(progress)); - }); + return response.json(); // Return the JSON data + }) + .then(data => { + if (data.status !== "completed" && data.status !== "partial") { + throw new Error(data.message || 'Unknown error occurred'); + } + // Update progress bar + const progress = (i + 1) / totalChunks * 100; + updateProgressBar(Math.round(progress), type, file_num); + }) + .catch(error => { + errors.push(error); // Store the error + }); - uploadPromises.push(uploadPromise); - } + uploadPromises.push(uploadPromise); + } - try { await Promise.all(uploadPromises); } catch (error) { - bootbox.alert("Error uploading file: " + error.message); + errors.push(error); // Store the error + } + + if (errors.length > 0) { + const errorMessage = errors.map(error => JSON.parse(error.message).data.message || 'Unknown error occurred').join('
'); + console.log(errorMessage) + bootbox.alert({ + title: 'Error', + message: errorMessage, + callback: function () { + window.location.reload(); + }, + }); + } else { + if (type !== "server_upload") { + // All promises resolved successfully + $("#upload_input").html(`
🔒
`); + if (type === "import") { + document.getElementById("lower_half").style.visibility = "visible"; + document.getElementById("lower_half").hidden = false; + } else if (type === "background") { + setTimeout(function () { + location.href = `/panel/custom_login`; + }, 2000); + } + } else { + let caught = false; + let expanded = false; + try { + expanded = document.getElementById(path).classList.contains("clicked"); + } catch { } + + let par_el; + let items; + try { + par_el = document.getElementById(path + "ul"); + items = par_el.children; + } catch (err) { + console.log(err); + caught = true; + par_el = document.getElementById("files-tree"); + items = par_el.children; + } + + let name = file.name; + let full_path = path + '/' + name; + let flag = false; + + for (let k = 0; k < items.length; ++k) { + if ($(items[k]).attr("data-name") == name) { + flag = true; + } + } + + if (!flag) { + if (caught && !expanded) { + $(par_el).append(`
  • ${name}
  • `); + } else if (expanded) { + $(par_el).append(`
  • ${name}
  • `); + } + setTreeViewContext(); + } + + $(`#upload-progress-bar-${file_num + 1}`).removeClass("progress-bar-striped"); + $(`#upload-progress-bar-${file_num + 1}`).addClass("bg-success"); + $(`#upload-progress-bar-${file_num + 1}`).html(''); + } } } -function updateProgressBar(progress) { - $(`#upload-progress-bar`).css('width', progress + '%'); - $(`#upload-progress-bar`).html(progress + '%'); +async function calculateFileHash(file) { + const arrayBuffer = await file.arrayBuffer(); + const hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); + + return hashHex; +} + +function updateProgressBar(progress, type, i) { + if (type !== "server_upload") { + if (progress === 100) { + $(`#upload-progress-bar`).removeClass("progress-bar-striped") + + $(`#upload-progress-bar`).removeClass("progress-bar-animated") + } + $(`#upload-progress-bar`).css('width', progress + '%'); + $(`#upload-progress-bar`).html(progress + '%'); + } else { + if (progress === 100) { + $(`#upload-progress-bar-${i + 1}`).removeClass("progress-bar-striped") + + $(`#upload-progress-bar-${i + 1}`).removeClass("progress-bar-animated") + } + $(`#upload-progress-bar-${i + 1}`).css('width', progress + '%'); + $(`#upload-progress-bar-${i + 1}`).html(progress + '%'); + } } function uuidv4() { diff --git a/app/frontend/templates/panel/server_files.html b/app/frontend/templates/panel/server_files.html index 7dae4934..ce7d2a9d 100644 --- a/app/frontend/templates/panel/server_files.html +++ b/app/frontend/templates/panel/server_files.html @@ -723,131 +723,7 @@ } } - async function uploadFile(file, path, file_num, onProgress) { - const fileId = uuidv4(); - const token = getCookie("_xsrf") - if (!file) { - alert("Please select a file first."); - return; - } - - const chunkSize = 1024 * 1024; // 1MB - const totalChunks = Math.ceil(file.size / chunkSize); - - const uploadPromises = []; - let res = await fetch(`/api/v2/servers/${serverId}/files/upload/`, { - method: 'POST', - headers: { - 'X-XSRFToken': token, - 'chunked': true, - 'type': "server_upload", - 'fileSize': file.size, - 'total_chunks': totalChunks, - 'location': path, - 'filename': file.name, - 'fileId': fileId, - }, - body: null, - }); - - let responseData = await res.json(); - - let file_id = "" - if (responseData.status === "ok") { - file_id = responseData.data["file-id"] - } - for (let i = 0; i < totalChunks; i++) { - const start = i * chunkSize; - const end = Math.min(start + chunkSize, file.size); - const chunk = file.slice(start, end); - - const uploadPromise = fetch(`/api/v2/servers/${serverId}/files/upload/`, { - method: 'POST', - body: chunk, - headers: { - 'Content-Range': `bytes ${start}-${end - 1}/${file.size}`, - 'Content-Length': chunk.size, - 'chunked': true, - 'type': "server_upload", - 'fileSize': file.size, - 'total_chunks': totalChunks, - 'filename': file.name, - 'location': path, - 'fileId': fileId, - 'chunkId': i, - }, - }).then(response => response.json()) - .then(data => { - if (data.status === "completed") { - let caught = false; - try { - if (document.getElementById(path).classList.contains("clicked")) { - var expanded = true; - } - } catch { - var expanded = false; - } - - try { - var par_el = document.getElementById(path + "ul"); - var items = par_el.children; - } catch (err) { - console.log(err) - caught = true; - var par_el = document.getElementById("files-tree"); - var items = par_el.children; - } - let name = file.name; - console.log(par_el) - let full_path = path + '/' + name - let flag = false; - for (var k = 0; k < items.length; ++k) { - if ($(items[k]).attr("data-name") == name) { - flag = true; - } - } - if (!flag) { - if (caught && expanded == false) { - $(par_el).append('
  • ' + name + '
  • '); - } else if (expanded == true) { - $(par_el).append('
  • ' + name + '
  • '); - } - setTreeViewContext(); - } - $(`#upload-progress-bar-${i + 1}`).removeClass("progress-bar-striped"); - $(`#upload-progress-bar-${i + 1}`).addClass("bg-success"); - $(`#upload-progress-bar-${i + 1}`).html('') - } else if (data.status !== "partial") { - throw new Error(data.message); - } - // Update progress bar - const progress = (i + 1) / totalChunks * 100; - updateProgressBar(Math.round(progress), file_num); - }); - - uploadPromises.push(uploadPromise); - } - - try { - await Promise.all(uploadPromises); - } catch (error) { - alert("Error uploading file: " + error.message); - } - } - function updateProgressBar(progress, i) { - $(`#upload-progress-bar-${i + 1}`).css('width', progress + '%'); - $(`#upload-progress-bar-${i + 1}`).html(progress + '%'); - } - function uuidv4() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { - const r = Math.random() * 16 | 0, - v = c === 'x' ? r : (r & 0x3 | 0x8); - return v.toString(16); - }); - } - let uploadWaitDialog; - let doUpload = true; async function uploadFilesE(event) { path = event.target.parentElement.getAttribute('data-path'); @@ -907,19 +783,17 @@ $('#upload-progress-bar-parent').append(progressHtml); - const uploadPromise = uploadFile(file, path, i, (progress) => { + const uploadPromise = uploadFile("server_upload", file, path, i, (progress) => { $(`#upload-progress-bar-${i + 1}`).attr('aria-valuenow', progress) $(`#upload-progress-bar-${i + 1}`).css('width', progress + '%'); }); uploadPromises.push(uploadPromise); } - try { - await Promise.all(uploadPromises); - hideUploadBox(); - } catch (error) { - alert("Error uploading file: " + error.message); - } + + await Promise.all(uploadPromises); + hideUploadBox(); + } } } @@ -927,6 +801,15 @@ }); } + async function calculateFileHash(file) { + const arrayBuffer = await file.arrayBuffer(); + const hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); + + return hashHex; + } + function getDirView(event) { let path = event.target.parentElement.getAttribute("data-path"); if (document.getElementById(path).classList.contains('clicked')) { @@ -1232,5 +1115,5 @@ - + {% end %} \ No newline at end of file