feat: add terminal for runtimes (#8758)

This commit is contained in:
CityFun 2025-05-20 18:34:17 +08:00 committed by GitHub
parent 6d8d6f05f1
commit 41b2dd82f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 188 additions and 1 deletions

View file

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

View file

@ -0,0 +1,85 @@
<template>
<DrawerPro
v-model="terminalVisible"
:header="$t('menu.terminal')"
@close="handleClose"
:resource="title"
size="large"
>
<template #content>
<el-form ref="formRef" :model="form" label-position="top">
<el-button v-if="!terminalOpen" @click="initTerm(formRef)">
{{ $t('commons.button.conn') }}
</el-button>
<el-button v-else @click="onClose()">{{ $t('commons.button.disConn') }}</el-button>
<Terminal
style="height: calc(100vh - 200px); margin-top: 18px"
ref="terminalRef"
v-if="terminalOpen"
></Terminal>
</el-form>
</template>
</DrawerPro>
</template>
<script lang="ts" setup>
import { reactive, ref, nextTick } from 'vue';
import { ElForm, FormInstance } from 'element-plus';
import Terminal from '@/components/terminal/index.vue';
const title = ref();
const terminalVisible = ref(false);
const terminalOpen = ref(false);
const form = reactive({
isCustom: false,
command: '',
user: '',
containerID: '',
});
const formRef = ref();
const terminalRef = ref<InstanceType<typeof Terminal> | null>(null);
interface DialogProps {
containerID: string;
container: string;
}
const acceptParams = async (params: DialogProps): Promise<void> => {
terminalVisible.value = true;
form.containerID = params.containerID;
title.value = params.container;
form.isCustom = false;
form.user = '';
form.command = '/bin/bash';
terminalOpen.value = false;
initTerm(formRef.value);
};
const initTerm = (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
terminalOpen.value = true;
await nextTick();
terminalRef.value!.acceptParams({
endpoint: '/api/v2/containers/exec',
args: `source=container&containerid=${form.containerID}&user=${form.user}&command=${form.command}`,
error: '',
initCmd: '',
});
});
};
const onClose = () => {
terminalRef.value?.onClose();
terminalOpen.value = false;
};
function handleClose() {
onClose();
terminalVisible.value = false;
}
defineExpose({
acceptParams,
});
</script>

View file

@ -78,6 +78,7 @@
<ComposeLogs ref="composeLogRef" />
<PortJumpDialog ref="dialogPortJumpRef" />
<AppResources ref="checkRef" @close="search" />
<Terminal ref="terminalRef" />
</div>
</template>
@ -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 });
};

View file

@ -78,6 +78,7 @@
<ComposeLogs ref="composeLogRef" />
<PortJumpDialog ref="dialogPortJumpRef" />
<AppResources ref="checkRef" @close="search" />
<Terminal ref="terminalRef" />
</div>
</template>
@ -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(

View file

@ -78,6 +78,7 @@
<ComposeLogs ref="composeLogRef" />
<PortJumpDialog ref="dialogPortJumpRef" />
<AppResources ref="checkRef" @close="search" />
<Terminal ref="terminalRef" />
</div>
</template>
@ -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 });
};

View file

@ -79,6 +79,7 @@
<PortJumpDialog ref="dialogPortJumpRef" />
<Modules ref="moduleRef" />
<AppResources ref="checkRef" @close="search" />
<Terminal ref="terminalRef" />
</div>
</template>
@ -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(

View file

@ -103,6 +103,7 @@
<ComposeLogs ref="composeLogRef" :highlightDiff="200" />
<Config ref="configRef" />
<Supervisor ref="supervisorRef" />
<Terminal ref="terminalRef" />
</div>
</template>
@ -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<Runtime.RuntimeDTO[]>([]);
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 });

View file

@ -78,6 +78,7 @@
<ComposeLogs ref="composeLogRef" />
<PortJumpDialog ref="dialogPortJumpRef" />
<AppResources ref="checkRef" @close="search" />
<Terminal ref="terminalRef" />
</div>
</template>
@ -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(