Skip to content

Commit 1d472a0

Browse files
authored
Container control buttons (louislam#649)
Merged @mizady's pull request adding container control buttons.
2 parents 7aca50c + 1dd3205 commit 1d472a0

File tree

4 files changed

+169
-2
lines changed

4 files changed

+169
-2
lines changed

backend/agent-socket-handlers/docker-socket-handler.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ export class DockerSocketHandler extends AgentSocketHandler {
147147
msgi18n: true,
148148
}, callback);
149149
server.sendStackList();
150+
151+
stack.leaveCombinedTerminal(socket);
150152
} catch (e) {
151153
callbackError(e, callback);
152154
}
@@ -238,6 +240,68 @@ export class DockerSocketHandler extends AgentSocketHandler {
238240
}
239241
});
240242

243+
// Start a service
244+
agentSocket.on("startService", async (stackName: unknown, serviceName: unknown, callback) => {
245+
try {
246+
checkLogin(socket);
247+
248+
if (typeof (stackName) !== "string" || typeof (serviceName) !== "string") {
249+
throw new ValidationError("Stack name and service name must be strings");
250+
}
251+
252+
const stack = await Stack.getStack(server, stackName);
253+
await stack.startService(socket, serviceName);
254+
stack.joinCombinedTerminal(socket); // Ensure the combined terminal is joined
255+
callbackResult({
256+
ok: true,
257+
msg: "Service" + serviceName + " started"
258+
}, callback);
259+
server.sendStackList();
260+
} catch (e) {
261+
callbackError(e, callback);
262+
}
263+
});
264+
265+
// Stop a service
266+
agentSocket.on("stopService", async (stackName: unknown, serviceName: unknown, callback) => {
267+
try {
268+
checkLogin(socket);
269+
270+
if (typeof (stackName) !== "string" || typeof (serviceName) !== "string") {
271+
throw new ValidationError("Stack name and service name must be strings");
272+
}
273+
274+
const stack = await Stack.getStack(server, stackName);
275+
await stack.stopService(socket, serviceName);
276+
callbackResult({
277+
ok: true,
278+
msg: "Service" + serviceName + " stopped"
279+
}, callback);
280+
server.sendStackList();
281+
} catch (e) {
282+
callbackError(e, callback);
283+
}
284+
});
285+
286+
agentSocket.on("restartService", async (stackName: unknown, serviceName: unknown, callback) => {
287+
try {
288+
checkLogin(socket);
289+
290+
if (typeof stackName !== "string" || typeof serviceName !== "string") {
291+
throw new Error("Invalid stackName or serviceName");
292+
}
293+
294+
const stack = await Stack.getStack(server, stackName, true);
295+
await stack.restartService(socket, serviceName);
296+
callbackResult({
297+
ok: true,
298+
msg: "Service" + serviceName + " restarted"
299+
}, callback);
300+
} catch (e) {
301+
callbackError(e, callback);
302+
}
303+
});
304+
241305
// getExternalNetworkList
242306
agentSocket.on("getDockerNetworkList", async (callback) => {
243307
try {

backend/stack.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,4 +530,34 @@ export class Stack {
530530
}
531531

532532
}
533+
534+
async startService(socket: DockgeSocket, serviceName: string) {
535+
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
536+
const exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", ["compose", "up", "-d", serviceName], this.path);
537+
if (exitCode !== 0) {
538+
throw new Error(`Failed to start service ${serviceName}, please check logs for more information.`);
539+
}
540+
541+
return exitCode;
542+
}
543+
544+
async stopService(socket: DockgeSocket, serviceName: string): Promise<number> {
545+
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
546+
const exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", ["compose", "stop", serviceName], this.path);
547+
if (exitCode !== 0) {
548+
throw new Error(`Failed to stop service ${serviceName}, please check logs for more information.`);
549+
}
550+
551+
return exitCode;
552+
}
553+
554+
async restartService(socket: DockgeSocket, serviceName: string): Promise<number> {
555+
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
556+
const exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", ["compose", "restart", serviceName], this.path);
557+
if (exitCode !== 0) {
558+
throw new Error(`Failed to restart service ${serviceName}, please check logs for more information.`);
559+
}
560+
561+
return exitCode;
562+
}
533563
}

frontend/src/components/Container.vue

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<div class="shadow-box big-padding mb-3 container">
33
<div class="row">
4-
<div class="col-7">
4+
<div class="col-5">
55
<h4>{{ name }}</h4>
66
<div class="image mb-2">
77
<span class="me-1">{{ imageName }}:</span><span class="tag">{{ imageTag }}</span>
@@ -14,12 +14,33 @@
1414
</a>
1515
</div>
1616
</div>
17-
<div class="col-5">
17+
<div class="col-7">
1818
<div class="function">
1919
<router-link v-if="!isEditMode" class="btn btn-normal" :to="terminalRouteLink" disabled="">
2020
<font-awesome-icon icon="terminal" />
2121
Bash
2222
</router-link>
23+
<button v-if="status !== 'running' && status !== 'healthy'"
24+
class="btn btn-primary me-2"
25+
:disabled="processing"
26+
@click="startService">
27+
<font-awesome-icon icon="play" class="me-1" />
28+
Start
29+
</button>
30+
<button v-if="status === 'running' || status === 'healthy' || status === 'unhealthy'"
31+
class="btn btn-danger me-2"
32+
:disabled="processing"
33+
@click="stopService">
34+
<font-awesome-icon icon="stop" class="me-1" />
35+
Stop
36+
</button>
37+
<button v-if="status === 'running' || status === 'healthy' || status === 'unhealthy'"
38+
class="btn btn-warning me-2"
39+
:disabled="processing"
40+
@click="restartService">
41+
<font-awesome-icon icon="sync" class="me-1" />
42+
Restart
43+
</button>
2344
</div>
2445
</div>
2546
</div>
@@ -284,6 +305,16 @@ export default defineComponent({
284305
remove() {
285306
delete this.jsonObject.services[this.name];
286307
},
308+
startService() {
309+
this.$emit("start-service", this.name);
310+
},
311+
stopService() {
312+
this.$emit("stop-service", this.name);
313+
},
314+
restartService() {
315+
this.$emit("restart-service", this.name);
316+
}
317+
287318
}
288319
});
289320
</script>

