add checkbox to confirm stack files deletion

This commit is contained in:
husa 2025-01-17 15:33:29 +02:00
parent d451e06e84
commit 4b6c44f99f
4 changed files with 23 additions and 10 deletions

View File

@ -1,7 +1,7 @@
import { AgentSocketHandler } from "../agent-socket-handler"; import { AgentSocketHandler } from "../agent-socket-handler";
import { DockgeServer } from "../dockge-server"; import { DockgeServer } from "../dockge-server";
import { callbackError, callbackResult, checkLogin, DockgeSocket, ValidationError } from "../util-server"; import { callbackError, callbackResult, checkLogin, DockgeSocket, ValidationError } from "../util-server";
import { Stack } from "../stack"; import { DeleteOptions, Stack } from "../stack";
import { AgentSocket } from "../../common/agent-socket"; import { AgentSocket } from "../../common/agent-socket";
export class DockerSocketHandler extends AgentSocketHandler { export class DockerSocketHandler extends AgentSocketHandler {
@ -40,7 +40,7 @@ export class DockerSocketHandler extends AgentSocketHandler {
} }
}); });
agentSocket.on("deleteStack", async (name : unknown, callback) => { agentSocket.on("deleteStack", async (name : unknown, deleteOptions: unknown, callback) => {
try { try {
checkLogin(socket); checkLogin(socket);
if (typeof(name) !== "string") { if (typeof(name) !== "string") {
@ -49,7 +49,7 @@ export class DockerSocketHandler extends AgentSocketHandler {
const stack = await Stack.getStack(server, name); const stack = await Stack.getStack(server, name);
try { try {
await stack.delete(socket); await stack.delete(socket, deleteOptions as DeleteOptions);
} catch (e) { } catch (e) {
server.sendStackList(); server.sendStackList();
throw e; throw e;

View File

@ -20,6 +20,10 @@ import { InteractiveTerminal, Terminal } from "./terminal";
import childProcessAsync from "promisify-child-process"; import childProcessAsync from "promisify-child-process";
import { Settings } from "./settings"; import { Settings } from "./settings";
export interface DeleteOptions {
deleteStackFiles: boolean
}
export class Stack { export class Stack {
name: string; name: string;
@ -215,18 +219,20 @@ export class Stack {
return exitCode; return exitCode;
} }
async delete(socket: DockgeSocket) : Promise<number> { async delete(socket: DockgeSocket, options: DeleteOptions) : Promise<number> {
const terminalName = getComposeTerminalName(socket.endpoint, this.name); const terminalName = getComposeTerminalName(socket.endpoint, this.name);
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "down", "--remove-orphans" ], this.path); let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "down", "--remove-orphans" ], this.path);
if (exitCode !== 0) { if (exitCode !== 0) {
throw new Error("Failed to delete, please check the terminal output for more information."); throw new Error("Failed to delete, please check the terminal output for more information.");
} }
// Remove the stack folder if (options.deleteStackFiles) {
await fsAsync.rm(this.path, { // Remove the stack folder
recursive: true, await fsAsync.rm(this.path, {
force: true recursive: true,
}); force: true
});
}
return exitCode; return exitCode;
} }

View File

@ -25,6 +25,7 @@
"saveStackDraft": "Save", "saveStackDraft": "Save",
"notAvailableShort": "N/A", "notAvailableShort": "N/A",
"deleteStackMsg": "Are you sure you want to delete this stack?", "deleteStackMsg": "Are you sure you want to delete this stack?",
"deleteStackFilesConfirmation": "delete all stack files",
"stackNotManagedByDockgeMsg": "This stack is not managed by Dockge.", "stackNotManagedByDockgeMsg": "This stack is not managed by Dockge.",
"primaryHostname": "Primary Hostname", "primaryHostname": "Primary Hostname",
"general": "General", "general": "General",

View File

@ -55,6 +55,7 @@
</div> </div>
<button v-if="isEditMode && !isAdd" class="btn btn-normal" :disabled="processing" @click="discardStack">{{ $t("discardStack") }}</button> <button v-if="isEditMode && !isAdd" class="btn btn-normal" :disabled="processing" @click="discardStack">{{ $t("discardStack") }}</button>
<button v-if="!isEditMode" class="btn btn-danger" :disabled="processing" @click="showDeleteDialog = !showDeleteDialog"> <button v-if="!isEditMode" class="btn btn-danger" :disabled="processing" @click="showDeleteDialog = !showDeleteDialog">
<font-awesome-icon icon="trash" class="me-1" /> <font-awesome-icon icon="trash" class="me-1" />
{{ $t("deleteStack") }} {{ $t("deleteStack") }}
@ -231,6 +232,10 @@
<!-- Delete Dialog --> <!-- Delete Dialog -->
<BModal v-model="showDeleteDialog" :okTitle="$t('deleteStack')" okVariant="danger" @ok="deleteDialog"> <BModal v-model="showDeleteDialog" :okTitle="$t('deleteStack')" okVariant="danger" @ok="deleteDialog">
{{ $t("deleteStackMsg") }} {{ $t("deleteStackMsg") }}
<div class="form-check mt-4">
<label><input v-model="deleteStackFiles" class="form-check-input" type="checkbox" />{{
$t("deleteStackFilesConfirmation") }}</label>
</div>
</BModal> </BModal>
</div> </div>
</transition> </transition>
@ -309,6 +314,7 @@ export default {
isEditMode: false, isEditMode: false,
submitted: false, submitted: false,
showDeleteDialog: false, showDeleteDialog: false,
deleteStackFiles: false,
newContainerName: "", newContainerName: "",
stopServiceStatusTimeout: false, stopServiceStatusTimeout: false,
}; };
@ -646,7 +652,7 @@ export default {
}, },
deleteDialog() { deleteDialog() {
this.$root.emitAgent(this.endpoint, "deleteStack", this.stack.name, (res) => { this.$root.emitAgent(this.endpoint, "deleteStack", this.stack.name, { deleteStackFiles: this.deleteStackFiles }, (res) => {
this.$root.toastRes(res); this.$root.toastRes(res);
if (res.ok) { if (res.ok) {
this.$router.push("/"); this.$router.push("/");