mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-11-24 02:59:16 +08:00
feat: 运行环境增加 Python (#6679)
This commit is contained in:
parent
d027a9a2ba
commit
1184c7d60e
@ -106,7 +106,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e
|
|||||||
if err := checkPortExist(int(portValue.(float64))); err != nil {
|
if err := checkPortExist(int(portValue.(float64))); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
|
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython:
|
||||||
if !fileOp.Stat(create.CodeDir) {
|
if !fileOp.Stat(create.CodeDir) {
|
||||||
return nil, buserr.New(constant.ErrPathNotFound)
|
return nil, buserr.New(constant.ErrPathNotFound)
|
||||||
}
|
}
|
||||||
@ -160,7 +160,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e
|
|||||||
if err = handlePHP(create, runtime, fileOp, appVersionDir); err != nil {
|
if err = handlePHP(create, runtime, fileOp, appVersionDir); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
|
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython:
|
||||||
runtime.Port = int(create.Params["port"].(float64))
|
runtime.Port = int(create.Params["port"].(float64))
|
||||||
if err = handleNodeAndJava(create, runtime, fileOp, appVersionDir); err != nil {
|
if err = handleNodeAndJava(create, runtime, fileOp, appVersionDir); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -339,7 +339,7 @@ func (r *RuntimeService) Get(id uint) (*response.RuntimeDTO, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.AppParams = appParams
|
res.AppParams = appParams
|
||||||
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
|
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython:
|
||||||
res.Params = make(map[string]interface{})
|
res.Params = make(map[string]interface{})
|
||||||
envs, err := gotenv.Unmarshal(runtime.Env)
|
envs, err := gotenv.Unmarshal(runtime.Env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -400,6 +400,8 @@ func (r *RuntimeService) Get(id uint) (*response.RuntimeDTO, error) {
|
|||||||
defaultVolumes = constant.RuntimeDefaultVolumes
|
defaultVolumes = constant.RuntimeDefaultVolumes
|
||||||
case constant.RuntimeGo:
|
case constant.RuntimeGo:
|
||||||
defaultVolumes = constant.GoDefaultVolumes
|
defaultVolumes = constant.GoDefaultVolumes
|
||||||
|
case constant.RuntimePython:
|
||||||
|
defaultVolumes = constant.RuntimeDefaultVolumes
|
||||||
}
|
}
|
||||||
for _, volume := range volumes {
|
for _, volume := range volumes {
|
||||||
exist := false
|
exist := false
|
||||||
@ -436,7 +438,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
|||||||
if exist != nil {
|
if exist != nil {
|
||||||
return buserr.New(constant.ErrImageExist)
|
return buserr.New(constant.ErrImageExist)
|
||||||
}
|
}
|
||||||
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
|
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython:
|
||||||
if runtime.Port != req.Port {
|
if runtime.Port != req.Port {
|
||||||
if err = checkPortExist(req.Port); err != nil {
|
if err = checkPortExist(req.Port); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -512,7 +514,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
go buildRuntime(runtime, imageID, oldEnv, req.Rebuild)
|
go buildRuntime(runtime, imageID, oldEnv, req.Rebuild)
|
||||||
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
|
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython:
|
||||||
runtime.Version = req.Version
|
runtime.Version = req.Version
|
||||||
runtime.CodeDir = req.CodeDir
|
runtime.CodeDir = req.CodeDir
|
||||||
runtime.Port = req.Port
|
runtime.Port = req.Port
|
||||||
@ -678,7 +680,7 @@ func (r *RuntimeService) SyncRuntimeStatus() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, runtime := range runtimes {
|
for _, runtime := range runtimes {
|
||||||
if runtime.Type == constant.RuntimeNode || runtime.Type == constant.RuntimeJava || runtime.Type == constant.RuntimeGo {
|
if runtime.Type == constant.RuntimeNode || runtime.Type == constant.RuntimeJava || runtime.Type == constant.RuntimeGo || runtime.Type == constant.RuntimePython {
|
||||||
_ = SyncRuntimeContainerStatus(&runtime)
|
_ = SyncRuntimeContainerStatus(&runtime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -412,6 +412,14 @@ func handleParams(create request.RuntimeCreate, projectDir string) (composeConte
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case constant.RuntimePython:
|
||||||
|
create.Params["CODE_DIR"] = create.CodeDir
|
||||||
|
create.Params["PYTHON_VERSION"] = create.Version
|
||||||
|
create.Params["PANEL_APP_PORT_HTTP"] = create.Port
|
||||||
|
composeContent, err = handleCompose(env, composeContent, create, projectDir)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newMap := make(map[string]string)
|
newMap := make(map[string]string)
|
||||||
@ -457,6 +465,8 @@ func handleCompose(env gotenv.Env, composeContent []byte, create request.Runtime
|
|||||||
ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${JAVA_APP_PORT}")
|
ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${JAVA_APP_PORT}")
|
||||||
case constant.RuntimeGo:
|
case constant.RuntimeGo:
|
||||||
ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${GO_APP_PORT}")
|
ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${GO_APP_PORT}")
|
||||||
|
case constant.RuntimePython:
|
||||||
|
ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${APP_PORT}")
|
||||||
|
|
||||||
}
|
}
|
||||||
for i, port := range create.ExposedPorts {
|
for i, port := range create.ExposedPorts {
|
||||||
@ -481,9 +491,7 @@ func handleCompose(env gotenv.Env, composeContent []byte, create request.Runtime
|
|||||||
var volumes []interface{}
|
var volumes []interface{}
|
||||||
defaultVolumes := make(map[string]string)
|
defaultVolumes := make(map[string]string)
|
||||||
switch create.Type {
|
switch create.Type {
|
||||||
case constant.RuntimeNode:
|
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimePython:
|
||||||
defaultVolumes = constant.RuntimeDefaultVolumes
|
|
||||||
case constant.RuntimeJava:
|
|
||||||
defaultVolumes = constant.RuntimeDefaultVolumes
|
defaultVolumes = constant.RuntimeDefaultVolumes
|
||||||
case constant.RuntimeGo:
|
case constant.RuntimeGo:
|
||||||
defaultVolumes = constant.GoDefaultVolumes
|
defaultVolumes = constant.GoDefaultVolumes
|
||||||
|
@ -19,6 +19,7 @@ const (
|
|||||||
RuntimeNode = "node"
|
RuntimeNode = "node"
|
||||||
RuntimeJava = "java"
|
RuntimeJava = "java"
|
||||||
RuntimeGo = "go"
|
RuntimeGo = "go"
|
||||||
|
RuntimePython = "python"
|
||||||
|
|
||||||
RuntimeProxyUnix = "unix"
|
RuntimeProxyUnix = "unix"
|
||||||
RuntimeProxyTcp = "tcp"
|
RuntimeProxyTcp = "tcp"
|
||||||
@ -43,7 +44,7 @@ var GoDefaultVolumes = map[string]string{
|
|||||||
}
|
}
|
||||||
|
|
||||||
var RuntimeDefaultVolumes = map[string]string{
|
var RuntimeDefaultVolumes = map[string]string{
|
||||||
|
"${CODE_DIR}": "/app",
|
||||||
"./run.sh": "/run.sh",
|
"./run.sh": "/run.sh",
|
||||||
"./.env": "/.env",
|
"./.env": "/.env",
|
||||||
"./mod": "/go/pkg/mod",
|
|
||||||
}
|
}
|
||||||
|
@ -2534,6 +2534,8 @@ const message = {
|
|||||||
envKey: 'Name',
|
envKey: 'Name',
|
||||||
envValue: 'Value',
|
envValue: 'Value',
|
||||||
environment: 'Environment Variable',
|
environment: 'Environment Variable',
|
||||||
|
pythonHelper:
|
||||||
|
'Please provide a complete start command, for example: pip install -r requirements.txt && python manage.py runserver 0.0.0.0:5000',
|
||||||
},
|
},
|
||||||
process: {
|
process: {
|
||||||
pid: 'Process ID',
|
pid: 'Process ID',
|
||||||
|
@ -2351,6 +2351,8 @@ const message = {
|
|||||||
envKey: '名稱',
|
envKey: '名稱',
|
||||||
envValue: '值',
|
envValue: '值',
|
||||||
environment: '環境變數',
|
environment: '環境變數',
|
||||||
|
pythonHelper:
|
||||||
|
'請填寫完整啟動指令,例如:pip install -r requirements.txt && python manage.py runserver 0.0.0.0:5000',
|
||||||
},
|
},
|
||||||
process: {
|
process: {
|
||||||
pid: '進程ID',
|
pid: '進程ID',
|
||||||
|
@ -2350,6 +2350,8 @@ const message = {
|
|||||||
envKey: '名称',
|
envKey: '名称',
|
||||||
envValue: '值',
|
envValue: '值',
|
||||||
environment: '环境变量',
|
environment: '环境变量',
|
||||||
|
pythonHelper:
|
||||||
|
'请填写完整启动命令,例如:pip install -r requirements.txt && python manage.py runserver 0.0.0.0:5000',
|
||||||
},
|
},
|
||||||
process: {
|
process: {
|
||||||
pid: '进程ID',
|
pid: '进程ID',
|
||||||
|
@ -78,6 +78,16 @@ const webSiteRouter = {
|
|||||||
requiresAuth: false,
|
requiresAuth: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/websites/runtimes/python',
|
||||||
|
name: 'python',
|
||||||
|
hidden: true,
|
||||||
|
component: () => import('@/views/website/runtime/python/index.vue'),
|
||||||
|
meta: {
|
||||||
|
activeMenu: '/websites/runtimes/php',
|
||||||
|
requiresAuth: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -243,16 +243,11 @@ const search = async (req: App.AppReq) => {
|
|||||||
const openInstall = (app: App.App) => {
|
const openInstall = (app: App.App) => {
|
||||||
switch (app.type) {
|
switch (app.type) {
|
||||||
case 'php':
|
case 'php':
|
||||||
router.push({ path: '/websites/runtimes/php' });
|
|
||||||
break;
|
|
||||||
case 'node':
|
case 'node':
|
||||||
router.push({ path: '/websites/runtimes/node' });
|
|
||||||
break;
|
|
||||||
case 'java':
|
case 'java':
|
||||||
router.push({ path: '/websites/runtimes/java' });
|
|
||||||
break;
|
|
||||||
case 'go':
|
case 'go':
|
||||||
router.push({ path: '/websites/runtimes/go' });
|
case 'python':
|
||||||
|
router.push({ path: '/websites/runtimes/' + app.type });
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
const params = {
|
const params = {
|
||||||
|
@ -25,5 +25,9 @@ const buttons = [
|
|||||||
label: 'Go',
|
label: 'Go',
|
||||||
path: '/websites/runtimes/go',
|
path: '/websites/runtimes/go',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Python',
|
||||||
|
path: '/websites/runtimes/python',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
267
frontend/src/views/website/runtime/python/index.vue
Normal file
267
frontend/src/views/website/runtime/python/index.vue
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<RouterMenu />
|
||||||
|
<LayoutContent :title="'Python'" v-loading="loading">
|
||||||
|
<template #prompt>
|
||||||
|
<el-alert type="info" :closable="false">
|
||||||
|
<template #title>
|
||||||
|
<span v-html="$t('runtime.statusHelper')"></span>
|
||||||
|
</template>
|
||||||
|
</el-alert>
|
||||||
|
</template>
|
||||||
|
<template #leftToolBar>
|
||||||
|
<el-button type="primary" @click="openCreate">
|
||||||
|
{{ $t('runtime.create') }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<template #main>
|
||||||
|
<ComplexTable :pagination-config="paginationConfig" :data="items" @search="search()" :heightDiff="350">
|
||||||
|
<el-table-column
|
||||||
|
:label="$t('commons.table.name')"
|
||||||
|
fix
|
||||||
|
prop="name"
|
||||||
|
min-width="120px"
|
||||||
|
show-overflow-tooltip
|
||||||
|
>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-text type="primary" class="cursor-pointer" @click="openDetail(row)">
|
||||||
|
{{ row.name }}
|
||||||
|
</el-text>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('website.runDir')" prop="codeDir">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button type="primary" link @click="toFolder(row.codeDir)">
|
||||||
|
<el-icon>
|
||||||
|
<FolderOpened />
|
||||||
|
</el-icon>
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('runtime.version')" prop="version"></el-table-column>
|
||||||
|
<el-table-column :label="$t('runtime.externalPort')" prop="port">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.port }}
|
||||||
|
<el-button link :icon="Promotion" @click="goDashboard(row.port, 'http')"></el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('commons.table.status')" prop="status">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-popover
|
||||||
|
v-if="row.status === 'error'"
|
||||||
|
placement="bottom"
|
||||||
|
:width="400"
|
||||||
|
trigger="hover"
|
||||||
|
:content="row.message"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<Status :key="row.status" :status="row.status"></Status>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
<div v-else>
|
||||||
|
<Status :key="row.status" :status="row.status"></Status>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('commons.button.log')" prop="path">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button @click="openLog(row)" link type="primary">{{ $t('website.check') }}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="createdAt"
|
||||||
|
:label="$t('commons.table.date')"
|
||||||
|
:formatter="dateFormat"
|
||||||
|
show-overflow-tooltip
|
||||||
|
min-width="120"
|
||||||
|
fix
|
||||||
|
/>
|
||||||
|
<fu-table-operations
|
||||||
|
:ellipsis="10"
|
||||||
|
width="300px"
|
||||||
|
:buttons="buttons"
|
||||||
|
:label="$t('commons.table.operate')"
|
||||||
|
fixed="right"
|
||||||
|
fix
|
||||||
|
/>
|
||||||
|
</ComplexTable>
|
||||||
|
</template>
|
||||||
|
</LayoutContent>
|
||||||
|
<Operate ref="operateRef" @close="search" />
|
||||||
|
<Delete ref="deleteRef" @close="search" />
|
||||||
|
<ComposeLogs ref="composeLogRef" />
|
||||||
|
<PortJumpDialog ref="dialogPortJumpRef" />
|
||||||
|
<AppResources ref="checkRef" @close="search" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, onUnmounted, reactive, ref } from 'vue';
|
||||||
|
import { Runtime } from '@/api/interface/runtime';
|
||||||
|
import { OperateRuntime, RuntimeDeleteCheck, SearchRuntimes, SyncRuntime } from '@/api/modules/runtime';
|
||||||
|
import { dateFormat } from '@/utils/util';
|
||||||
|
import Operate from '@/views/website/runtime/python/operate/index.vue';
|
||||||
|
import Status from '@/components/status/index.vue';
|
||||||
|
import Delete from '@/views/website/runtime/delete/index.vue';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import RouterMenu from '../index.vue';
|
||||||
|
import router from '@/routers/router';
|
||||||
|
import ComposeLogs from '@/components/compose-log/index.vue';
|
||||||
|
import { Promotion } from '@element-plus/icons-vue';
|
||||||
|
import PortJumpDialog from '@/components/port-jump/index.vue';
|
||||||
|
import AppResources from '@/views/website/runtime/php/check/index.vue';
|
||||||
|
import { ElMessageBox } from 'element-plus';
|
||||||
|
|
||||||
|
let timer: NodeJS.Timer | null = null;
|
||||||
|
const loading = ref(false);
|
||||||
|
const items = ref<Runtime.RuntimeDTO[]>([]);
|
||||||
|
const operateRef = ref();
|
||||||
|
const deleteRef = ref();
|
||||||
|
const dialogPortJumpRef = ref();
|
||||||
|
const composeLogRef = ref();
|
||||||
|
const checkRef = ref();
|
||||||
|
|
||||||
|
const paginationConfig = reactive({
|
||||||
|
cacheSizeKey: 'runtime-page-size',
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
const req = reactive<Runtime.RuntimeReq>({
|
||||||
|
name: '',
|
||||||
|
page: 1,
|
||||||
|
pageSize: 40,
|
||||||
|
type: 'python',
|
||||||
|
});
|
||||||
|
const buttons = [
|
||||||
|
{
|
||||||
|
label: i18n.global.t('container.stop'),
|
||||||
|
click: function (row: Runtime.Runtime) {
|
||||||
|
operateRuntime('down', row.id);
|
||||||
|
},
|
||||||
|
disabled: function (row: Runtime.Runtime) {
|
||||||
|
return row.status === 'recreating' || row.status === 'stopped';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('container.start'),
|
||||||
|
click: function (row: Runtime.Runtime) {
|
||||||
|
operateRuntime('up', row.id);
|
||||||
|
},
|
||||||
|
disabled: function (row: Runtime.Runtime) {
|
||||||
|
return row.status === 'starting' || row.status === 'recreating' || row.status === 'running';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('container.restart'),
|
||||||
|
click: function (row: Runtime.Runtime) {
|
||||||
|
operateRuntime('restart', row.id);
|
||||||
|
},
|
||||||
|
disabled: function (row: Runtime.Runtime) {
|
||||||
|
return row.status === 'recreating';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.edit'),
|
||||||
|
click: function (row: Runtime.Runtime) {
|
||||||
|
openDetail(row);
|
||||||
|
},
|
||||||
|
disabled: function (row: Runtime.Runtime) {
|
||||||
|
return row.status === 'recreating';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.delete'),
|
||||||
|
click: function (row: Runtime.Runtime) {
|
||||||
|
openDelete(row);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
req.page = paginationConfig.currentPage;
|
||||||
|
req.pageSize = paginationConfig.pageSize;
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await SearchRuntimes(req);
|
||||||
|
items.value = res.data.items;
|
||||||
|
paginationConfig.total = res.data.total;
|
||||||
|
} catch (error) {
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sync = () => {
|
||||||
|
SyncRuntime();
|
||||||
|
};
|
||||||
|
|
||||||
|
const openCreate = () => {
|
||||||
|
operateRef.value.acceptParams({ type: 'python', mode: 'create' });
|
||||||
|
};
|
||||||
|
|
||||||
|
const openDetail = (row: Runtime.Runtime) => {
|
||||||
|
operateRef.value.acceptParams({ type: row.type, mode: 'edit', id: row.id });
|
||||||
|
};
|
||||||
|
|
||||||
|
const openDelete = async (row: Runtime.Runtime) => {
|
||||||
|
RuntimeDeleteCheck(row.id).then(async (res) => {
|
||||||
|
const items = res.data;
|
||||||
|
if (res.data && res.data.length > 0) {
|
||||||
|
checkRef.value.acceptParams({ items: items, key: 'website', installID: row.id });
|
||||||
|
} else {
|
||||||
|
deleteRef.value.acceptParams(row.id, row.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const openLog = (row: any) => {
|
||||||
|
composeLogRef.value.acceptParams({ compose: row.path + '/docker-compose.yml', resource: row.name });
|
||||||
|
};
|
||||||
|
|
||||||
|
const goDashboard = async (port: any, protocol: string) => {
|
||||||
|
dialogPortJumpRef.value.acceptParams({ port: port, protocol: protocol });
|
||||||
|
};
|
||||||
|
|
||||||
|
const operateRuntime = async (operate: string, ID: number) => {
|
||||||
|
try {
|
||||||
|
const action = await ElMessageBox.confirm(
|
||||||
|
i18n.global.t('runtime.operatorHelper', [i18n.global.t('commons.operate.' + operate)]),
|
||||||
|
i18n.global.t('commons.operate.' + operate),
|
||||||
|
{
|
||||||
|
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||||
|
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||||
|
type: 'info',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (action === 'confirm') {
|
||||||
|
loading.value = true;
|
||||||
|
await OperateRuntime({ operate: operate, ID: ID });
|
||||||
|
search();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toFolder = (folder: string) => {
|
||||||
|
router.push({ path: '/hosts/files', query: { path: folder } });
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
sync();
|
||||||
|
search();
|
||||||
|
timer = setInterval(() => {
|
||||||
|
search();
|
||||||
|
sync();
|
||||||
|
}, 1000 * 10);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearInterval(Number(timer));
|
||||||
|
timer = null;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
332
frontend/src/views/website/runtime/python/operate/index.vue
Normal file
332
frontend/src/views/website/runtime/python/operate/index.vue
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
<template>
|
||||||
|
<DrawerPro
|
||||||
|
v-model="open"
|
||||||
|
:header="$t('runtime.' + mode)"
|
||||||
|
size="large"
|
||||||
|
:resource="mode === 'edit' ? runtime.name : ''"
|
||||||
|
:back="handleClose"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
v-loading="loading"
|
||||||
|
ref="runtimeForm"
|
||||||
|
label-position="top"
|
||||||
|
:model="runtime"
|
||||||
|
label-width="125px"
|
||||||
|
:rules="rules"
|
||||||
|
:validate-on-rule-change="false"
|
||||||
|
>
|
||||||
|
<el-form-item :label="$t('commons.table.name')" prop="name">
|
||||||
|
<el-input :disabled="mode === 'edit'" v-model="runtime.name"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('runtime.app')" prop="appID">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-select
|
||||||
|
v-model="runtime.appID"
|
||||||
|
:disabled="mode === 'edit'"
|
||||||
|
@change="changeApp(runtime.appID)"
|
||||||
|
class="p-w-200"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="(app, index) in apps"
|
||||||
|
:key="index"
|
||||||
|
:label="app.name"
|
||||||
|
:value="app.id"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-select
|
||||||
|
v-model="runtime.version"
|
||||||
|
:disabled="mode === 'edit'"
|
||||||
|
@change="changeVersion()"
|
||||||
|
class="p-w-200"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="(version, index) in appVersions"
|
||||||
|
:key="index"
|
||||||
|
:label="version"
|
||||||
|
:value="version"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('tool.supervisor.dir')" prop="codeDir">
|
||||||
|
<el-input v-model.trim="runtime.codeDir" :disabled="mode === 'edit'">
|
||||||
|
<template #prepend>
|
||||||
|
<FileList
|
||||||
|
:disabled="mode === 'edit'"
|
||||||
|
:path="runtime.codeDir"
|
||||||
|
@choose="getPath"
|
||||||
|
:dir="true"
|
||||||
|
></FileList>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="18">
|
||||||
|
<el-form-item :label="$t('runtime.runScript')" prop="params.EXEC_SCRIPT">
|
||||||
|
<el-input v-model="runtime.params['EXEC_SCRIPT']"></el-input>
|
||||||
|
<span class="input-help">
|
||||||
|
{{ $t('runtime.pythonHelper') }}
|
||||||
|
</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<PortConfig :params="runtime.params" :exposedPorts="runtime.exposedPorts" :rules="rules" />
|
||||||
|
<Environment :environments="runtime.environments" />
|
||||||
|
<Volumes :volumes="runtime.volumes" />
|
||||||
|
<el-form-item :label="$t('app.containerName')" prop="params.CONTAINER_NAME">
|
||||||
|
<el-input v-model.trim="runtime.params['CONTAINER_NAME']"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
|
<el-button type="primary" @click="submit(runtimeForm)" :disabled="loading">
|
||||||
|
{{ $t('commons.button.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</DrawerPro>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { App } from '@/api/interface/app';
|
||||||
|
import { Runtime } from '@/api/interface/runtime';
|
||||||
|
import { GetApp, GetAppDetail, SearchApp } from '@/api/modules/app';
|
||||||
|
import { CreateRuntime, GetRuntime, UpdateRuntime } from '@/api/modules/runtime';
|
||||||
|
import { Rules, checkNumberRange } from '@/global/form-rules';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||||
|
import { FormInstance } from 'element-plus';
|
||||||
|
import { reactive, ref, watch } from 'vue';
|
||||||
|
import PortConfig from '@/views/website/runtime/port/index.vue';
|
||||||
|
import Environment from '@/views/website/runtime/environment/index.vue';
|
||||||
|
import Volumes from '@/views/website/runtime/volume/index.vue';
|
||||||
|
|
||||||
|
interface OperateRrops {
|
||||||
|
id?: number;
|
||||||
|
mode: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const open = ref(false);
|
||||||
|
const apps = ref<App.App[]>([]);
|
||||||
|
const runtimeForm = ref<FormInstance>();
|
||||||
|
const loading = ref(false);
|
||||||
|
const mode = ref('create');
|
||||||
|
const editParams = ref<App.InstallParams[]>();
|
||||||
|
const appVersions = ref<string[]>([]);
|
||||||
|
const appReq = reactive({
|
||||||
|
type: 'python',
|
||||||
|
page: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
resource: 'remote',
|
||||||
|
});
|
||||||
|
const initData = (type: string) => ({
|
||||||
|
name: '',
|
||||||
|
appDetailID: undefined,
|
||||||
|
image: '',
|
||||||
|
params: {
|
||||||
|
HOST_IP: '0.0.0.0',
|
||||||
|
},
|
||||||
|
type: type,
|
||||||
|
resource: 'appstore',
|
||||||
|
rebuild: false,
|
||||||
|
codeDir: '/',
|
||||||
|
port: 8080,
|
||||||
|
exposedPorts: [],
|
||||||
|
environments: [],
|
||||||
|
volumes: [],
|
||||||
|
});
|
||||||
|
let runtime = reactive<Runtime.RuntimeCreate>(initData('python'));
|
||||||
|
const rules = ref<any>({
|
||||||
|
name: [Rules.requiredInput, Rules.appName],
|
||||||
|
appID: [Rules.requiredSelect],
|
||||||
|
codeDir: [Rules.requiredInput],
|
||||||
|
port: [Rules.requiredInput, Rules.paramPort, checkNumberRange(1, 65535)],
|
||||||
|
source: [Rules.requiredSelect],
|
||||||
|
params: {
|
||||||
|
APP_PORT: [Rules.requiredInput, Rules.paramPort, checkNumberRange(1, 65535)],
|
||||||
|
HOST_IP: [Rules.requiredSelect],
|
||||||
|
CONTAINER_NAME: [Rules.requiredInput, Rules.containerName],
|
||||||
|
EXEC_SCRIPT: [Rules.requiredInput],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const scripts = ref<Runtime.NodeScripts[]>([]);
|
||||||
|
const em = defineEmits(['close']);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => runtime.params['APP_PORT'],
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal && mode.value == 'create') {
|
||||||
|
runtime.port = newVal;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => runtime.name,
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal && mode.value == 'create') {
|
||||||
|
runtime.params['CONTAINER_NAME'] = newVal;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
open.value = false;
|
||||||
|
em('close', false);
|
||||||
|
runtimeForm.value?.resetFields();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPath = (codeDir: string) => {
|
||||||
|
runtime.codeDir = codeDir;
|
||||||
|
};
|
||||||
|
|
||||||
|
const searchApp = (appID: number) => {
|
||||||
|
SearchApp(appReq).then((res) => {
|
||||||
|
apps.value = res.data.items || [];
|
||||||
|
if (res.data && res.data.items && res.data.items.length > 0) {
|
||||||
|
if (appID == null) {
|
||||||
|
runtime.appID = res.data.items[0].id;
|
||||||
|
getApp(res.data.items[0].key, mode.value);
|
||||||
|
} else {
|
||||||
|
res.data.items.forEach((item) => {
|
||||||
|
if (item.id === appID) {
|
||||||
|
getApp(item.key, mode.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeApp = (appID: number) => {
|
||||||
|
for (const app of apps.value) {
|
||||||
|
if (app.id === appID) {
|
||||||
|
getApp(app.key, mode.value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeVersion = () => {
|
||||||
|
loading.value = true;
|
||||||
|
GetAppDetail(runtime.appID, runtime.version, 'runtime')
|
||||||
|
.then((res) => {
|
||||||
|
runtime.appDetailID = res.data.id;
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getApp = (appkey: string, mode: string) => {
|
||||||
|
GetApp(appkey).then((res) => {
|
||||||
|
appVersions.value = res.data.versions || [];
|
||||||
|
if (res.data.versions.length > 0) {
|
||||||
|
if (mode === 'create') {
|
||||||
|
runtime.version = res.data.versions[0];
|
||||||
|
changeVersion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const submit = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
await formEl.validate((valid) => {
|
||||||
|
if (!valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (runtime.exposedPorts && runtime.exposedPorts.length > 0) {
|
||||||
|
const containerPortMap = new Map();
|
||||||
|
const hostPortMap = new Map();
|
||||||
|
containerPortMap[runtime.params['APP_PORT']] = true;
|
||||||
|
hostPortMap[runtime.port] = true;
|
||||||
|
for (const port of runtime.exposedPorts) {
|
||||||
|
if (containerPortMap[port.containerPort]) {
|
||||||
|
MsgError(i18n.global.t('runtime.portError'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (hostPortMap[port.hostPort]) {
|
||||||
|
MsgError(i18n.global.t('runtime.portError'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hostPortMap[port.hostPort] = true;
|
||||||
|
containerPortMap[port.containerPort] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode.value == 'create') {
|
||||||
|
loading.value = true;
|
||||||
|
CreateRuntime(runtime)
|
||||||
|
.then(() => {
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
|
||||||
|
handleClose();
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
loading.value = true;
|
||||||
|
UpdateRuntime(runtime)
|
||||||
|
.then(() => {
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
|
||||||
|
handleClose();
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRuntime = async (id: number) => {
|
||||||
|
try {
|
||||||
|
const res = await GetRuntime(id);
|
||||||
|
const data = res.data;
|
||||||
|
Object.assign(runtime, {
|
||||||
|
id: data.id,
|
||||||
|
name: data.name,
|
||||||
|
appDetailId: data.appDetailID,
|
||||||
|
image: data.image,
|
||||||
|
type: data.type,
|
||||||
|
resource: data.resource,
|
||||||
|
appID: data.appID,
|
||||||
|
version: data.version,
|
||||||
|
rebuild: true,
|
||||||
|
source: data.source,
|
||||||
|
params: data.params,
|
||||||
|
codeDir: data.codeDir,
|
||||||
|
port: data.port,
|
||||||
|
});
|
||||||
|
runtime.exposedPorts = data.exposedPorts || [];
|
||||||
|
runtime.environments = data.environments || [];
|
||||||
|
runtime.volumes = data.volumes || [];
|
||||||
|
editParams.value = data.appParams;
|
||||||
|
searchApp(data.appID);
|
||||||
|
open.value = true;
|
||||||
|
} catch (error) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const acceptParams = async (props: OperateRrops) => {
|
||||||
|
mode.value = props.mode;
|
||||||
|
scripts.value = [];
|
||||||
|
if (props.mode === 'create') {
|
||||||
|
Object.assign(runtime, initData(props.type));
|
||||||
|
searchApp(null);
|
||||||
|
open.value = true;
|
||||||
|
} else {
|
||||||
|
getRuntime(props.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
Loading…
Reference in New Issue
Block a user