querysub 0.252.0 → 0.253.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/3-path-functions/PathFunctionRunner.ts +1 -0
- package/src/4-deploy/git.ts +34 -1
- package/src/deployManager/MachinesPage.tsx +7 -10
- package/src/deployManager/components/MachineDetailPage.tsx +13 -21
- package/src/deployManager/components/MachinesListPage.tsx +59 -48
- package/src/deployManager/components/ServiceDetailPage.tsx +7 -9
- package/src/deployManager/components/ServicesListPage.tsx +41 -31
- package/src/deployManager/components/deployButtons.tsx +212 -0
- package/src/deployManager/machineApplyMainCode.ts +10 -4
- package/src/deployManager/machineController.ts +31 -4
- package/src/deployManager/machineSchema.ts +134 -45
- package/src/deployManager/spec.txt +3 -62
- package/src/deployManager/urlParams.ts +0 -1
- package/src/diagnostics/errorLogs/ErrorLogController.ts +1 -0
- package/src/diagnostics/errorLogs/LogClassifiers.tsx +6 -0
- package/src/library-components/SyncedController.ts +91 -31
- package/src/misc/formatJSX.tsx +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "querysub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.253.0",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"note1": "note on node-forge fork, see https://github.com/digitalbazaar/forge/issues/744 for details",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"js-sha512": "^0.9.0",
|
|
23
23
|
"node-forge": "https://github.com/sliftist/forge#e618181b469b07bdc70b968b0391beb8ef5fecd6",
|
|
24
24
|
"pako": "^2.1.0",
|
|
25
|
-
"socket-function": "^0.
|
|
25
|
+
"socket-function": "^0.134.0",
|
|
26
26
|
"terser": "^5.31.0",
|
|
27
27
|
"typesafecss": "^0.22.0",
|
|
28
28
|
"yaml": "^2.5.0",
|
|
@@ -354,6 +354,7 @@ export class PathFunctionRunner {
|
|
|
354
354
|
let runCallPromise = self.runCall(call, functionSpec);
|
|
355
355
|
void runCallPromise.finally(() => {
|
|
356
356
|
outstandingCalls--;
|
|
357
|
+
// IMPORTANT! We remove the call, NOT for GC, but! Because if the call writes are rejected (due to contention), then the Result will also be rejected, changing it back to undefined, so (as long as little enough time has passed), we will run the call again! Or rather, we will try to, as long as runningCalls doesn't have the value!
|
|
357
358
|
runningCalls.delete(call.CallId);
|
|
358
359
|
});
|
|
359
360
|
logErrors(runCallPromise);
|
package/src/4-deploy/git.ts
CHANGED
|
@@ -11,6 +11,40 @@ export async function getGitRefLive(gitDir = ".") {
|
|
|
11
11
|
return (await runPromise(`git rev-parse HEAD`, { cwd: gitDir })).trim();
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
export async function getGitUncommitted(gitDir = "."): Promise<string[]> {
|
|
15
|
+
return (await runPromise(`git status --porcelain`, { cwd: gitDir })).split("\n").map(x => x.trim()).filter(x => x);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function getLatestRefOnUpstreamBranch(gitDir = ".") {
|
|
19
|
+
await runPromise(`git fetch`, { cwd: gitDir });
|
|
20
|
+
return (await runPromise(`git rev-parse @{upstream}`, { cwd: gitDir })).trim();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function commitAndPush(config: {
|
|
24
|
+
gitDir: string;
|
|
25
|
+
message: string;
|
|
26
|
+
}) {
|
|
27
|
+
await runPromise(`git add --all`, { cwd: config.gitDir });
|
|
28
|
+
await runPromise(`git commit -m "${config.message}"`, { cwd: config.gitDir });
|
|
29
|
+
await runPromise(`git push`, { cwd: config.gitDir });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function getGitRefInfo(config: {
|
|
33
|
+
gitDir: string;
|
|
34
|
+
ref: string;
|
|
35
|
+
}): Promise<{
|
|
36
|
+
time: number;
|
|
37
|
+
description: string;
|
|
38
|
+
}> {
|
|
39
|
+
const timeOutput = await runPromise(`git show --format="%ct" -s ${config.ref}`, { cwd: config.gitDir });
|
|
40
|
+
const descriptionOutput = await runPromise(`git show --format="%s" -s ${config.ref}`, { cwd: config.gitDir });
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
time: parseInt(timeOutput.trim(), 10) * 1000,
|
|
44
|
+
description: descriptionOutput.trim()
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
14
48
|
export const getGitURLSync = cache(function getGitURLSync(gitDir?: string) {
|
|
15
49
|
return child_process.execSync(`git remote get-url origin`, { cwd: gitDir || "." }).toString().trim();
|
|
16
50
|
});
|
|
@@ -20,7 +54,6 @@ export const getGitRefSync = cache(function getGitRefSync(gitDir?: string) {
|
|
|
20
54
|
|
|
21
55
|
export const setGitRef = measureWrap(async function setGitRef(config: {
|
|
22
56
|
gitFolder: string;
|
|
23
|
-
repoUrl: string;
|
|
24
57
|
gitRef: string;
|
|
25
58
|
}) {
|
|
26
59
|
await fs.promises.mkdir(config.gitFolder, { recursive: true });
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { SocketFunction } from "socket-function/SocketFunction";
|
|
2
2
|
import { qreact } from "../4-dom/qreact";
|
|
3
|
-
import {
|
|
3
|
+
import { MachineServiceController } from "./machineSchema";
|
|
4
4
|
import { css } from "typesafecss";
|
|
5
5
|
import { currentViewParam, selectedServiceIdParam, selectedMachineIdParam } from "./urlParams";
|
|
6
6
|
import { ServicesListPage } from "./components/ServicesListPage";
|
|
7
7
|
import { MachinesListPage } from "./components/MachinesListPage";
|
|
8
8
|
import { ServiceDetailPage } from "./components/ServiceDetailPage";
|
|
9
9
|
import { MachineDetailPage } from "./components/MachineDetailPage";
|
|
10
|
+
import { Anchor } from "../library-components/ATag";
|
|
10
11
|
|
|
11
12
|
export class MachinesPage extends qreact.Component {
|
|
12
13
|
private renderTabs() {
|
|
@@ -19,24 +20,20 @@ export class MachinesPage extends qreact.Component {
|
|
|
19
20
|
{ key: "services", label: "Services", otherKeys: ["service-detail"] },
|
|
20
21
|
].map(tab => {
|
|
21
22
|
let isActive = currentViewParam.value === tab.key || tab.otherKeys.includes(currentViewParam.value);
|
|
22
|
-
return <
|
|
23
|
+
return <Anchor noStyles key={tab.key}
|
|
24
|
+
values={[currentViewParam.getOverride(tab.key as any), selectedServiceIdParam.getOverride(""), selectedMachineIdParam.getOverride("")]}
|
|
23
25
|
className={css.pad2(12, 8).button
|
|
24
26
|
+ (isActive && css.hsl(0, 0, 100).bord2(210, 50, 50))
|
|
25
27
|
+ (!isActive && css.hsl(0, 0, 70).colorhsl(0, 0, 50).bord2(0, 0, 50))
|
|
26
|
-
}
|
|
27
|
-
onClick={() => {
|
|
28
|
-
currentViewParam.value = tab.key as any;
|
|
29
|
-
selectedServiceIdParam.value = "";
|
|
30
|
-
selectedMachineIdParam.value = "";
|
|
31
|
-
}}>
|
|
28
|
+
}>
|
|
32
29
|
{tab.label}
|
|
33
|
-
</
|
|
30
|
+
</Anchor>;
|
|
34
31
|
})}
|
|
35
32
|
</div>;
|
|
36
33
|
}
|
|
37
34
|
|
|
38
35
|
render() {
|
|
39
|
-
let controller =
|
|
36
|
+
let controller = MachineServiceController(SocketFunction.browserNodeId());
|
|
40
37
|
let serviceConfigType = controller.getServiceConfigType();
|
|
41
38
|
if (!serviceConfigType) return <div>Loading...</div>;
|
|
42
39
|
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { SocketFunction } from "socket-function/SocketFunction";
|
|
2
2
|
import { qreact } from "../../4-dom/qreact";
|
|
3
|
-
import { MACHINE_RESYNC_INTERVAL,
|
|
3
|
+
import { MACHINE_RESYNC_INTERVAL, MachineServiceController, ServiceConfig } from "../machineSchema";
|
|
4
4
|
import { css } from "typesafecss";
|
|
5
5
|
import { currentViewParam, selectedMachineIdParam, selectedServiceIdParam } from "../urlParams";
|
|
6
6
|
import { formatVeryNiceDateTime } from "socket-function/src/formatting/format";
|
|
7
7
|
import { sort } from "socket-function/src/misc";
|
|
8
|
+
import { Anchor } from "../../library-components/ATag";
|
|
8
9
|
|
|
9
10
|
export class MachineDetailPage extends qreact.Component {
|
|
10
11
|
render() {
|
|
11
12
|
const selectedMachineId = selectedMachineIdParam.value;
|
|
12
13
|
if (!selectedMachineId) return <div>No machine selected</div>;
|
|
13
14
|
|
|
14
|
-
let controller =
|
|
15
|
+
let controller = MachineServiceController(SocketFunction.browserNodeId());
|
|
15
16
|
let machineInfo = controller.getMachineInfo(selectedMachineId);
|
|
16
17
|
let serviceList = controller.getServiceList();
|
|
17
18
|
|
|
@@ -77,14 +78,10 @@ export class MachineDetailPage extends qreact.Component {
|
|
|
77
78
|
<h3>⚠️ Configured Services Not Running ({configuredButNotRunning.length})</h3>
|
|
78
79
|
<div className={css.vbox(8)}>
|
|
79
80
|
{configuredButNotRunning.map(([serviceId, serviceConfig]) => (
|
|
80
|
-
<
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
onClick={() => {
|
|
85
|
-
currentViewParam.value = "service-detail";
|
|
86
|
-
selectedServiceIdParam.value = serviceId;
|
|
87
|
-
}}>
|
|
81
|
+
<Anchor noStyles
|
|
82
|
+
key={serviceId}
|
|
83
|
+
values={[currentViewParam.getOverride("service-detail"), selectedServiceIdParam.getOverride(serviceId)]}
|
|
84
|
+
className={css.pad2(12).button.bord2(0, 0, 20).hsl(0, 70, 90)}>
|
|
88
85
|
<div className={css.hbox(12)}>
|
|
89
86
|
<div className={css.flexGrow(1)}>
|
|
90
87
|
{serviceConfig.info.title} ({serviceConfig.parameters.key} / {serviceId})
|
|
@@ -96,7 +93,7 @@ export class MachineDetailPage extends qreact.Component {
|
|
|
96
93
|
Deploy: {serviceConfig.parameters.deploy ? "Yes" : "No"}
|
|
97
94
|
</div>
|
|
98
95
|
</div>
|
|
99
|
-
</
|
|
96
|
+
</Anchor>
|
|
100
97
|
))}
|
|
101
98
|
</div>
|
|
102
99
|
</div>
|
|
@@ -118,15 +115,10 @@ export class MachineDetailPage extends qreact.Component {
|
|
|
118
115
|
}
|
|
119
116
|
|
|
120
117
|
return (
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
onClick={() => {
|
|
127
|
-
currentViewParam.value = "service-detail";
|
|
128
|
-
selectedServiceIdParam.value = serviceId;
|
|
129
|
-
}}>
|
|
118
|
+
<Anchor noStyles
|
|
119
|
+
key={serviceId}
|
|
120
|
+
values={[currentViewParam.getOverride("service-detail"), selectedServiceIdParam.getOverride(serviceId)]}
|
|
121
|
+
className={css.pad2(12).button.bord2(0, 0, 20) + backgroundColor}>
|
|
130
122
|
<div className={css.hbox(12)}>
|
|
131
123
|
<div className={css.flexGrow(1)}>
|
|
132
124
|
{serviceConfig && <>{serviceConfig.info.title} ({serviceConfig.parameters.key})</> || <>serviceId = {serviceId}</>}
|
|
@@ -148,7 +140,7 @@ export class MachineDetailPage extends qreact.Component {
|
|
|
148
140
|
</div>
|
|
149
141
|
)}
|
|
150
142
|
</div>
|
|
151
|
-
</
|
|
143
|
+
</Anchor>
|
|
152
144
|
);
|
|
153
145
|
})}
|
|
154
146
|
</div>
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { SocketFunction } from "socket-function/SocketFunction";
|
|
2
2
|
import { qreact } from "../../4-dom/qreact";
|
|
3
|
-
import { MACHINE_RESYNC_INTERVAL,
|
|
3
|
+
import { MACHINE_RESYNC_INTERVAL, MachineServiceController } from "../machineSchema";
|
|
4
4
|
import { css } from "typesafecss";
|
|
5
5
|
import { t } from "../../2-proxy/schema2";
|
|
6
6
|
import { Querysub } from "../../4-querysub/QuerysubController";
|
|
7
7
|
import { currentViewParam, selectedMachineIdParam } from "../urlParams";
|
|
8
8
|
import { formatVeryNiceDateTime } from "socket-function/src/formatting/format";
|
|
9
9
|
import { sort } from "socket-function/src/misc";
|
|
10
|
+
import { formatTimeJSX } from "../../misc/formatJSX";
|
|
11
|
+
import { DeployMachineButtons, RenderGitRefInfo } from "./deployButtons";
|
|
12
|
+
import { isDefined } from "../../misc";
|
|
10
13
|
|
|
11
14
|
export class MachinesListPage extends qreact.Component {
|
|
12
15
|
state = t.state({
|
|
@@ -16,7 +19,7 @@ export class MachinesListPage extends qreact.Component {
|
|
|
16
19
|
});
|
|
17
20
|
|
|
18
21
|
render() {
|
|
19
|
-
let controller =
|
|
22
|
+
let controller = MachineServiceController(SocketFunction.browserNodeId());
|
|
20
23
|
let machineList = controller.getMachineList();
|
|
21
24
|
|
|
22
25
|
if (!machineList) return <div>Loading machines...</div>;
|
|
@@ -52,6 +55,8 @@ export class MachinesListPage extends qreact.Component {
|
|
|
52
55
|
</button>
|
|
53
56
|
)}
|
|
54
57
|
|
|
58
|
+
<DeployMachineButtons machines={machines.map(x => x[1]).filter(isDefined)} />
|
|
59
|
+
|
|
55
60
|
{this.state.isDeleteMode && (
|
|
56
61
|
<div className={css.hbox(8)}>
|
|
57
62
|
<div className={css.pad2(8, 4)}>
|
|
@@ -116,58 +121,64 @@ export class MachinesListPage extends qreact.Component {
|
|
|
116
121
|
return acc + (service.totalTimesLaunched || 0);
|
|
117
122
|
}, 0);
|
|
118
123
|
|
|
119
|
-
return <div
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (this.state.
|
|
132
|
-
|
|
124
|
+
return <div className={css.hbox(10)}>
|
|
125
|
+
<div
|
|
126
|
+
className={
|
|
127
|
+
css.pad2(12).bord2(0, 0, 20).button
|
|
128
|
+
+ (
|
|
129
|
+
failingServices.length > 0 && css.hsl(0, 50, 60)
|
|
130
|
+
|| isMachineDead && css.hsl(0, 0, 50)
|
|
131
|
+
|| css.hsl(0, 0, 100)
|
|
132
|
+
)
|
|
133
|
+
+ (this.state.isDeleteMode && isSelected && css.bord2(200, 80, 60, 2))
|
|
134
|
+
}
|
|
135
|
+
onClick={() => {
|
|
136
|
+
if (this.state.isDeleteMode) {
|
|
137
|
+
if (this.state.selectedForDeletion[machineId]) {
|
|
138
|
+
delete this.state.selectedForDeletion[machineId];
|
|
139
|
+
} else {
|
|
140
|
+
this.state.selectedForDeletion[machineId] = true;
|
|
141
|
+
}
|
|
133
142
|
} else {
|
|
134
|
-
|
|
143
|
+
currentViewParam.value = "machine-detail";
|
|
144
|
+
selectedMachineIdParam.value = machineId;
|
|
135
145
|
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
</div>
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
Last heartbeat {formatVeryNiceDateTime(machineInfo.heartbeat)}
|
|
158
|
-
</div>
|
|
159
|
-
<div className={css.vbox(4).flexGrow(1)}>
|
|
160
|
-
<div>
|
|
161
|
-
{machineId}
|
|
162
|
-
</div>
|
|
163
|
-
<div>
|
|
164
|
-
{machineInfo.info["getExternalIP"]}
|
|
146
|
+
}}>
|
|
147
|
+
<div className={css.hbox(12)}>
|
|
148
|
+
{this.state.isDeleteMode && (
|
|
149
|
+
<div className={css.flexShrink0}>
|
|
150
|
+
<input
|
|
151
|
+
type="checkbox"
|
|
152
|
+
checked={isSelected}
|
|
153
|
+
onChange={() => { }} // Controlled by onClick above
|
|
154
|
+
onClick={(e) => e.stopPropagation()} // Prevent double-toggle
|
|
155
|
+
className={css.size(16, 16)}
|
|
156
|
+
/>
|
|
157
|
+
</div>
|
|
158
|
+
)}
|
|
159
|
+
{isMachineDead && <div className={css.colorhsl(0, 80, 50)}>
|
|
160
|
+
⚠️ Machine is likely dead
|
|
161
|
+
</div>}
|
|
162
|
+
<div className={css.vbox(4)}>
|
|
163
|
+
<div>
|
|
164
|
+
Last heartbeat {formatTimeJSX(machineInfo.heartbeat)}
|
|
165
|
+
</div>
|
|
166
|
+
<RenderGitRefInfo gitRef={machineInfo.gitRef} />
|
|
165
167
|
</div>
|
|
166
|
-
<div>
|
|
167
|
-
|
|
168
|
+
<div className={css.vbox(4).flexGrow(1)}>
|
|
169
|
+
<div>
|
|
170
|
+
{machineId}
|
|
171
|
+
</div>
|
|
172
|
+
<div>
|
|
173
|
+
{machineInfo.info["getExternalIP"]}
|
|
174
|
+
</div>
|
|
175
|
+
<div>
|
|
176
|
+
{serviceCount} services {failingServices.length > 0 ? `(${failingServices.length} failing)` : ""} • {totalLaunches} launches
|
|
177
|
+
</div>
|
|
168
178
|
</div>
|
|
169
179
|
</div>
|
|
170
180
|
</div>
|
|
181
|
+
<DeployMachineButtons machines={[machineInfo]} />
|
|
171
182
|
</div>;
|
|
172
183
|
})}
|
|
173
184
|
</div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SocketFunction } from "socket-function/SocketFunction";
|
|
2
2
|
import { qreact } from "../../4-dom/qreact";
|
|
3
|
-
import { MACHINE_RESYNC_INTERVAL,
|
|
3
|
+
import { MACHINE_RESYNC_INTERVAL, MachineServiceController, ServiceConfig } from "../machineSchema";
|
|
4
4
|
import { css } from "typesafecss";
|
|
5
5
|
import { t } from "../../2-proxy/schema2";
|
|
6
6
|
import { Querysub } from "../../4-querysub/QuerysubController";
|
|
@@ -13,6 +13,7 @@ import { Button } from "../../library-components/Button";
|
|
|
13
13
|
import { isDefined } from "../../misc";
|
|
14
14
|
import { watchScreenOutput, stopWatchingScreenOutput } from "../machineController";
|
|
15
15
|
import { getPathStr2 } from "../../path";
|
|
16
|
+
import { Anchor } from "../../library-components/ATag";
|
|
16
17
|
import { ScrollOnMount } from "../../library-components/ScrollOnMount";
|
|
17
18
|
import { StickyBottomScroll } from "../../library-components/StickyBottomScroll";
|
|
18
19
|
import { PrimitiveDisplay } from "../../diagnostics/logs/ObjectDisplay";
|
|
@@ -179,7 +180,7 @@ type ServiceConfig = ${serviceConfigType}
|
|
|
179
180
|
model.setValue(`const config: ServiceConfig = ${newConfigStr}`);
|
|
180
181
|
}
|
|
181
182
|
}
|
|
182
|
-
await
|
|
183
|
+
await MachineServiceController(SocketFunction.browserNodeId()).setServiceConfigs.promise([updatedConfig]);
|
|
183
184
|
} catch (error) {
|
|
184
185
|
Querysub.localCommit(() => {
|
|
185
186
|
this.state.saveError = error instanceof Error ? error.message : String(error);
|
|
@@ -198,7 +199,7 @@ type ServiceConfig = ${serviceConfigType}
|
|
|
198
199
|
const selectedServiceId = selectedServiceIdParam.value;
|
|
199
200
|
if (!selectedServiceId) return <div>No service selected</div>;
|
|
200
201
|
|
|
201
|
-
let controller =
|
|
202
|
+
let controller = MachineServiceController(SocketFunction.browserNodeId());
|
|
202
203
|
let originalConfig = controller.getServiceConfig(selectedServiceId);
|
|
203
204
|
let serviceConfigType = controller.getServiceConfigType();
|
|
204
205
|
|
|
@@ -371,15 +372,12 @@ type ServiceConfig = ${serviceConfigType}
|
|
|
371
372
|
</div>
|
|
372
373
|
)}
|
|
373
374
|
|
|
374
|
-
<
|
|
375
|
+
<Anchor noStyles
|
|
376
|
+
values={[currentViewParam.getOverride("machine-detail"), selectedMachineIdParam.getOverride(machineId)]}
|
|
375
377
|
className={css.button.pad2(16, 8).bord2(0, 0, 10) + css.hsl(200, 70, 90)}
|
|
376
|
-
onClick={() => {
|
|
377
|
-
currentViewParam.value = "machine-detail";
|
|
378
|
-
selectedMachineIdParam.value = machineId;
|
|
379
|
-
}}
|
|
380
378
|
>
|
|
381
379
|
View Machine
|
|
382
|
-
</
|
|
380
|
+
</Anchor>
|
|
383
381
|
|
|
384
382
|
|
|
385
383
|
<div
|
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import { SocketFunction } from "socket-function/SocketFunction";
|
|
2
2
|
import { qreact } from "../../4-dom/qreact";
|
|
3
|
-
import {
|
|
3
|
+
import { MachineServiceController, ServiceConfig } from "../machineSchema";
|
|
4
4
|
import { css } from "typesafecss";
|
|
5
5
|
import { t } from "../../2-proxy/schema2";
|
|
6
6
|
import { Querysub } from "../../4-querysub/QuerysubController";
|
|
7
7
|
import { currentViewParam, selectedServiceIdParam } from "../urlParams";
|
|
8
8
|
import { formatNiceDateTime, formatTime, formatVeryNiceDateTime } from "socket-function/src/formatting/format";
|
|
9
|
-
import { sort } from "socket-function/src/misc";
|
|
9
|
+
import { sort, timeInMinute } from "socket-function/src/misc";
|
|
10
10
|
import { cache } from "socket-function/src/caching";
|
|
11
|
+
import { Anchor } from "../../library-components/ATag";
|
|
12
|
+
import { isPublic } from "../../config";
|
|
13
|
+
import { UpdateButtons, UpdateServiceButtons } from "./deployButtons";
|
|
14
|
+
import { isDefined } from "../../misc";
|
|
15
|
+
import { formatTimeJSX } from "../../misc/formatJSX";
|
|
16
|
+
|
|
17
|
+
module.hotreload = true;
|
|
11
18
|
|
|
12
19
|
export class ServicesListPage extends qreact.Component {
|
|
13
20
|
|
|
14
21
|
render() {
|
|
15
|
-
let controller =
|
|
22
|
+
let controller = MachineServiceController(SocketFunction.browserNodeId());
|
|
16
23
|
let serviceList = controller.getServiceList();
|
|
17
24
|
|
|
18
25
|
if (!serviceList) return <div>Loading services...</div>;
|
|
@@ -23,7 +30,7 @@ export class ServicesListPage extends qreact.Component {
|
|
|
23
30
|
let getMachineInfo = cache((machineId: string) => controller.getMachineInfo(machineId));
|
|
24
31
|
|
|
25
32
|
return <div className={css.vbox(16)}>
|
|
26
|
-
<div className={css.hbox(12)}>
|
|
33
|
+
<div className={css.hbox(12).wrap}>
|
|
27
34
|
<h2 className={css.flexGrow(1)}>Services</h2>
|
|
28
35
|
<button className={css.pad2(12, 8).button.bord2(0, 0, 20).hsl(0, 0, 100)}
|
|
29
36
|
onClick={() => {
|
|
@@ -36,14 +43,14 @@ export class ServicesListPage extends qreact.Component {
|
|
|
36
43
|
parameters: {
|
|
37
44
|
key: newServiceId,
|
|
38
45
|
repoUrl: gitInfo.repoUrl,
|
|
39
|
-
gitRef: gitInfo.
|
|
46
|
+
gitRef: gitInfo.latestRef,
|
|
40
47
|
command: "ping 1.1.1.1",
|
|
41
48
|
deploy: false
|
|
42
49
|
},
|
|
43
50
|
info: {
|
|
44
51
|
title: `New Service created at ${formatNiceDateTime(Date.now())}`,
|
|
45
52
|
notes: "Notes",
|
|
46
|
-
lastUpdatedTime: Date.now()
|
|
53
|
+
lastUpdatedTime: Date.now(),
|
|
47
54
|
}
|
|
48
55
|
};
|
|
49
56
|
|
|
@@ -56,10 +63,11 @@ export class ServicesListPage extends qreact.Component {
|
|
|
56
63
|
}}>
|
|
57
64
|
+ New Service
|
|
58
65
|
</button>
|
|
66
|
+
<UpdateButtons services={services.map(x => x[1]).filter(isDefined)} />
|
|
59
67
|
</div>
|
|
60
68
|
<div className={css.vbox(8)}>
|
|
61
69
|
{services.map(([serviceId, config]) => {
|
|
62
|
-
if (!config) return <div key={serviceId}>Config is broken?</div>;
|
|
70
|
+
if (!config) return <div key={serviceId}>Config is broken? Missing value for service? Is the file corrupted?</div>;
|
|
63
71
|
let failingMachines = config.machineIds.filter(machineId => {
|
|
64
72
|
let machineInfo = getMachineInfo(machineId);
|
|
65
73
|
return machineInfo?.services[serviceId]?.errorFromLastRun;
|
|
@@ -78,33 +86,35 @@ export class ServicesListPage extends qreact.Component {
|
|
|
78
86
|
return acc + (machineInfo?.services[serviceId]?.totalTimesLaunched || 0);
|
|
79
87
|
}, 0);
|
|
80
88
|
let unknown = config.machineIds.length - runningMachines.length - failingMachines.length - missingMachines.length;
|
|
81
|
-
return <div
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
89
|
+
return <div className={css.hbox(10)}>
|
|
90
|
+
<Anchor noStyles key={serviceId}
|
|
91
|
+
values={[currentViewParam.getOverride("service-detail"), selectedServiceIdParam.getOverride(serviceId)]}
|
|
92
|
+
className={
|
|
93
|
+
css.pad2(12).button.bord2(0, 0, 20)
|
|
94
|
+
+ (
|
|
95
|
+
failingMachines.length > 0 && css.hsl(0, 50, 60)
|
|
96
|
+
|| config.parameters.deploy && css.hsl(0, 0, 100)
|
|
97
|
+
|| !config.parameters.deploy && css.hsl(0, 0, 70)
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
>
|
|
101
|
+
<div className={css.hbox(12)}>
|
|
102
|
+
<div className={css.vbox(4).flexGrow(1)}>
|
|
103
|
+
<div className={css.fontSize(14).boldStyle}>{config.info.title}</div>
|
|
104
|
+
<div>{config.parameters.key}</div>
|
|
105
|
+
<div>
|
|
106
|
+
{config.machineIds.length} configured {failingMachines.length > 0 && `(${failingMachines.length} failing)`} {missingMachines.length > 0 && `(${missingMachines.length} machine hasn't run service yet)`} {unknown > 0 && `(${unknown} unknown)`} • {totalLaunches} launches •
|
|
107
|
+
Deploy: {config.parameters.deploy ? "enabled" : "disabled"}
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
98
110
|
<div>
|
|
99
|
-
|
|
100
|
-
Deploy: {config.parameters.deploy ? "enabled" : "disabled"}
|
|
111
|
+
Updated {formatTimeJSX(config.info.lastUpdatedTime)}
|
|
101
112
|
</div>
|
|
102
113
|
</div>
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
</div>;
|
|
114
|
+
</Anchor>
|
|
115
|
+
<UpdateServiceButtons service={config} />
|
|
116
|
+
</div>
|
|
117
|
+
;
|
|
108
118
|
})}
|
|
109
119
|
</div>
|
|
110
120
|
</div>;
|