diff --git a/CHANGELOG.md b/CHANGELOG.md
index 24d7f5fe..d2cbea30 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,27 @@
# Changelog
-## --- [4.4.1] - 2024/08/06
+## --- [4.4.4] - 2024/TBD
+### New features
+TBD
+### Bug fixes
+TBD
+### Tweaks
+TBD
+### Lang
+TBD
+
+## --- [4.4.3] - 2024/08/08
+### Bug fixes
+- Fix schedules creation fail due to missing action ID ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/791))
+
+
+## --- [4.4.2] - 2024/08/07
+### Bug fixes
+- Migrations | Fix exception message on file not found for backups migration ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/789))
+- UploadAPI | Upload chunks in batches to avoid overloading browser cache ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/788))
+
+
+## --- [4.4.1] - 2024/08/06
### Patch Fixes
- Migrations | Fix orphan backup configurations crashing migration operation ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/785))
- Migrations | Fix missing default configuration if no server backup config exists during the migration ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/785))
diff --git a/README.md b/README.md
index 2b382faf..c4b7ad56 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
[![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com)
-# Crafty Controller 4.4.1
+# Crafty Controller 4.4.4
> Python based Control Panel for your Minecraft Server
## What is Crafty Controller?
diff --git a/app/classes/shared/tasks.py b/app/classes/shared/tasks.py
index da9735a9..ebd8aadf 100644
--- a/app/classes/shared/tasks.py
+++ b/app/classes/shared/tasks.py
@@ -341,7 +341,7 @@ class TasksManager:
job_data["cron_string"],
job_data["parent"],
job_data["delay"],
- job_data["action_id"],
+ job_data.get("action_id", None),
)
# Checks to make sure some doofus didn't actually make the newly
@@ -372,7 +372,7 @@ class TasksManager:
"system"
),
"command": job_data["command"],
- "action_id": job_data["action_id"],
+ "action_id": job_data.get("action_id", None),
}
],
)
@@ -399,7 +399,7 @@ class TasksManager:
"system"
),
"command": job_data["command"],
- "action_id": job_data["action_id"],
+ "action_id": job_data.get("action_id", None),
}
],
)
@@ -416,7 +416,7 @@ class TasksManager:
"system"
),
"command": job_data["command"],
- "action_id": job_data["action_id"],
+ "action_id": job_data.get("action_id", None),
}
],
)
@@ -436,7 +436,7 @@ class TasksManager:
"system"
),
"command": job_data["command"],
- "action_id": job_data["action_id"],
+ "action_id": job_data.get("action_id", None),
}
],
)
@@ -529,7 +529,7 @@ class TasksManager:
"system"
),
"command": job_data["command"],
- "action_id": job_data["action_id"],
+ "action_id": job_data.get("action_id", None),
}
],
)
@@ -553,7 +553,7 @@ class TasksManager:
"system"
),
"command": job_data["command"],
- "action_id": job_data["action_id"],
+ "action_id": job_data.get("action_id", None),
}
],
)
@@ -570,7 +570,7 @@ class TasksManager:
"system"
),
"command": job_data["command"],
- "action_id": job_data["action_id"],
+ "action_id": job_data.get("action_id", None),
}
],
)
@@ -590,7 +590,7 @@ class TasksManager:
"system"
),
"command": job_data["command"],
- "action_id": job_data["action_id"],
+ "action_id": job_data.get("action_id", None),
}
],
)
diff --git a/app/config/version.json b/app/config/version.json
index c8db4444..4ae818ef 100644
--- a/app/config/version.json
+++ b/app/config/version.json
@@ -1,5 +1,5 @@
{
"major": 4,
"minor": 4,
- "sub": 1
+ "sub": 4
}
diff --git a/app/frontend/static/assets/js/shared/upload.js b/app/frontend/static/assets/js/shared/upload.js
index d31d4e23..d926afa4 100644
--- a/app/frontend/static/assets/js/shared/upload.js
+++ b/app/frontend/static/assets/js/shared/upload.js
@@ -1,33 +1,74 @@
+function delay(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+async function uploadChunk(file, url, chunk, start, end, chunk_hash, totalChunks, type, path, fileId, i, file_num, updateProgressBar) {
+ return fetch(url, {
+ method: 'POST',
+ body: chunk,
+ headers: {
+ 'Content-Range': `bytes ${start}-${end - 1}/${file.size}`,
+ 'Content-Length': chunk.size,
+ 'fileSize': file.size,
+ 'chunkHash': chunk_hash,
+ 'chunked': true,
+ 'type': type,
+ 'totalChunks': 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');
+ }
+ 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);
+ });
+}
+
async function uploadFile(type, file = null, path = null, file_num = 0, _onProgress = null) {
if (file == null) {
try {
file = $("#file")[0].files[0];
} catch {
- bootbox.alert("Please select a file first.")
+ bootbox.alert("Please select a file first.");
return;
}
-
}
+
const fileId = uuidv4();
const token = getCookie("_xsrf");
if (type !== "server_upload") {
document.getElementById("upload_input").innerHTML = '