mirror of
https://gitee.com/fit2cloud-feizhiyun/1Panel.git
synced 2024-12-05 13:29:11 +08:00
feat: 增加 java 运行环境 (#5576)
This commit is contained in:
parent
151f9d58d3
commit
eded682ecc
@ -154,7 +154,7 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if runtime.Type == constant.RuntimeNode {
|
||||
if runtime.Type == constant.RuntimeNode || runtime.Type == constant.RuntimeJava {
|
||||
if err := handleRuntimeRecover(runtime, fmt.Sprintf("%s/%s.runtime.tar.gz", tmpPath, website.Alias), true, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -225,7 +225,7 @@ func handleWebsiteBackup(website *model.Website, backupDir, fileName string, exc
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if runtime.Type == constant.RuntimeNode {
|
||||
if runtime.Type == constant.RuntimeNode || runtime.Type == constant.RuntimeJava {
|
||||
if err := handleRuntimeBackup(runtime, tmpDir, fmt.Sprintf("%s.runtime.tar.gz", website.Alias), excludes, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e
|
||||
if exist != nil {
|
||||
return nil, buserr.New(constant.ErrImageExist)
|
||||
}
|
||||
case constant.RuntimeNode:
|
||||
case constant.RuntimeNode, constant.RuntimeJava:
|
||||
if !fileOp.Stat(create.CodeDir) {
|
||||
return nil, buserr.New(constant.ErrPathNotFound)
|
||||
}
|
||||
@ -133,9 +133,9 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e
|
||||
if err = handlePHP(create, runtime, fileOp, appVersionDir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case constant.RuntimeNode:
|
||||
case constant.RuntimeNode, constant.RuntimeJava:
|
||||
runtime.Port = create.Port
|
||||
if err = handleNode(create, runtime, fileOp, appVersionDir); err != nil {
|
||||
if err = handleNodeAndJava(create, runtime, fileOp, appVersionDir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@ -217,7 +217,7 @@ func (r *RuntimeService) Delete(runtimeDelete request.RuntimeDelete) error {
|
||||
global.LOG.Errorf("delete image id [%s] error %v", imageID, err)
|
||||
}
|
||||
}
|
||||
case constant.RuntimeNode:
|
||||
case constant.RuntimeNode, constant.RuntimeJava:
|
||||
if out, err := compose.Down(runtime.GetComposePath()); err != nil && !runtimeDelete.ForceDelete {
|
||||
if out != "" {
|
||||
return errors.New(out)
|
||||
@ -300,7 +300,7 @@ func (r *RuntimeService) Get(id uint) (*response.RuntimeDTO, error) {
|
||||
}
|
||||
}
|
||||
res.AppParams = appParams
|
||||
case constant.RuntimeNode:
|
||||
case constant.RuntimeNode, constant.RuntimeJava:
|
||||
res.Params = make(map[string]interface{})
|
||||
envs, err := gotenv.Unmarshal(runtime.Env)
|
||||
if err != nil {
|
||||
@ -308,7 +308,7 @@ func (r *RuntimeService) Get(id uint) (*response.RuntimeDTO, error) {
|
||||
}
|
||||
for k, v := range envs {
|
||||
switch k {
|
||||
case "NODE_APP_PORT", "PANEL_APP_PORT_HTTP":
|
||||
case "NODE_APP_PORT", "PANEL_APP_PORT_HTTP", "JAVA_APP_PORT":
|
||||
port, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -361,7 +361,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
||||
if exist != nil {
|
||||
return buserr.New(constant.ErrImageExist)
|
||||
}
|
||||
case constant.RuntimeNode:
|
||||
case constant.RuntimeNode, constant.RuntimeJava:
|
||||
if runtime.Port != req.Port {
|
||||
if err = checkPortExist(req.Port); err != nil {
|
||||
return err
|
||||
@ -441,7 +441,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
||||
return err
|
||||
}
|
||||
go buildRuntime(runtime, imageID, req.Rebuild)
|
||||
case constant.RuntimeNode:
|
||||
case constant.RuntimeNode, constant.RuntimeJava:
|
||||
runtime.Version = req.Version
|
||||
runtime.CodeDir = req.CodeDir
|
||||
runtime.Port = req.Port
|
||||
@ -610,7 +610,7 @@ func (r *RuntimeService) SyncRuntimeStatus() error {
|
||||
return err
|
||||
}
|
||||
for _, runtime := range runtimes {
|
||||
if runtime.Type == constant.RuntimeNode {
|
||||
if runtime.Type == constant.RuntimeNode || runtime.Type == constant.RuntimeJava {
|
||||
_ = SyncRuntimeContainerStatus(&runtime)
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func handleNode(create request.RuntimeCreate, runtime *model.Runtime, fileOp files.FileOp, appVersionDir string) (err error) {
|
||||
func handleNodeAndJava(create request.RuntimeCreate, runtime *model.Runtime, fileOp files.FileOp, appVersionDir string) (err error) {
|
||||
runtimeDir := path.Join(constant.RuntimeDir, create.Type)
|
||||
if err = fileOp.CopyDir(appVersionDir, runtimeDir); err != nil {
|
||||
return
|
||||
@ -318,7 +318,15 @@ func handleParams(create request.RuntimeCreate, projectDir string) (composeConte
|
||||
}
|
||||
create.Params["CONTAINER_PACKAGE_URL"] = create.Source
|
||||
|
||||
composeContent, err = handleNodeCompose(env, composeContent, create, projectDir)
|
||||
composeContent, err = handleCompose(env, composeContent, create, projectDir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case constant.RuntimeJava:
|
||||
create.Params["CODE_DIR"] = create.CodeDir
|
||||
create.Params["JAVA_VERSION"] = create.Version
|
||||
create.Params["PANEL_APP_PORT_HTTP"] = create.Port
|
||||
composeContent, err = handleCompose(env, composeContent, create, projectDir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -341,7 +349,7 @@ func handleParams(create request.RuntimeCreate, projectDir string) (composeConte
|
||||
return
|
||||
}
|
||||
|
||||
func handleNodeCompose(env gotenv.Env, composeContent []byte, create request.RuntimeCreate, projectDir string) (composeByte []byte, err error) {
|
||||
func handleCompose(env gotenv.Env, composeContent []byte, create request.RuntimeCreate, projectDir string) (composeByte []byte, err error) {
|
||||
existMap := make(map[string]interface{})
|
||||
composeMap := make(map[string]interface{})
|
||||
if err = yaml.Unmarshal(composeContent, &composeMap); err != nil {
|
||||
@ -360,7 +368,14 @@ func handleNodeCompose(env gotenv.Env, composeContent []byte, create request.Run
|
||||
_, ok := serviceValue["ports"].([]interface{})
|
||||
if ok {
|
||||
var ports []interface{}
|
||||
ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${NODE_APP_PORT}")
|
||||
|
||||
switch create.Type {
|
||||
case constant.RuntimeNode:
|
||||
ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${NODE_APP_PORT}")
|
||||
case constant.RuntimeJava:
|
||||
ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${JAVA_APP_PORT}")
|
||||
}
|
||||
|
||||
for i, port := range create.ExposedPorts {
|
||||
containerPortStr := fmt.Sprintf("CONTAINER_PORT_%d", i)
|
||||
hostPortStr := fmt.Sprintf("HOST_PORT_%d", i)
|
||||
|
@ -336,7 +336,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
||||
}
|
||||
website.Proxy = proxy
|
||||
}
|
||||
case constant.RuntimeNode:
|
||||
case constant.RuntimeNode, constant.RuntimeJava:
|
||||
website.Proxy = fmt.Sprintf("127.0.0.1:%d", runtime.Port)
|
||||
}
|
||||
}
|
||||
@ -540,9 +540,7 @@ func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate)
|
||||
}
|
||||
for _, domain := range domainModels {
|
||||
wafSite.Domains = append(wafSite.Domains, domain.Domain)
|
||||
if domain.Port != 80 && domain.Port != 443 {
|
||||
wafSite.Host = append(wafSite.Host, domain.Domain+":"+string(rune(domain.Port)))
|
||||
}
|
||||
wafSite.Host = append(wafSite.Host, domain.Domain+":"+strconv.Itoa(domain.Port))
|
||||
}
|
||||
if len(wafSite.Host) == 0 {
|
||||
wafSite.Host = []string{}
|
||||
@ -634,7 +632,7 @@ func (w WebsiteService) DeleteWebsiteDomain(domainId uint) error {
|
||||
oldHostArray := wafSite.Host
|
||||
var newHostArray []string
|
||||
for _, host := range oldHostArray {
|
||||
if host == webSiteDomain.Domain+":"+string(rune(webSiteDomain.Port)) {
|
||||
if host == webSiteDomain.Domain+":"+strconv.Itoa(webSiteDomain.Port) {
|
||||
continue
|
||||
}
|
||||
newHostArray = append(newHostArray, host)
|
||||
|
@ -276,7 +276,7 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
|
||||
server.UpdateRoot(rootIndex)
|
||||
server.UpdatePHPProxy([]string{website.Proxy}, "")
|
||||
}
|
||||
case constant.RuntimeNode:
|
||||
case constant.RuntimeNode, constant.RuntimeJava:
|
||||
proxy := fmt.Sprintf("http://127.0.0.1:%d", runtime.Port)
|
||||
server.UpdateRootProxy([]string{proxy})
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ const (
|
||||
|
||||
RuntimePHP = "php"
|
||||
RuntimeNode = "node"
|
||||
RuntimeJava = "java"
|
||||
|
||||
RuntimeProxyUnix = "unix"
|
||||
RuntimeProxyTcp = "tcp"
|
||||
|
@ -2302,6 +2302,7 @@ const message = {
|
||||
customScriptHelper: 'Please fill in the complete startup command, for example: npm run start',
|
||||
portError: 'Cannot fill in the same port',
|
||||
systemRestartHelper: 'Status description: Interruption - status acquisition failed due to system restart',
|
||||
javaScriptHelper: 'Please fill in the complete startup command, for example: java -jar halo.jar',
|
||||
},
|
||||
process: {
|
||||
pid: 'Process ID',
|
||||
|
@ -2138,6 +2138,7 @@ const message = {
|
||||
customScriptHelper: '請填寫完整的啟動指令,例如:npm run start',
|
||||
portError: '不能填寫相同連接埠',
|
||||
systemRestartHelper: '狀態說明:中斷-系統重新啟動導致狀態取得失敗',
|
||||
javaScriptHelper: '請填寫完整啟動指令,例如:java -jar halo.jar',
|
||||
},
|
||||
process: {
|
||||
pid: '進程ID',
|
||||
|
@ -2141,6 +2141,7 @@ const message = {
|
||||
customScriptHelper: '请填写完整的启动命令,例如:npm run start',
|
||||
portError: '不能填写相同端口',
|
||||
systemRestartHelper: '状态说明:中断-系统重启导致状态获取失败',
|
||||
javaScriptHelper: '请填写完整启动命令,例如:java -jar halo.jar',
|
||||
},
|
||||
process: {
|
||||
pid: '进程ID',
|
||||
|
@ -58,6 +58,16 @@ const webSiteRouter = {
|
||||
requiresAuth: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/websites/runtimes/java',
|
||||
name: 'java',
|
||||
hidden: true,
|
||||
component: () => import('@/views/website/runtime/java/index.vue'),
|
||||
meta: {
|
||||
activeMenu: '/websites/runtimes/java',
|
||||
requiresAuth: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
@ -238,6 +238,9 @@ const openInstall = (app: App.App) => {
|
||||
case 'node':
|
||||
router.push({ path: '/websites/runtimes/node' });
|
||||
break;
|
||||
case 'java':
|
||||
router.push({ path: '/websites/runtimes/java' });
|
||||
break;
|
||||
default:
|
||||
const params = {
|
||||
app: app,
|
||||
|
@ -13,6 +13,10 @@ const buttons = [
|
||||
label: 'PHP',
|
||||
path: '/websites/runtimes/php',
|
||||
},
|
||||
{
|
||||
label: 'Java',
|
||||
path: '/websites/runtimes/java',
|
||||
},
|
||||
{
|
||||
label: 'Node.js',
|
||||
path: '/websites/runtimes/node',
|
||||
|
296
frontend/src/views/website/runtime/java/index.vue
Normal file
296
frontend/src/views/website/runtime/java/index.vue
Normal file
@ -0,0 +1,296 @@
|
||||
<template>
|
||||
<div>
|
||||
<RouterMenu />
|
||||
<LayoutContent :title="'Java'" 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 #toolbar>
|
||||
<el-button type="primary" @click="openCreate">
|
||||
{{ $t('runtime.create') }}
|
||||
</el-button>
|
||||
|
||||
<el-button type="primary" plain @click="onOpenBuildCache()">
|
||||
{{ $t('container.cleanBuildCache') }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #main>
|
||||
<ComplexTable :pagination-config="paginationConfig" :data="items" @search="search()">
|
||||
<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('runtime.codeDir')" 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>
|
||||
<OperateJava 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 OperateJava from '@/views/website/runtime/java/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';
|
||||
import { containerPrune } from '@/api/modules/container';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
|
||||
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: 'java',
|
||||
});
|
||||
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: 'java', 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 onOpenBuildCache = () => {
|
||||
ElMessageBox.confirm(i18n.global.t('container.delBuildCacheHelper'), i18n.global.t('container.cleanBuildCache'), {
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
type: 'info',
|
||||
}).then(async () => {
|
||||
loading.value = true;
|
||||
let params = {
|
||||
pruneType: 'buildcache',
|
||||
withTagAll: false,
|
||||
};
|
||||
await containerPrune(params)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
MsgSuccess(i18n.global.t('container.cleanSuccess', [res.data.deletedNumber]));
|
||||
search();
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
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>
|
395
frontend/src/views/website/runtime/java/operate/index.vue
Normal file
395
frontend/src/views/website/runtime/java/operate/index.vue
Normal file
@ -0,0 +1,395 @@
|
||||
<template>
|
||||
<el-drawer :close-on-click-modal="false" :close-on-press-escape="false" v-model="open" size="50%">
|
||||
<template #header>
|
||||
<DrawerHeader
|
||||
:header="$t('runtime.' + mode)"
|
||||
:hideResource="mode == 'create'"
|
||||
:resource="runtime.name"
|
||||
:back="handleClose"
|
||||
/>
|
||||
</template>
|
||||
<el-row v-loading="loading">
|
||||
<el-col :span="22" :offset="1">
|
||||
<el-form
|
||||
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('runtime.codeDir')" 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.customScriptHelper') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="7">
|
||||
<el-form-item :label="$t('runtime.appPort')" prop="params.JAVA_APP_PORT">
|
||||
<el-input v-model.number="runtime.params['JAVA_APP_PORT']" />
|
||||
<span class="input-help">{{ $t('runtime.appPortHelper') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<el-form-item :label="$t('runtime.externalPort')" prop="port">
|
||||
<el-input v-model.number="runtime.port" />
|
||||
<span class="input-help">{{ $t('runtime.externalPortHelper') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-form-item :label="$t('commons.button.add') + $t('commons.table.port')">
|
||||
<el-button @click="addPort">
|
||||
<el-icon><Plus /></el-icon>
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item :label="$t('app.allowPort')" prop="params.HOST_IP">
|
||||
<el-switch
|
||||
v-model="runtime.params['HOST_IP']"
|
||||
:active-value="'0.0.0.0'"
|
||||
:inactive-value="'127.0.0.1'"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20" v-for="(port, index) of runtime.exposedPorts" :key="index">
|
||||
<el-col :span="7">
|
||||
<el-form-item
|
||||
:prop="'exposedPorts.' + index + '.containerPort'"
|
||||
:rules="rules.params.JAVA_APP_PORT"
|
||||
>
|
||||
<el-input v-model.number="port.containerPort" :placeholder="$t('runtime.appPort')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<el-form-item
|
||||
:prop="'exposedPorts.' + index + '.hostPort'"
|
||||
:rules="rules.params.JAVA_APP_PORT"
|
||||
>
|
||||
<el-input v-model.number="port.hostPort" :placeholder="$t('runtime.externalPort')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="removePort(index)" link>
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<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>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<template #footer>
|
||||
<span>
|
||||
<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>
|
||||
</span>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</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 DrawerHeader from '@/components/drawer-header/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: 'java',
|
||||
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: [],
|
||||
});
|
||||
let runtime = reactive<Runtime.RuntimeCreate>(initData('java'));
|
||||
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: {
|
||||
JAVA_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['JAVA_APP_PORT'],
|
||||
(newVal) => {
|
||||
if (newVal && mode.value == 'create') {
|
||||
runtime.port = newVal;
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => runtime.name,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
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 addPort = () => {
|
||||
runtime.exposedPorts.push({
|
||||
hostPort: undefined,
|
||||
containerPort: undefined,
|
||||
});
|
||||
};
|
||||
|
||||
const removePort = (index: number) => {
|
||||
runtime.exposedPorts.splice(index, 1);
|
||||
};
|
||||
|
||||
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['JAVA_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 || [];
|
||||
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>
|
@ -163,6 +163,7 @@
|
||||
<el-select v-model="website.runtimeType" @change="changeRuntimeType()">
|
||||
<el-option label="PHP" value="php"></el-option>
|
||||
<el-option label="Node.js" value="node"></el-option>
|
||||
<el-option label="Java" value="java"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -640,7 +641,7 @@ const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!flag) {
|
||||
MsgError(i18n.global.t('website.containWarn'));
|
||||
loading.value = false;
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
PreCheck({})
|
||||
.then((res) => {
|
||||
|
Loading…
Reference in New Issue
Block a user