From 41b2dd82f0f7522508e90f9d6ee65eb398c19c7c Mon Sep 17 00:00:00 2001
From: CityFun <31820853+zhengkunwang223@users.noreply.github.com>
Date: Tue, 20 May 2025 18:34:17 +0800
Subject: [PATCH] feat: add terminal for runtimes (#8758)
---
agent/app/service/host_tool.go | 2 +-
.../website/runtime/components/terminal.vue | 85 +++++++++++++++++++
.../views/website/runtime/dotnet/index.vue | 17 ++++
.../src/views/website/runtime/go/index.vue | 17 ++++
.../src/views/website/runtime/java/index.vue | 17 ++++
.../src/views/website/runtime/node/index.vue | 17 ++++
.../src/views/website/runtime/php/index.vue | 17 ++++
.../views/website/runtime/python/index.vue | 17 ++++
8 files changed, 188 insertions(+), 1 deletion(-)
create mode 100644 frontend/src/views/website/runtime/components/terminal.vue
diff --git a/agent/app/service/host_tool.go b/agent/app/service/host_tool.go
index 145deb40a..eb8ed22f7 100644
--- a/agent/app/service/host_tool.go
+++ b/agent/app/service/host_tool.go
@@ -552,7 +552,7 @@ func operateSupervisorCtl(operate, name, group, includeDir, containerName string
err error
)
if containerName != "" {
- cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(2 * time.Second))
+ cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(30 * time.Second))
output, err = cmdMgr.RunWithStdoutBashCf("docker exec %s supervisorctl %s", containerName, strings.Join(processNames, " "))
} else {
var out []byte
diff --git a/frontend/src/views/website/runtime/components/terminal.vue b/frontend/src/views/website/runtime/components/terminal.vue
new file mode 100644
index 000000000..e70ce463e
--- /dev/null
+++ b/frontend/src/views/website/runtime/components/terminal.vue
@@ -0,0 +1,85 @@
+
+
+
+
+
+ {{ $t('commons.button.conn') }}
+
+ {{ $t('commons.button.disConn') }}
+
+
+
+
+
+
+
diff --git a/frontend/src/views/website/runtime/dotnet/index.vue b/frontend/src/views/website/runtime/dotnet/index.vue
index ac1e7edce..bedc4bdeb 100644
--- a/frontend/src/views/website/runtime/dotnet/index.vue
+++ b/frontend/src/views/website/runtime/dotnet/index.vue
@@ -78,6 +78,7 @@
+
@@ -98,6 +99,7 @@ import { ElMessageBox } from 'element-plus';
import { GlobalStore } from '@/store';
import RuntimeStatus from '@/views/website/runtime/components/runtime-status.vue';
import PortJump from '@/views/website/runtime/components/port-jump.vue';
+import Terminal from '@/views/website/runtime/components/terminal.vue';
import { disabledButton } from '@/utils/runtime';
const loading = ref(false);
@@ -107,6 +109,7 @@ const deleteRef = ref();
const dialogPortJumpRef = ref();
const composeLogRef = ref();
const checkRef = ref();
+const terminalRef = ref();
const globalStore = GlobalStore();
const mobile = computed(() => {
@@ -162,6 +165,15 @@ const buttons = [
return disabledButton(row, 'edit');
},
},
+ {
+ label: i18n.global.t('menu.terminal'),
+ click: function (row: Runtime.Runtime) {
+ openTerminal(row);
+ },
+ disabled: function (row: Runtime.Runtime) {
+ return disabledButton(row, 'config');
+ },
+ },
{
label: i18n.global.t('commons.button.delete'),
click: function (row: Runtime.Runtime) {
@@ -207,6 +219,11 @@ const openDelete = (row: Runtime.Runtime) => {
});
};
+const openTerminal = (row: Runtime.Runtime) => {
+ const container = row.params['CONTAINER_NAME'];
+ terminalRef.value.acceptParams({ containerID: container, container: container });
+};
+
const openLog = (row: any) => {
composeLogRef.value.acceptParams({ compose: row.path + '/docker-compose.yml', resource: row.name });
};
diff --git a/frontend/src/views/website/runtime/go/index.vue b/frontend/src/views/website/runtime/go/index.vue
index 1ef791540..8ff79afe0 100644
--- a/frontend/src/views/website/runtime/go/index.vue
+++ b/frontend/src/views/website/runtime/go/index.vue
@@ -78,6 +78,7 @@
+
@@ -97,6 +98,7 @@ import AppResources from '@/views/website/runtime/php/check/index.vue';
import { ElMessageBox } from 'element-plus';
import RuntimeStatus from '@/views/website/runtime/components/runtime-status.vue';
import PortJump from '@/views/website/runtime/components/port-jump.vue';
+import Terminal from '@/views/website/runtime/components/terminal.vue';
import { disabledButton } from '@/utils/runtime';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
@@ -111,6 +113,7 @@ const deleteRef = ref();
const dialogPortJumpRef = ref();
const composeLogRef = ref();
const checkRef = ref();
+const terminalRef = ref();
const paginationConfig = reactive({
cacheSizeKey: 'runtime-page-size',
@@ -161,6 +164,15 @@ const buttons = [
return disabledButton(row, 'edit');
},
},
+ {
+ label: i18n.global.t('menu.terminal'),
+ click: function (row: Runtime.Runtime) {
+ openTerminal(row);
+ },
+ disabled: function (row: Runtime.Runtime) {
+ return disabledButton(row, 'config');
+ },
+ },
{
label: i18n.global.t('commons.button.delete'),
click: function (row: Runtime.Runtime) {
@@ -214,6 +226,11 @@ const goDashboard = async (port: any, protocol: string) => {
dialogPortJumpRef.value.acceptParams({ port: port, protocol: protocol });
};
+const openTerminal = (row: Runtime.Runtime) => {
+ const container = row.params['CONTAINER_NAME'];
+ terminalRef.value.acceptParams({ containerID: container, container: container });
+};
+
const operateRuntime = async (operate: string, ID: number) => {
try {
const action = await ElMessageBox.confirm(
diff --git a/frontend/src/views/website/runtime/java/index.vue b/frontend/src/views/website/runtime/java/index.vue
index c4bd07305..9b772adff 100644
--- a/frontend/src/views/website/runtime/java/index.vue
+++ b/frontend/src/views/website/runtime/java/index.vue
@@ -78,6 +78,7 @@
+
@@ -97,6 +98,7 @@ import AppResources from '@/views/website/runtime/php/check/index.vue';
import { ElMessageBox } from 'element-plus';
import RuntimeStatus from '@/views/website/runtime/components/runtime-status.vue';
import PortJump from '@/views/website/runtime/components/port-jump.vue';
+import Terminal from '@/views/website/runtime/components/terminal.vue';
import { disabledButton } from '@/utils/runtime';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
@@ -111,6 +113,7 @@ const deleteRef = ref();
const dialogPortJumpRef = ref();
const composeLogRef = ref();
const checkRef = ref();
+const terminalRef = ref();
const paginationConfig = reactive({
cacheSizeKey: 'runtime-page-size',
@@ -161,6 +164,15 @@ const buttons = [
return disabledButton(row, 'edit');
},
},
+ {
+ label: i18n.global.t('menu.terminal'),
+ click: function (row: Runtime.Runtime) {
+ openTerminal(row);
+ },
+ disabled: function (row: Runtime.Runtime) {
+ return disabledButton(row, 'config');
+ },
+ },
{
label: i18n.global.t('commons.button.delete'),
click: function (row: Runtime.Runtime) {
@@ -210,6 +222,11 @@ const openLog = (row: any) => {
composeLogRef.value.acceptParams({ compose: row.path + '/docker-compose.yml', resource: row.name });
};
+const openTerminal = (row: Runtime.Runtime) => {
+ const container = row.params['CONTAINER_NAME'];
+ terminalRef.value.acceptParams({ containerID: container, container: container });
+};
+
const goDashboard = async (port: any, protocol: string) => {
dialogPortJumpRef.value.acceptParams({ port: port, protocol: protocol });
};
diff --git a/frontend/src/views/website/runtime/node/index.vue b/frontend/src/views/website/runtime/node/index.vue
index 4cf520f9f..f09eb053f 100644
--- a/frontend/src/views/website/runtime/node/index.vue
+++ b/frontend/src/views/website/runtime/node/index.vue
@@ -79,6 +79,7 @@
+
@@ -99,6 +100,7 @@ import AppResources from '@/views/website/runtime/php/check/index.vue';
import { ElMessageBox } from 'element-plus';
import RuntimeStatus from '@/views/website/runtime/components/runtime-status.vue';
import PortJump from '@/views/website/runtime/components/port-jump.vue';
+import Terminal from '@/views/website/runtime/components/terminal.vue';
import { disabledButton } from '@/utils/runtime';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
@@ -114,6 +116,7 @@ const dialogPortJumpRef = ref();
const composeLogRef = ref();
const moduleRef = ref();
const checkRef = ref();
+const terminalRef = ref();
const paginationConfig = reactive({
cacheSizeKey: 'runtime-page-size',
@@ -173,6 +176,15 @@ const buttons = [
return disabledButton(row, 'edit');
},
},
+ {
+ label: i18n.global.t('menu.terminal'),
+ click: function (row: Runtime.Runtime) {
+ openTerminal(row);
+ },
+ disabled: function (row: Runtime.Runtime) {
+ return disabledButton(row, 'config');
+ },
+ },
{
label: i18n.global.t('commons.button.delete'),
click: function (row: Runtime.Runtime) {
@@ -230,6 +242,11 @@ const goDashboard = async (port: any, protocol: string) => {
dialogPortJumpRef.value.acceptParams({ port: port, protocol: protocol });
};
+const openTerminal = (row: Runtime.Runtime) => {
+ const container = row.params['CONTAINER_NAME'];
+ terminalRef.value.acceptParams({ containerID: container, container: container });
+};
+
const operateRuntime = async (operate: string, ID: number) => {
try {
const action = await ElMessageBox.confirm(
diff --git a/frontend/src/views/website/runtime/php/index.vue b/frontend/src/views/website/runtime/php/index.vue
index 5cb513053..ab1115f02 100644
--- a/frontend/src/views/website/runtime/php/index.vue
+++ b/frontend/src/views/website/runtime/php/index.vue
@@ -103,6 +103,7 @@
+
@@ -125,6 +126,7 @@ import ComposeLogs from '@/components/log/compose/index.vue';
import Config from '@/views/website/runtime/php/config/index.vue';
import Supervisor from '@/views/website/runtime/php/supervisor/index.vue';
import RuntimeStatus from '@/views/website/runtime/components/runtime-status.vue';
+import Terminal from '@/views/website/runtime/components/terminal.vue';
import { disabledButton } from '@/utils/runtime';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
@@ -155,6 +157,7 @@ const items = ref([]);
const composeLogRef = ref();
const configRef = ref();
const supervisorRef = ref();
+const terminalRef = ref();
const buttons = [
{
@@ -220,6 +223,15 @@ const buttons = [
return disabledButton(row, 'config');
},
},
+ {
+ label: i18n.global.t('menu.terminal'),
+ click: function (row: Runtime.Runtime) {
+ openTerminal(row);
+ },
+ disabled: function (row: Runtime.Runtime) {
+ return disabledButton(row, 'config');
+ },
+ },
{
label: i18n.global.t('commons.button.delete'),
click: function (row: Runtime.Runtime) {
@@ -258,6 +270,11 @@ const openSupervisor = (row: Runtime.Runtime) => {
supervisorRef.value.acceptParams(row.id);
};
+const openTerminal = (row: Runtime.Runtime) => {
+ const container = row.params['CONTAINER_NAME'];
+ terminalRef.value.acceptParams({ containerID: container, container: container });
+};
+
const openLog = (row: Runtime.RuntimeDTO) => {
if (row.status == 'Running') {
composeLogRef.value.acceptParams({ compose: row.path + '/docker-compose.yml', resource: row.name });
diff --git a/frontend/src/views/website/runtime/python/index.vue b/frontend/src/views/website/runtime/python/index.vue
index af113a47c..a64e301e9 100644
--- a/frontend/src/views/website/runtime/python/index.vue
+++ b/frontend/src/views/website/runtime/python/index.vue
@@ -78,6 +78,7 @@
+
@@ -97,6 +98,7 @@ import AppResources from '@/views/website/runtime/php/check/index.vue';
import { ElMessageBox } from 'element-plus';
import RuntimeStatus from '@/views/website/runtime/components/runtime-status.vue';
import PortJump from '@/views/website/runtime/components/port-jump.vue';
+import Terminal from '@/views/website/runtime/components/terminal.vue';
import { disabledButton } from '@/utils/runtime';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
@@ -111,6 +113,7 @@ const deleteRef = ref();
const dialogPortJumpRef = ref();
const composeLogRef = ref();
const checkRef = ref();
+const terminalRef = ref();
const paginationConfig = reactive({
cacheSizeKey: 'runtime-page-size',
@@ -161,6 +164,15 @@ const buttons = [
return disabledButton(row, 'edit');
},
},
+ {
+ label: i18n.global.t('menu.terminal'),
+ click: function (row: Runtime.Runtime) {
+ openTerminal(row);
+ },
+ disabled: function (row: Runtime.Runtime) {
+ return disabledButton(row, 'config');
+ },
+ },
{
label: i18n.global.t('commons.button.delete'),
click: function (row: Runtime.Runtime) {
@@ -214,6 +226,11 @@ const goDashboard = async (port: any, protocol: string) => {
dialogPortJumpRef.value.acceptParams({ port: port, protocol: protocol });
};
+const openTerminal = (row: Runtime.Runtime) => {
+ const container = row.params['CONTAINER_NAME'];
+ terminalRef.value.acceptParams({ containerID: container, container: container });
+};
+
const operateRuntime = async (operate: string, ID: number) => {
try {
const action = await ElMessageBox.confirm(