mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Refactor upload route
Import uploads broke
This commit is contained in:
parent
7c8781e09e
commit
45aacb97c8
@ -44,8 +44,8 @@ from app.classes.web.routes.api.servers.server.files import (
|
|||||||
ApiServersServerFilesIndexHandler,
|
ApiServersServerFilesIndexHandler,
|
||||||
ApiServersServerFilesCreateHandler,
|
ApiServersServerFilesCreateHandler,
|
||||||
ApiServersServerFilesZipHandler,
|
ApiServersServerFilesZipHandler,
|
||||||
ApiServersServerFilesUploadHandler,
|
|
||||||
)
|
)
|
||||||
|
from app.classes.web.routes.api.crafty.upload.index import ApiFilesUploadHandler
|
||||||
from app.classes.web.routes.api.servers.server.tasks.task.children import (
|
from app.classes.web.routes.api.servers.server.tasks.task.children import (
|
||||||
ApiServersServerTasksTaskChildrenHandler,
|
ApiServersServerTasksTaskChildrenHandler,
|
||||||
)
|
)
|
||||||
@ -239,9 +239,19 @@ def api_handlers(handler_args):
|
|||||||
ApiServersServerFilesZipHandler,
|
ApiServersServerFilesZipHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/crafty/admin/upload/?",
|
||||||
|
ApiFilesUploadHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/servers/import/upload/?",
|
||||||
|
ApiFilesUploadHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
(
|
(
|
||||||
r"/api/v2/servers/([a-z0-9-]+)/files/upload/?",
|
r"/api/v2/servers/([a-z0-9-]+)/files/upload/?",
|
||||||
ApiServersServerFilesUploadHandler,
|
ApiFilesUploadHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
183
app/classes/web/routes/api/crafty/upload/index.py
Normal file
183
app/classes/web/routes/api/crafty/upload/index.py
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
import shutil
|
||||||
|
from jsonschema import validate
|
||||||
|
from jsonschema.exceptions import ValidationError
|
||||||
|
from app.classes.models.server_permissions import EnumPermissionsServer
|
||||||
|
from app.classes.shared.helpers import Helpers
|
||||||
|
from app.classes.shared.main_controller import WebSocketManager, Controller
|
||||||
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
|
||||||
|
class ApiFilesUploadHandler(BaseApiHandler):
|
||||||
|
async def post(self, server_id=None):
|
||||||
|
auth_data = self.authenticate_user()
|
||||||
|
if not auth_data:
|
||||||
|
return
|
||||||
|
|
||||||
|
upload_type = self.request.headers.get("type")
|
||||||
|
for header, value in self.request.headers.items():
|
||||||
|
print(f"{header}: {value}")
|
||||||
|
if server_id:
|
||||||
|
if server_id not in [str(x["server_id"]) for x in auth_data[0]]:
|
||||||
|
# if the user doesn't have access to the server, return an error
|
||||||
|
return self.finish_json(
|
||||||
|
400, {"status": "error", "error": "NOT_AUTHORIZED"}
|
||||||
|
)
|
||||||
|
mask = self.controller.server_perms.get_lowest_api_perm_mask(
|
||||||
|
self.controller.server_perms.get_user_permissions_mask(
|
||||||
|
auth_data[4]["user_id"], server_id
|
||||||
|
),
|
||||||
|
auth_data[5],
|
||||||
|
)
|
||||||
|
server_permissions = self.controller.server_perms.get_permissions(mask)
|
||||||
|
if EnumPermissionsServer.FILES not in server_permissions:
|
||||||
|
# if the user doesn't have Files permission, return an error
|
||||||
|
return self.finish_json(
|
||||||
|
400, {"status": "error", "error": "NOT_AUTHORIZED"}
|
||||||
|
)
|
||||||
|
u_type = "server_upload"
|
||||||
|
elif auth_data[4]["superuser"] and not upload_type != "import":
|
||||||
|
u_type = "admin_config"
|
||||||
|
self.upload_dir = os.path.join(
|
||||||
|
self.controller.project_root,
|
||||||
|
"app/frontend/static/assets/images/auth/custom",
|
||||||
|
)
|
||||||
|
elif upload_type == "import":
|
||||||
|
if not self.controller.crafty_perms.can_create_server(
|
||||||
|
auth_data[4]["user_id"]
|
||||||
|
):
|
||||||
|
return self.finish_json(
|
||||||
|
400,
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"error": "NOT_AUTHORIZED",
|
||||||
|
"data": {"message": ""},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
self.upload_dir = os.path.join(
|
||||||
|
self.controller.project_root, "import", "upload"
|
||||||
|
)
|
||||||
|
u_type = "server_import"
|
||||||
|
# Get the headers from the request
|
||||||
|
fileHash = self.request.headers.get("fileHash", 0)
|
||||||
|
chunkHash = self.request.headers.get("chunk-hash", 0)
|
||||||
|
self.file_id = self.request.headers.get("fileId")
|
||||||
|
self.chunked = self.request.headers.get("chunked", True)
|
||||||
|
self.filename = self.request.headers.get("filename", None)
|
||||||
|
try:
|
||||||
|
file_size = int(self.request.headers.get("fileSize", None))
|
||||||
|
total_chunks = int(self.request.headers.get("total_chunks", None))
|
||||||
|
except TypeError:
|
||||||
|
return self.finish_json(
|
||||||
|
400, {"status": "error", "error": "TYPE ERROR", "data": {}}
|
||||||
|
)
|
||||||
|
self.chunk_index = self.request.headers.get("chunkId")
|
||||||
|
if u_type == "server_upload":
|
||||||
|
self.upload_dir = self.request.headers.get("location", None)
|
||||||
|
self.temp_dir = os.path.join(self.controller.project_root, "temp", self.file_id)
|
||||||
|
|
||||||
|
_total, _used, free = shutil.disk_usage(self.upload_dir)
|
||||||
|
|
||||||
|
# Check to see if we have enough space
|
||||||
|
if free <= file_size:
|
||||||
|
self.finish_json(
|
||||||
|
507,
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"error": "NO STORAGE SPACE",
|
||||||
|
"data": {"message": "Out Of Space!"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# If this has no chunk index we know it's the inital request
|
||||||
|
if self.chunked and not self.chunk_index:
|
||||||
|
return self.finish_json(
|
||||||
|
200, {"status": "ok", "data": {"file-id": self.file_id}}
|
||||||
|
)
|
||||||
|
|
||||||
|
if not self.chunked:
|
||||||
|
with open(os.path.join(self.upload_dir, self.filename), "wb") as file:
|
||||||
|
while True:
|
||||||
|
chunk = self.request.body
|
||||||
|
if not chunk:
|
||||||
|
break
|
||||||
|
file.write(chunk)
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
"status": "completed",
|
||||||
|
"data": {"message": "File uploaded successfully"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create the upload and temp directories if they don't exist
|
||||||
|
os.makedirs(self.upload_dir, exist_ok=True)
|
||||||
|
os.makedirs(self.temp_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# Read headers and query parameters
|
||||||
|
content_length = int(self.request.headers.get("Content-Length"))
|
||||||
|
if content_length <= 0:
|
||||||
|
return self.finish_json(
|
||||||
|
400,
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"error": "INVALID CONTENT LENGTH",
|
||||||
|
"data": {"message": "Invalid content length"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if not self.filename or self.chunk_index is None or total_chunks is None:
|
||||||
|
return self.finish_json(
|
||||||
|
400,
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"error": "INDEX ERROR",
|
||||||
|
"data": {
|
||||||
|
"message": "Filename, chunk_index,"
|
||||||
|
" and total_chunks are required"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# File paths
|
||||||
|
file_path = os.path.join(self.upload_dir, self.filename)
|
||||||
|
chunk_path = os.path.join(
|
||||||
|
self.temp_dir, f"{self.filename}.part{self.chunk_index}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Save the chunk
|
||||||
|
with open(chunk_path, "wb") as f:
|
||||||
|
f.write(self.request.body)
|
||||||
|
|
||||||
|
# Check if all chunks are received
|
||||||
|
received_chunks = [
|
||||||
|
f
|
||||||
|
for f in os.listdir(self.temp_dir)
|
||||||
|
if f.startswith(f"{self.filename}.part")
|
||||||
|
]
|
||||||
|
if len(received_chunks) == total_chunks:
|
||||||
|
with open(file_path, "wb") as outfile:
|
||||||
|
for i in range(total_chunks):
|
||||||
|
chunk_file = os.path.join(self.temp_dir, f"{self.filename}.part{i}")
|
||||||
|
with open(chunk_file, "rb") as infile:
|
||||||
|
outfile.write(infile.read())
|
||||||
|
os.remove(chunk_file)
|
||||||
|
|
||||||
|
self.finish_json(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
"status": "completed",
|
||||||
|
"data": {"message": "File uploaded successfully"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.write(
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"status": "partial",
|
||||||
|
"message": f"Chunk {self.chunk_index} received",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
@ -578,91 +578,3 @@ class ApiServersServerFilesZipHandler(BaseApiHandler):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
return self.finish_json(200, {"status": "ok"})
|
return self.finish_json(200, {"status": "ok"})
|
||||||
|
|
||||||
|
|
||||||
class ApiServersServerFilesUploadHandler(BaseApiHandler):
|
|
||||||
async def post(self, server_id: str):
|
|
||||||
for header, value in self.request.headers.items():
|
|
||||||
print(f"{header}: {value}")
|
|
||||||
fileHash = self.request.headers.get("fileHash", 0)
|
|
||||||
chunkHash = self.request.headers.get("chunk-hash", 0)
|
|
||||||
file_size = self.request.headers.get("fileSize", None)
|
|
||||||
self.file_id = self.request.headers.get("fileId")
|
|
||||||
self.chunked = self.request.headers.get("chunked", True)
|
|
||||||
self.filename = self.request.headers.get("filename", None)
|
|
||||||
try:
|
|
||||||
total_chunks = int(self.request.headers.get("total_chunks", None))
|
|
||||||
except TypeError:
|
|
||||||
return self.finish_json(
|
|
||||||
400, {"status": "error", "data": "INVALID CHUNK COUNT"}
|
|
||||||
)
|
|
||||||
self.chunk_index = self.request.headers.get("chunkId")
|
|
||||||
self.location = self.request.headers.get("location", None)
|
|
||||||
self.upload_dir = self.location
|
|
||||||
self.temp_dir = os.path.join(self.controller.project_root, "temp", self.file_id)
|
|
||||||
if self.chunked and not self.chunk_index:
|
|
||||||
return self.finish_json(
|
|
||||||
200, {"status": "ok", "data": {"file-id": self.file_id}}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Create the upload and temp directories if they don't exist
|
|
||||||
os.makedirs(self.upload_dir, exist_ok=True)
|
|
||||||
os.makedirs(self.temp_dir, exist_ok=True)
|
|
||||||
|
|
||||||
# Read headers and query parameters
|
|
||||||
content_length = int(self.request.headers.get("Content-Length"))
|
|
||||||
if content_length <= 0:
|
|
||||||
return self.finish_json(
|
|
||||||
400, {"status": "error", "data": {"message": "Invalid content length"}}
|
|
||||||
)
|
|
||||||
|
|
||||||
if not self.filename or self.chunk_index is None or total_chunks is None:
|
|
||||||
return self.finish_json(
|
|
||||||
400,
|
|
||||||
{
|
|
||||||
"status": "error",
|
|
||||||
"data": {
|
|
||||||
"message": "Filename, chunk_index,"
|
|
||||||
" and total_chunks are required"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
# File paths
|
|
||||||
file_path = os.path.join(self.upload_dir, self.filename)
|
|
||||||
chunk_path = os.path.join(
|
|
||||||
self.temp_dir, f"{self.filename}.part{self.chunk_index}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Save the chunk
|
|
||||||
with open(chunk_path, "wb") as f:
|
|
||||||
f.write(self.request.body)
|
|
||||||
|
|
||||||
# Check if all chunks are received
|
|
||||||
received_chunks = [
|
|
||||||
f
|
|
||||||
for f in os.listdir(self.temp_dir)
|
|
||||||
if f.startswith(f"{self.filename}.part")
|
|
||||||
]
|
|
||||||
if len(received_chunks) == total_chunks:
|
|
||||||
with open(file_path, "wb") as outfile:
|
|
||||||
for i in range(total_chunks):
|
|
||||||
chunk_file = os.path.join(self.temp_dir, f"{self.filename}.part{i}")
|
|
||||||
with open(chunk_file, "rb") as infile:
|
|
||||||
outfile.write(infile.read())
|
|
||||||
os.remove(chunk_file)
|
|
||||||
|
|
||||||
self.write(
|
|
||||||
json.dumps(
|
|
||||||
{"status": "completed", "message": "File uploaded successfully"}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.write(
|
|
||||||
json.dumps(
|
|
||||||
{
|
|
||||||
"status": "partial",
|
|
||||||
"message": f"Chunk {self.chunk_index} received",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
@ -723,10 +723,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uploadFile(file, path, onProgress) {
|
async function uploadFile(file, path, file_num, onProgress) {
|
||||||
const fileId = uuidv4();
|
const fileId = uuidv4();
|
||||||
const token = getCookie("_xsrf")
|
const token = getCookie("_xsrf")
|
||||||
const fileInput = document.getElementById('fileInput');
|
|
||||||
if (!file) {
|
if (!file) {
|
||||||
alert("Please select a file first.");
|
alert("Please select a file first.");
|
||||||
return;
|
return;
|
||||||
@ -741,6 +740,8 @@
|
|||||||
headers: {
|
headers: {
|
||||||
'X-XSRFToken': token,
|
'X-XSRFToken': token,
|
||||||
'chunked': true,
|
'chunked': true,
|
||||||
|
'type': "server_upload",
|
||||||
|
'fileSize': file.size,
|
||||||
'total_chunks': totalChunks,
|
'total_chunks': totalChunks,
|
||||||
'location': path,
|
'location': path,
|
||||||
'filename': file.name,
|
'filename': file.name,
|
||||||
@ -767,20 +768,61 @@
|
|||||||
'Content-Range': `bytes ${start}-${end - 1}/${file.size}`,
|
'Content-Range': `bytes ${start}-${end - 1}/${file.size}`,
|
||||||
'Content-Length': chunk.size,
|
'Content-Length': chunk.size,
|
||||||
'chunked': true,
|
'chunked': true,
|
||||||
|
'type': "server_upload",
|
||||||
|
'fileSize': file.size,
|
||||||
'total_chunks': totalChunks,
|
'total_chunks': totalChunks,
|
||||||
'filename': file.name,
|
'filename': file.name,
|
||||||
'location': path,
|
'location': path,
|
||||||
'filename': file.name,
|
|
||||||
'fileId': fileId,
|
'fileId': fileId,
|
||||||
'chunkId': i,
|
'chunkId': i,
|
||||||
},
|
},
|
||||||
}).then(response => response.json())
|
}).then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.status === "completed") {
|
if (data.status === "completed") {
|
||||||
alert("File uploaded successfully!");
|
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('<li id=' + '"' + full_path.toString() + 'li' + '"' + 'class="d-block tree-ctx-item tree-file tree-item" data-path=' + '"' + full_path.toString() + '"' + ' data-name=' + '"' + name.toString() + '"' + ' onclick="clickOnFile(event)" ><span style="margin-right: 6px;"><i class="far fa-file"></i></span>' + name + '</li>');
|
||||||
|
} else if (expanded == true) {
|
||||||
|
$(par_el).append('<li id=' + '"' + full_path.toString() + 'li' + '"' + 'class="tree-ctx-item tree-file tree-item" data-path=' + '"' + full_path.toString() + '"' + ' data-name=' + '"' + name.toString() + '"' + ' onclick="clickOnFile(event)" ><span style="margin-right: 6px;"><i class="far fa-file"></i></span>' + name + '</li>');
|
||||||
|
}
|
||||||
|
setTreeViewContext();
|
||||||
|
}
|
||||||
|
$(`#upload-progress-bar-${i + 1}`).removeClass("progress-bar-striped");
|
||||||
|
$(`#upload-progress-bar-${i + 1}`).addClass("bg-success");
|
||||||
|
$(`#upload-progress-bar-${i + 1}`).html('<i style="color: black;" class="fas fa-box-check"></i>')
|
||||||
} else if (data.status !== "partial") {
|
} else if (data.status !== "partial") {
|
||||||
throw new Error(data.message);
|
throw new Error(data.message);
|
||||||
}
|
}
|
||||||
|
// Update progress bar
|
||||||
|
const progress = (i + 1) / totalChunks * 100;
|
||||||
|
updateProgressBar(Math.round(progress), file_num);
|
||||||
});
|
});
|
||||||
|
|
||||||
uploadPromises.push(uploadPromise);
|
uploadPromises.push(uploadPromise);
|
||||||
@ -792,7 +834,10 @@
|
|||||||
alert("Error uploading file: " + error.message);
|
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() {
|
function uuidv4() {
|
||||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||||
const r = Math.random() * 16 | 0,
|
const r = Math.random() * 16 | 0,
|
||||||
@ -801,103 +846,6 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendFile(file, path, serverId, left, i, onProgress) {
|
|
||||||
let xmlHttpRequest = new XMLHttpRequest();
|
|
||||||
let token = getCookie("_xsrf")
|
|
||||||
let fileName = file.name
|
|
||||||
let target = '/upload?server_id=' + serverId
|
|
||||||
let mimeType = file.type
|
|
||||||
let size = file.size
|
|
||||||
|
|
||||||
xmlHttpRequest.upload.addEventListener('progress', function (e) {
|
|
||||||
|
|
||||||
if (e.loaded <= size) {
|
|
||||||
var percent = Math.round(e.loaded / size * 100);
|
|
||||||
$(`#upload-progress-bar-${i + 1}`).css('width', percent + '%');
|
|
||||||
$(`#upload-progress-bar-${i + 1}`).html(percent + '%');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
xmlHttpRequest.open('POST', target, true);
|
|
||||||
xmlHttpRequest.setRequestHeader('X-Content-Type', mimeType);
|
|
||||||
xmlHttpRequest.setRequestHeader('X-XSRFToken', token);
|
|
||||||
xmlHttpRequest.setRequestHeader('X-Content-Length', size);
|
|
||||||
xmlHttpRequest.setRequestHeader('X-Content-Disposition', 'attachment; filename="' + fileName + '"');
|
|
||||||
xmlHttpRequest.setRequestHeader('X-Path', path);
|
|
||||||
xmlHttpRequest.setRequestHeader('X-Content-Upload-Type', 'server_files')
|
|
||||||
xmlHttpRequest.setRequestHeader('X-Files-Left', left);
|
|
||||||
xmlHttpRequest.setRequestHeader('X-FileName', fileName);
|
|
||||||
xmlHttpRequest.setRequestHeader('X-ServerId', serverId);
|
|
||||||
xmlHttpRequest.upload.addEventListener('progress', (event) =>
|
|
||||||
onProgress(Math.floor(event.loaded / event.total * 100)), false);
|
|
||||||
xmlHttpRequest.addEventListener('load', (event) => {
|
|
||||||
if (event.target.responseText == 'success') {
|
|
||||||
console.log('Upload for file', file.name, 'was successful!');
|
|
||||||
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('<li id=' + '"' + full_path.toString() + 'li' + '"' + 'class="d-block tree-ctx-item tree-file tree-item" data-path=' + '"' + full_path.toString() + '"' + ' data-name=' + '"' + name.toString() + '"' + ' onclick="clickOnFile(event)" ><span style="margin-right: 6px;"><i class="far fa-file"></i></span>' + name + '</li>');
|
|
||||||
} else if (expanded == true) {
|
|
||||||
$(par_el).append('<li id=' + '"' + full_path.toString() + 'li' + '"' + 'class="tree-ctx-item tree-file tree-item" data-path=' + '"' + full_path.toString() + '"' + ' data-name=' + '"' + name.toString() + '"' + ' onclick="clickOnFile(event)" ><span style="margin-right: 6px;"><i class="far fa-file"></i></span>' + name + '</li>');
|
|
||||||
}
|
|
||||||
setTreeViewContext();
|
|
||||||
}
|
|
||||||
$(`#upload-progress-bar-${i + 1}`).removeClass("progress-bar-striped");
|
|
||||||
$(`#upload-progress-bar-${i + 1}`).addClass("bg-success");
|
|
||||||
$(`#upload-progress-bar-${i + 1}`).html('<i style="color: black;" class="fas fa-box-check"></i>')
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
let response_text = JSON.parse(event.target.responseText);
|
|
||||||
var x = document.querySelector('.bootbox');
|
|
||||||
if (x) {
|
|
||||||
x.remove()
|
|
||||||
}
|
|
||||||
var x = document.querySelector('.modal-content');
|
|
||||||
if (x) {
|
|
||||||
x.remove()
|
|
||||||
}
|
|
||||||
console.log(JSON.parse(event.target.responseText).info)
|
|
||||||
bootbox.alert({
|
|
||||||
message: JSON.parse(event.target.responseText).info,
|
|
||||||
callback: function () {
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
doUpload = false;
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
xmlHttpRequest.addEventListener('error', (e) => {
|
|
||||||
console.error('Error while uploading file', file.name + '.', 'Event:', e)
|
|
||||||
}, false);
|
|
||||||
xmlHttpRequest.send(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
let uploadWaitDialog;
|
let uploadWaitDialog;
|
||||||
let doUpload = true;
|
let doUpload = true;
|
||||||
|
|
||||||
@ -938,51 +886,44 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
let nFiles = files.files.length;
|
let nFiles = files.files.length;
|
||||||
for (i = 0; i < nFiles; i++) {
|
const uploadPromises = [];
|
||||||
if (!doUpload) {
|
|
||||||
doUpload = true;
|
|
||||||
hideUploadBox();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for (let i = 0; i < nFiles; i++) {
|
||||||
|
const file = files.files[i];
|
||||||
const progressHtml = `
|
const progressHtml = `
|
||||||
<div style="width: 100%; min-width: 100%;">
|
<div style="width: 100%; min-width: 100%;">
|
||||||
${files.files[i].name}:
|
${file.name}:
|
||||||
<br><div
|
<br><div
|
||||||
id="upload-progress-bar-${i + 1}"
|
id="upload-progress-bar-${i + 1}"
|
||||||
class="progress-bar progress-bar-striped progress-bar-animated"
|
class="progress-bar progress-bar-striped progress-bar-animated"
|
||||||
role="progressbar"
|
role="progressbar"
|
||||||
style="width: 100%; height: 10px;"
|
style="width: 100%; height: 10px;"
|
||||||
aria-valuenow="0"
|
aria-valuenow="0"
|
||||||
aria-valuemin="0"
|
aria-valuemin="0"
|
||||||
aria-valuemax="100"
|
aria-valuemax="100"
|
||||||
></div>
|
></div>
|
||||||
</div><br>
|
</div><br>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
$('#upload-progress-bar-parent').append(progressHtml);
|
$('#upload-progress-bar-parent').append(progressHtml);
|
||||||
|
|
||||||
await uploadFile(files.files[i], path, (progress) => {
|
const uploadPromise = uploadFile(file, path, i, (progress) => {
|
||||||
$(`#upload-progress-bar-${i + 1}`).attr('aria-valuenow', progress)
|
$(`#upload-progress-bar-${i + 1}`).attr('aria-valuenow', progress)
|
||||||
$(`#upload-progress-bar-${i + 1}`).css('width', 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);
|
||||||
}
|
}
|
||||||
hideUploadBox();
|
|
||||||
//$('#upload_file').submit(); //.trigger('submit');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var fileList = document.getElementById("files");
|
|
||||||
fileList.addEventListener("change", function (e) {
|
|
||||||
var list = "";
|
|
||||||
let files = Array.from(this.files)
|
|
||||||
files.forEach(file => {
|
|
||||||
list += "<li class='col-xs-12 file-list'>" + file.name + "</li>"
|
|
||||||
})
|
|
||||||
|
|
||||||
document.getElementById("fileList").innerHTML = list;
|
|
||||||
}, false);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,7 +514,7 @@
|
|||||||
'labelZipFile', data['lang']) }}</label>
|
'labelZipFile', data['lang']) }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button type="button" class="btn btn-info upload-button" id="upload-button" onclick="sendFile()"
|
<button type="button" class="btn btn-info upload-button" id="upload-button" onclick="uploadFile()"
|
||||||
disabled>{{ translate('serverWizard',
|
disabled>{{ translate('serverWizard',
|
||||||
'uploadButton', data['lang']) }}</button>
|
'uploadButton', data['lang']) }}</button>
|
||||||
</div>
|
</div>
|
||||||
@ -852,6 +852,96 @@
|
|||||||
});
|
});
|
||||||
var upload = false;
|
var upload = false;
|
||||||
var file;
|
var file;
|
||||||
|
async function uploadFile() {
|
||||||
|
file = $("#file")[0].files[0]
|
||||||
|
const fileId = uuidv4();
|
||||||
|
const token = getCookie("_xsrf")
|
||||||
|
document.getElementById("upload_input").innerHTML = '<div class="progress" style="width: 100%;"><div id="upload-progress-bar" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%"> <i class="fa-solid fa-spinner"></i></div></div>'
|
||||||
|
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/import/upload/`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'X-XSRFToken': token,
|
||||||
|
'chunked': true,
|
||||||
|
'fileSize': file.size,
|
||||||
|
'type': "import",
|
||||||
|
'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/`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: chunk,
|
||||||
|
headers: {
|
||||||
|
'Content-Range': `bytes ${start}-${end - 1}/${file.size}`,
|
||||||
|
'Content-Length': chunk.size,
|
||||||
|
'fileSize': file.size,
|
||||||
|
'chunked': true,
|
||||||
|
'type': "import",
|
||||||
|
'total_chunks': totalChunks,
|
||||||
|
'filename': file.name,
|
||||||
|
'fileId': fileId,
|
||||||
|
'chunkId': i,
|
||||||
|
},
|
||||||
|
}).then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.status === "completed") {
|
||||||
|
$("#upload_input").html(`<div class="card-header header-sm d-flex justify-content-between align-items-center" style="width: 100%;"><input value="${file.name}" type="text" id="file-uploaded" disabled></input> 🔒</div>`);
|
||||||
|
document.getElementById("lower_half").style.visibility = "visible";
|
||||||
|
document.getElementById("lower_half").hidden = false;
|
||||||
|
} else if (data.status !== "partial") {
|
||||||
|
throw new Error(data.message);
|
||||||
|
}
|
||||||
|
// Update progress bar
|
||||||
|
const progress = (i + 1) / totalChunks * 100;
|
||||||
|
updateProgressBar(Math.round(progress));
|
||||||
|
});
|
||||||
|
|
||||||
|
uploadPromises.push(uploadPromise);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Promise.all(uploadPromises);
|
||||||
|
} catch (error) {
|
||||||
|
alert("Error uploading file: " + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateProgressBar(progress) {
|
||||||
|
$(`#upload-progress-bar`).css('width', progress + '%');
|
||||||
|
$(`#upload-progress-bar`).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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function sendFile() {
|
function sendFile() {
|
||||||
file = $("#file")[0].files[0]
|
file = $("#file")[0].files[0]
|
||||||
document.getElementById("upload_input").innerHTML = '<div class="progress" style="width: 100%;"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%"> <i class="fa-solid fa-spinner"></i></div></div>'
|
document.getElementById("upload_input").innerHTML = '<div class="progress" style="width: 100%;"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%"> <i class="fa-solid fa-spinner"></i></div></div>'
|
||||||
|
Loading…
Reference in New Issue
Block a user