diff --git a/packages/master/src/routes.js b/packages/master/src/routes.js
index 0843f79a..b6d7f741 100644
--- a/packages/master/src/routes.js
+++ b/packages/master/src/routes.js
@@ -259,6 +259,11 @@ async function getStream(req, res) {
}
+const zipMimes = [
+ "application/zip",
+ "application/x-zip-compressed",
+];
+
async function uploadSave(req, res) {
try {
res.locals.user.checkPermission("core.instance.save.upload");
@@ -319,7 +324,7 @@ async function uploadSave(req, res) {
requestErrors.push("instance_id must come before files uploaded");
}
- if (fileMime !== "application/zip") {
+ if (!zipMimes.includes(fileMime)) {
requestErrors.push("invalid file Content-Type");
}
@@ -350,7 +355,7 @@ async function uploadSave(req, res) {
req.pipe(busboy);
});
- } else if (mimeType === "application/zip") {
+ } else if (zipMimes.includes(mimeType)) {
let filename = req.query.filename;
if (typeof filename !== "string") {
requestErrors.push("Missing or invalid filename parameter");
diff --git a/packages/web_ui/src/components/SavesList.jsx b/packages/web_ui/src/components/SavesList.jsx
index 448577c1..bd5eb6a4 100644
--- a/packages/web_ui/src/components/SavesList.jsx
+++ b/packages/web_ui/src/components/SavesList.jsx
@@ -1,5 +1,5 @@
import React, { useContext, useState } from "react";
-import { Button, Popconfirm, Space, Table, Tooltip, Upload } from "antd";
+import { Button, List, Popconfirm, Progress, Space, Table, Tooltip, Upload } from "antd";
import CaretLeftOutlined from "@ant-design/icons/CaretLeftOutlined";
import LeftOutlined from "@ant-design/icons/LeftOutlined";
@@ -27,6 +27,7 @@ export default function SavesList(props) {
let control = useContext(ControlContext);
let saves = useSaves(props.instance.id);
let [starting, setStarting] = useState(false);
+ let [uploadingFiles, setUploadingFiles] = useState([]);
const saveTable =
;
+ function onChange(changeEvent) {
+ if (["done", "error"].includes(changeEvent.file.status)) {
+ if (changeEvent.file.status === "error") {
+ notifyErrorHandler("Error uploading file")(changeEvent.file.error);
+ }
+
+ let newUploadingFiles = [...uploadingFiles];
+ let index = newUploadingFiles.findIndex(f => f.uid === changeEvent.file.uid);
+ if (index !== -1) {
+ newUploadingFiles.splice(index, 1);
+ setUploadingFiles(newUploadingFiles);
+ }
+ return;
+ }
+
+ let file = {
+ uid: changeEvent.file.uid,
+ name: changeEvent.file.name,
+ percent: changeEvent.file.percent,
+ status: changeEvent.file.status,
+ };
+
+ let newUploadingFiles = [...uploadingFiles];
+ let index = newUploadingFiles.findIndex(f => f.uid === changeEvent.file.uid);
+ if (index !== -1) {
+ newUploadingFiles[index] = file;
+ } else {
+ newUploadingFiles.push(file);
+ }
+ setUploadingFiles(newUploadingFiles);
+ }
+
let uploadProps = {
name: "file",
accept: ".zip",
@@ -118,7 +151,7 @@ export default function SavesList(props) {
},
showUploadList: false,
action: `${webRoot}api/upload-save`,
- // TODO: show progress to user.
+ onChange,
};
return
@@ -131,5 +164,15 @@ export default function SavesList(props) {
{saveTable}
+ {uploadingFiles.length ?
+ {uploadingFiles.map(file =>
+ {file.name}
+ )}
+
: null}
;
}
diff --git a/test/integration/clusterio.js b/test/integration/clusterio.js
index 5f3d6441..fff1c043 100644
--- a/test/integration/clusterio.js
+++ b/test/integration/clusterio.js
@@ -65,6 +65,12 @@ describe("Integration of Clusterio", function() {
describe("clusterioslave", function() {
describe("slaveUpdateEventHandler()", function() {
it("should trigger when a new slave is added", async function() {
+ // On windows there's currently no way to automate graceful shutdown of the slave
+ // process as CTRL+C is some weird terminal thing and SIGINT isn't a thing.
+ if (process.platform === "win32") {
+ this.skip();
+ }
+
slowTest(this);
getControl().slaveUpdates = [];
let config = "alt-slave-config.json";
diff --git a/test/master/routes.js b/test/master/routes.js
index 7037be75..cca2c3b7 100644
--- a/test/master/routes.js
+++ b/test/master/routes.js
@@ -325,5 +325,25 @@ describe("master/src/routes", function() {
assert.deepEqual(JSON.parse(response.body), { saves: ["test.zip"] });
assert.equal(response.statusCode, 200);
});
+ it("should complete a valid transfer with non-standerd mime type", async function() {
+ master.forwardRequestToInstance = async (request, data) => {
+ let stream = await phin({
+ url: `http://localhost:${port}/api/stream/${data.stream_id}`,
+ });
+ assert.equal(stream.body.toString(), "totally a zip file");
+ return { save: data.filename };
+ };
+ let response;
+ response = await phin({
+ url: `http://localhost:${port}/api/upload-save?instance_id=123&filename=test.zip`, method: "POST",
+ headers: {
+ "X-Access-Token": testToken,
+ "Content-Type": "application/x-zip-compressed",
+ },
+ data: "totally a zip file",
+ });
+ assert.deepEqual(JSON.parse(response.body), { saves: ["test.zip"] });
+ assert.equal(response.statusCode, 200);
+ });
});
});