frontend/src/pages/Compose.vue

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@
129129
:is-edit-mode="isEditMode"
130130
:first="name === Object.keys(jsonConfig.services)[0]"
131131
:status="serviceStatusList[name]"
132+
:processing="processing"
133+
@start-service="startService"
134+
@stop-service="stopService"
135+
@restart-service="restartService"
132136
/>
133137
</div>
134138

@@ -776,6 +780,44 @@ export default {
776780
this.stack.name = this.stack?.name?.toLowerCase();
777781
},
778782
783+
startService(serviceName) {
784+
this.processing = true;
785+
786+
this.$root.emitAgent(this.endpoint, "startService", this.stack.name, serviceName, (res) => {
787+
this.processing = false;
788+
this.$root.toastRes(res);
789+
790+
if (res.ok) {
791+
this.requestServiceStatus(); // Refresh service status
792+
}
793+
});
794+
},
795+
796+
stopService(serviceName) {
797+
this.processing = true;
798+
799+
this.$root.emitAgent(this.endpoint, "stopService", this.stack.name, serviceName, (res) => {
800+
this.processing = false;
801+
this.$root.toastRes(res);
802+
803+
if (res.ok) {
804+
this.requestServiceStatus(); // Refresh service status
805+
}
806+
});
807+
},
808+
809+
restartService(serviceName) {
810+
this.processing = true;
811+
812+
this.$root.emitAgent(this.endpoint, "restartService", this.stack.name, serviceName, (res) => {
813+
this.processing = false;
814+
this.$root.toastRes(res);
815+
816+
if (res.ok) {
817+
this.requestServiceStatus(); // Refresh service status
818+
}
819+
});
820+
},
779821
}
780822
};
781823
</script>

0 commit comments

Comments
 (0)