diff --git a/CHANGELOG.md b/CHANGELOG.md
index eba715c2..2991fa6f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@
- Packaged and uploaded to npm again, greatly simplifying distribution and installation of both Clusterio and plugins.
- Added centralized logging for the cluster.
All logs from both Clusterio and Factorio is stored in a shared log which can be inspected and queried.
+- Added remote save management with the ability to list, create, upload, download, delete and load saves for instances.
### Features
diff --git a/packages/ctl/ctl.js b/packages/ctl/ctl.js
index 6ddc1d09..7d15d6e5 100755
--- a/packages/ctl/ctl.js
+++ b/packages/ctl/ctl.js
@@ -137,10 +137,11 @@ slaveCommands.add(new libCommand.Command({
slaveCommands.add(new libCommand.Command({
definition: ["generate-token", "Generate token for a slave", (yargs) => {
- yargs.option("id", { type: "number", nargs: 1, describe: "Slave id", demandOption: true });
+ yargs.option("id", { type: "number", nargs: 1, describe: "Slave id" });
}],
handler: async function(args, control) {
- let response = await libLink.messages.generateSlaveToken.send(control, { slave_id: args.id });
+ let slaveId = typeof args.id === "number" ? args.id : null;
+ let response = await libLink.messages.generateSlaveToken.send(control, { slave_id: slaveId });
print(response.token);
},
}));
diff --git a/packages/lib/link/messages.js b/packages/lib/link/messages.js
index 6ba0a8a4..dbb84455 100644
--- a/packages/lib/link/messages.js
+++ b/packages/lib/link/messages.js
@@ -422,7 +422,7 @@ messages.generateSlaveToken = new Request({
links: ["control-master"],
permission: "core.slave.generate_token",
requestProperties: {
- "slave_id": { type: "integer" },
+ "slave_id": { type: ["integer", "null"] },
},
responseProperties: {
"token": { type: "string" },
diff --git a/packages/master/src/ControlConnection.js b/packages/master/src/ControlConnection.js
index ddf42e83..2792774b 100644
--- a/packages/master/src/ControlConnection.js
+++ b/packages/master/src/ControlConnection.js
@@ -130,7 +130,11 @@ class ControlConnection extends BaseConnection {
}
async generateSlaveTokenRequestHandler(message) {
- return { token: this.generateSlaveToken(message.data.slave_id) };
+ let slaveId = message.data.slave_id;
+ if (slaveId === null) {
+ slaveId = Math.random() * 2**31 | 0;
+ }
+ return { token: this.generateSlaveToken(slaveId) };
}
async createSlaveConfigRequestHandler(message) {
diff --git a/packages/web_ui/src/components/InstancesPage.jsx b/packages/web_ui/src/components/InstancesPage.jsx
index bab926ba..71fb90a4 100644
--- a/packages/web_ui/src/components/InstancesPage.jsx
+++ b/packages/web_ui/src/components/InstancesPage.jsx
@@ -21,7 +21,8 @@ function CreateInstanceButton(props) {
async function createInstance() {
let values = form.getFieldsValue();
if (!values.instanceName) {
- form.setFields([{ name: "mapGenSettings", errors: ["Name is required"] }]);
+ form.setFields([{ name: "instanceName", errors: ["Name is required"] }]);
+ return;
}
let instanceConfig = new libConfig.InstanceConfig("control");
diff --git a/packages/web_ui/src/components/SlavesPage.jsx b/packages/web_ui/src/components/SlavesPage.jsx
index 0283c8bb..74af789a 100644
--- a/packages/web_ui/src/components/SlavesPage.jsx
+++ b/packages/web_ui/src/components/SlavesPage.jsx
@@ -1,11 +1,73 @@
-import React, { useContext } from "react";
-import { Table } from "antd";
+import React, { useContext, useRef, useState } from "react";
+import { Button, Form, Input, Modal, PageHeader, Table } from "antd";
import libLink from "@clusterio/lib/link";
import ControlContext from "./ControlContext";
import PageLayout from "./PageLayout";
import { useSlaveList } from "../model/slave";
+import { notifyErrorHandler } from "../util/notify";
+
+
+function GenerateSlaveTokenButton(props) {
+ let control = useContext(ControlContext);
+ let [visible, setVisible] = useState(false);
+ let [token, setToken] = useState(null);
+ let [form] = Form.useForm();
+ let tokenTextAreaRef = useRef(null);
+
+ async function generateToken() {
+ let values = form.getFieldsValue();
+ let slaveId = null;
+ if (values.slaveId) {
+ slaveId = Number.parseInt(values.slaveId, 10);
+ if (Number.isNaN(slaveId)) {
+ form.setFields([{ name: "slaveId", errors: ["Must be an integer"] }]);
+ return;
+ }
+ form.setFields([{ name: "slaveId", errors: [] }]);
+ }
+
+ let result = await libLink.messages.generateSlaveToken.send(control, { slave_id: slaveId });
+ setToken(result.token);
+ }
+
+ return <>
+
+ {
+ setVisible(false);
+ setToken(null);
+ form.resetFields();
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+ >;
+}
export default function SlavesPage() {
@@ -13,7 +75,11 @@ export default function SlavesPage() {
let [slaveList] = useSlaveList();
return
- Slaves
+
+ />