Merge pull request #424 from bbassie/slave-instance-ui-updates

Slave instance UI updates
This commit is contained in:
Arvids Godjuks authored and GitHub committed 2021-11-19 21:48:52 +02:00
commit 3b8e89ea00
11 files changed
+107 -12

No files matched your search

+4
View File
@@ -337,6 +337,7 @@ let slaveProperties = {
"name": { type: "string" },
"id": { type: "integer" },
"connected": { type: "boolean" },
"public_address": { type: ["null", "string"] },
};
messages.listSlaves = new Request({
@@ -372,6 +373,7 @@ let instanceProperties = {
"name": { type: "string" },
"id": { type: "integer" },
"assigned_slave": { type: ["null", "integer"] },
"game_port": { type: ["null", "integer"] },
"status": { enum: [
"unknown", "unassigned", "stopped", "starting", "running", "stopping",
"creating_save", "exporting_data", "deleted",
@@ -1145,11 +1147,13 @@ messages.instanceInitialized = new Event({
messages.instanceStatusChanged = new Event({
type: "instance_status_changed",
links: ["instance-slave", "slave-master"],
eventRequired: ["instance_id", "status"],
eventProperties: {
"instance_id": { type: "integer" },
"status": { enum: [
"stopped", "starting", "running", "stopping", "creating_save", "exporting_data",
]},
"game_port": { type: ["null", "integer"] },
},
});
+1
View File
@@ -178,6 +178,7 @@ const clientHandshake = ajv.compile({
"version": { type: "string" },
"name": { type: "string" },
"id": { type: "integer" },
"public_address": { type: "string" },
"plugins": {
type: "object",
additionalProperties: { type: "string" },
+4
View File
@@ -106,6 +106,7 @@ class ControlConnection extends BaseConnection {
version: slave.version,
id: slave.id,
name: slave.name,
public_address: slave.public_address || null,
connected: this._master.wsServer.slaveConnections.has(slave.id),
});
}
@@ -169,6 +170,7 @@ class ControlConnection extends BaseConnection {
id,
name: instance.config.get("instance.name"),
assigned_slave: instance.config.get("instance.assigned_slave"),
game_port: instance.game_port || null,
status: instance.status,
};
}
@@ -180,6 +182,7 @@ class ControlConnection extends BaseConnection {
id: instance.config.get("instance.id"),
name: instance.config.get("instance.name"),
assigned_slave: instance.config.get("instance.assigned_slave"),
game_port: instance.game_port || null,
status: instance.status,
});
}
@@ -199,6 +202,7 @@ class ControlConnection extends BaseConnection {
id: instance.config.get("instance.id"),
name: instance.config.get("instance.name"),
assigned_slave: instance.config.get("instance.assigned_slave"),
game_port: instance.game_port || null,
status: instance.status,
});
}
+1
View File
@@ -368,6 +368,7 @@ class Master {
version: slave.version,
id: slave.id,
name: slave.name,
public_address: slave.public_address || null,
connected: this.wsServer.slaveConnections.has(slave.id),
};
+2
View File
@@ -28,6 +28,7 @@ class SlaveConnection extends BaseConnection {
id: this._id,
name: this._name,
version: this._version,
public_address: registerData.public_address,
plugins: registerData.plugins,
});
@@ -87,6 +88,7 @@ class SlaveConnection extends BaseConnection {
let prev = instance.status;
instance.status = message.data.status;
instance.game_port = message.data.game_port;
logger.verbose(`Instance ${instance.config.get("instance.name")} State: ${instance.status}`);
this._master.instanceUpdated(instance);
await libPlugin.invokeHook(this._master.plugins, "onInstanceStatusChanged", instance, prev);
+4 -1
View File
@@ -260,7 +260,9 @@ class Instance extends libLink.Link {
notifyStatus(status) {
this._status = status;
libLink.messages.instanceStatusChanged.send(this, {
instance_id: this.id, status,
instance_id: this.id,
status,
game_port: this.server?.gamePort || this.config.get("factorio.game_port") || null,
});
}
@@ -1004,6 +1006,7 @@ class SlaveConnector extends libLink.WebSocketClientConnector {
version,
id: this.slaveConfig.get("slave.id"),
name: this.slaveConfig.get("slave.name"),
public_address: this.slaveConfig.get("slave.public_address"),
plugins,
});
}
@@ -1,12 +1,13 @@
import React from "react";
import { useHistory } from "react-router-dom";
import { Table } from "antd";
import { message, Button, Table } from "antd";
import CopyOutlined from "@ant-design/icons/CopyOutlined";
import { useSlaveList } from "../model/slave";
import InstanceStatusTag from "./InstanceStatusTag";
const strcmp = new Intl.Collator(undefined, { numerice: "true", sensitivity: "base" }).compare;
export default function InstanceList(props) {
let history = useHistory();
let [slaveList] = useSlaveList();
@@ -19,6 +20,17 @@ export default function InstanceList(props) {
return String(slaveId);
}
function instancePublicAddress(instance) {
let slave = slaveList.find(s => s.id === instance.assigned_slave);
if (slave && slave.public_address) {
if (instance.game_port) {
return `${slave.public_address}:${instance.game_port}`;
}
return slave.public_address;
}
return null;
}
let columns = [
{
title: "Name",
@@ -32,9 +44,30 @@ export default function InstanceList(props) {
render: instance => slaveName(instance["assigned_slave"]),
sorter: (a, b) => strcmp(slaveName(a["assigned_slave"]), slaveName(b["assigned_slave"])),
},
{
title: "Public address",
key: "public_address",
render: instance => {
let public_address = instancePublicAddress(instance);
return public_address ? <>
{public_address}
<Button
type="text"
icon={<CopyOutlined/>}
onClick={(e) => {
e.stopPropagation();
navigator.clipboard.writeText(public_address);
message.success("Copied public address!");
}}
/>
</> : "";
},
sorter: (a, b) => strcmp(slavePublicAddress(a["assigned_slave"]), slavePublicAddress(b["assigned_slave"])),
},
{
title: "Status",
dataIndex: "status",
render: status => <InstanceStatusTag status={status} />,
sorter: (a, b) => strcmp(a["status"], b["status"]),
},
];
@@ -0,0 +1,21 @@
import { Tag } from "antd";
import React from "react";
const statusColors = {
unknown: "#8c8c8c",
unassigned: "#eb2f96",
stopped: "#cf1322",
starting: "#ad8b00",
running: "#389e0d",
stopping: "#d48806",
creating_save: "#096dd9",
exporting_data: "#08979c",
deleted: "#262626",
};
export default function InstanceStatusTag(props) {
return <Tag color={statusColors[props.status]}>
{props.status.replace("_", " ")}
</Tag>;
}
@@ -1,6 +1,6 @@
import React, { useContext, useState } from "react";
import { useHistory, useParams } from "react-router-dom";
import { Button, Descriptions, Popconfirm, Space, Spin, Typography } from "antd";
import { Alert, Button, Descriptions, Popconfirm, Space, Spin, Typography } from "antd";
import DeleteOutlined from "@ant-design/icons/DeleteOutlined";
import { libLink } from "@clusterio/lib";
@@ -18,6 +18,7 @@ import SavesList from "./SavesList";
import { notifyErrorHandler } from "../util/notify";
import { useInstance } from "../model/instance";
import { useSlave } from "../model/slave";
import InstanceStatusTag from "./InstanceStatusTag";
const { Title, Paragraph } = Typography;
@@ -40,10 +41,22 @@ export default function InstanceViewPage(props) {
return <PageLayout nav={nav}><Spin size="large" /></PageLayout>;
}
if (instance.missing) {
if (instance.missing || instance["status"] === "deleted") {
return <PageLayout nav={nav}>
<h2>Instance not found</h2>
<p>Instance with id {instanceId} was not found on the master server.</p>
<Alert
message={instance["status"] === "deleted" ? "Instance has been deleted" : "Instance not found" }
showIcon
description={<>Instance with id {instanceId} was not found on the master server.</>}
type="warning"
action={
<Button
type="text"
onClick={() => { history.push("/instances"); }}
>
Go back to instances list
</Button>
}
/>
</PageLayout>;
}
@@ -116,7 +129,7 @@ export default function InstanceViewPage(props) {
buttonContent={assigned ? "Reassign" : "Assign"}
/>}
</Descriptions.Item>
<Descriptions.Item label="Status">{instance["status"]}</Descriptions.Item>
<Descriptions.Item label="Status"><InstanceStatusTag status={instance["status"]} /></Descriptions.Item>
</Descriptions>
{
@@ -1,5 +1,5 @@
import React, { useContext, useState } from "react";
import { Descriptions, PageHeader, Spin, Typography } from "antd";
import { Descriptions, PageHeader, Spin, Tag, Typography } from "antd";
import { useParams } from "react-router-dom";
import InstanceList from "./InstanceList";
@@ -44,7 +44,11 @@ export default function SlaveViewPage(props) {
<Descriptions bordered size="small" column={{ xs: 1, sm: 2, xl: 4 }}>
<Descriptions.Item label="Name">{slave["name"]}</Descriptions.Item>
<Descriptions.Item label="Connected">{slave["connected"] ? "yes" : "no"}</Descriptions.Item>
<Descriptions.Item label="Connected">
<Tag color={slave["connected"] ? "#389e0d" : "#cf1322"}>
{slave["connected"] ? "Connected" : "Disconnected"}
</Tag>
</Descriptions.Item>
<Descriptions.Item label="Agent">{slave["agent"]}</Descriptions.Item>
<Descriptions.Item label="Version">{slave["version"]}</Descriptions.Item>
</Descriptions>
+11 -2
View File
@@ -1,6 +1,6 @@
import React, { useContext, useRef, useState } from "react";
import { useHistory } from "react-router-dom";
import { Button, Form, Input, Modal, PageHeader, Table } from "antd";
import { Button, Form, Input, Modal, PageHeader, Table, Tag } from "antd";
import { libLink } from "@clusterio/lib";
@@ -104,10 +104,19 @@ export default function SlavesPage() {
dataIndex: "version",
sorter: (a, b) => strcmp(a["version"], b["version"]),
},
{
title: "Public address",
dataIndex: "public_address",
sorter: (a, b) => strcmp(a["public_address"], b["public_address"]),
},
{
title: "Connected",
key: "connected",
render: slave => slave["connected"] && "Yes",
render: slave => <Tag
color={slave["connected"] ? "#389e0d" : "#cf1322"}
>
{slave["connected"] ? "Connected" : "Disconnected"}
</Tag>,
sorter: (a, b) => a["connected"] - b["connected"],
},
]}