Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/course/compute/terminal-command.tsx
1503 views
1
import { Button, Card, Input, InputNumber, List, Space } from "antd";
2
import { Icon } from "@cocalc/frontend/components/icon";
3
import { type CSSProperties, useState } from "react";
4
import ShowError from "@cocalc/frontend/components/error";
5
import { plural } from "@cocalc/util/misc";
6
import { getServerId } from "./students";
7
import type { SelectedStudents, ServersMap } from "./students";
8
import type { Unit } from "../store";
9
import type { CourseActions } from "../actions";
10
import { RenderOutput } from "../configuration/terminal-command";
11
12
export function TerminalButton({ terminal, setTerminal }) {
13
return (
14
<>
15
<Button onClick={() => setTerminal(!terminal)}>
16
<Icon name="terminal" /> Terminal
17
</Button>
18
</>
19
);
20
}
21
22
export function TerminalCommand({
23
style,
24
servers,
25
selected,
26
unit,
27
actions,
28
onClose,
29
}: {
30
style?: CSSProperties;
31
servers: ServersMap;
32
selected: SelectedStudents;
33
unit: Unit;
34
actions: CourseActions;
35
onClose;
36
}) {
37
const [timeout, setTimeout] = useState<number | null>(30);
38
const [command, setCommand] = useState<string>("");
39
const [running, setRunning] = useState<boolean>(false);
40
const [error, setError] = useState<string>("");
41
const [outputs, setOutputs] = useState<
42
{
43
stdout?: string;
44
stderr?: string;
45
exit_code?: number;
46
student_id: string;
47
total_time: number;
48
}[]
49
>([]);
50
51
const runningStudentIds: string[] = Array.from(selected).filter(
52
(student_id) =>
53
servers[getServerId({ unit, student_id })]?.state == "running",
54
);
55
56
const runTerminalCommand = async () => {
57
try {
58
setRunning(true);
59
setOutputs([]);
60
await actions.compute.runTerminalCommand({
61
setOutputs,
62
unit,
63
student_ids: runningStudentIds,
64
command,
65
timeout: timeout ?? 30,
66
err_on_exit: false,
67
});
68
} catch (err) {
69
setError(`${err}`);
70
} finally {
71
setRunning(false);
72
}
73
};
74
75
return (
76
<Card
77
title={
78
<div>
79
<Icon name="terminal" style={{ marginRight: "5px" }} /> Run
80
{running ? "ning" : ""} Command on the {runningStudentIds.length}{" "}
81
Running Student Compute {plural(runningStudentIds.length, "Server")}
82
<Button onClick={onClose} style={{ float: "right" }}>
83
Close
84
</Button>
85
</div>
86
}
87
style={style}
88
>
89
<Space.Compact
90
style={{
91
display: "flex",
92
whiteSpace: "nowrap",
93
marginBottom: "15px",
94
}}
95
>
96
<Input
97
allowClear
98
disabled={running}
99
style={{ fontFamily: "monospace" }}
100
placeholder={"Command to run on compute servers..."}
101
value={command}
102
onChange={(e) => {
103
setCommand(e.target.value);
104
}}
105
onPressEnter={() => {
106
runTerminalCommand();
107
}}
108
/>
109
<Button
110
style={{ width: "6em" }}
111
onClick={runTerminalCommand}
112
disabled={running || runningStudentIds.length == 0 || !command.trim()}
113
>
114
<Icon
115
name={running ? "cocalc-ring" : "play"}
116
spin={running}
117
style={{ marginRight: "5px" }}
118
/>{" "}
119
Run
120
</Button>
121
</Space.Compact>
122
<InputNumber
123
disabled={running}
124
style={{ maxWidth: "300px" }}
125
value={timeout}
126
onChange={(t) => setTimeout(t ?? null)}
127
min={15}
128
max={60 * 60}
129
addonAfter={"seconds timeout"}
130
/>
131
<ShowError style={{ margin: "15px" }} error={error} setError={setError} />
132
{outputs.length > 0 && (
133
<List
134
size="small"
135
style={{ marginTop: "15px", maxHeight: "400px", overflowY: "auto" }}
136
bordered
137
dataSource={outputs}
138
renderItem={(output) => (
139
<List.Item style={{ padding: "5px 5px 5px 30px" }}>
140
<RenderOutput
141
key={output.student_id}
142
title={
143
actions.get_store()?.get_student_name(output.student_id) ??
144
"---"
145
}
146
stdout={output.stdout}
147
stderr={output.stderr}
148
timeout={timeout}
149
total_time={output.total_time}
150
/>
151
</List.Item>
152
)}
153
/>
154
)}
155
</Card>
156
);
157
}
158
159