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 @@ + + + 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(