mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 14:08:06 +08:00
fix: 修改镜像构建和编排创建路径限制,增加 config 校验 (#342)
fix: 修改镜像构建和编排创建路径限制,增加 config 校验
This commit is contained in:
parent
68a457ae89
commit
6ee9789a2f
@ -70,6 +70,34 @@ func (b *BaseApi) SearchCompose(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Container Compose
|
||||||
|
// @Summary Test compose
|
||||||
|
// @Description 测试 compose 是否可用
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.ComposeCreate true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /containers/compose/test [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"检测 compose [name] 格式","formatEN":"check compose [name]"}
|
||||||
|
func (b *BaseApi) TestCompose(c *gin.Context) {
|
||||||
|
var req dto.ComposeCreate
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := global.VALID.Struct(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isOK, err := containerService.TestCompose(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, isOK)
|
||||||
|
}
|
||||||
|
|
||||||
// @Tags Container Compose
|
// @Tags Container Compose
|
||||||
// @Summary Create compose
|
// @Summary Create compose
|
||||||
// @Description 创建容器编排
|
// @Description 创建容器编排
|
||||||
|
@ -125,40 +125,28 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
|||||||
return int64(total), BackDatas, nil
|
return int64(total), BackDatas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error) {
|
func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) {
|
||||||
if req.From == "template" {
|
if err := u.loadPath(&req); err != nil {
|
||||||
template, err := composeRepo.Get(commonRepo.WithByID(req.Template))
|
return false, err
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
req.From = "edit"
|
|
||||||
req.File = template.Content
|
|
||||||
}
|
}
|
||||||
if req.From == "edit" {
|
cmd := exec.Command("docker-compose", "-f", req.Path, "config")
|
||||||
dir := fmt.Sprintf("%s/docker/compose/%s", constant.DataDir, req.Name)
|
stdout, err := cmd.CombinedOutput()
|
||||||
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
if err != nil {
|
||||||
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
|
return false, errors.New(string(stdout))
|
||||||
return "", err
|
}
|
||||||
}
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
path := fmt.Sprintf("%s/docker-compose.yml", dir)
|
func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error) {
|
||||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
if err := u.loadPath(&req); err != nil {
|
||||||
if err != nil {
|
return "", err
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
write := bufio.NewWriter(file)
|
|
||||||
_, _ = write.WriteString(string(req.File))
|
|
||||||
write.Flush()
|
|
||||||
req.Path = path
|
|
||||||
}
|
}
|
||||||
global.LOG.Infof("docker-compose.yml %s create successful, start to docker-compose up", req.Name)
|
global.LOG.Infof("docker-compose.yml %s create successful, start to docker-compose up", req.Name)
|
||||||
|
|
||||||
if req.From == "path" {
|
if req.From == "path" {
|
||||||
req.Name = path.Base(strings.ReplaceAll(req.Path, "/docker-compose.yml", ""))
|
req.Name = path.Base(strings.ReplaceAll(req.Path, "/"+path.Base(req.Path), ""))
|
||||||
}
|
}
|
||||||
logName := strings.ReplaceAll(req.Path, "docker-compose.yml", "compose.log")
|
logName := path.Dir(req.Path) + "/compose.log"
|
||||||
file, err := os.OpenFile(logName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
file, err := os.OpenFile(logName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -221,3 +209,34 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *ContainerService) loadPath(req *dto.ComposeCreate) error {
|
||||||
|
if req.From == "template" {
|
||||||
|
template, err := composeRepo.Get(commonRepo.WithByID(req.Template))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.From = "edit"
|
||||||
|
req.File = template.Content
|
||||||
|
}
|
||||||
|
if req.From == "edit" {
|
||||||
|
dir := fmt.Sprintf("%s/docker/compose/%s", constant.DataDir, req.Name)
|
||||||
|
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
||||||
|
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/docker-compose.yml", dir)
|
||||||
|
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
write := bufio.NewWriter(file)
|
||||||
|
_, _ = write.WriteString(string(req.File))
|
||||||
|
write.Flush()
|
||||||
|
req.Path = path
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -122,6 +122,7 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
fileName := "Dockerfile"
|
||||||
if req.From == "edit" {
|
if req.From == "edit" {
|
||||||
dir := fmt.Sprintf("%s/docker/build/%s", constant.DataDir, strings.ReplaceAll(req.Name, ":", "_"))
|
dir := fmt.Sprintf("%s/docker/build/%s", constant.DataDir, strings.ReplaceAll(req.Name, ":", "_"))
|
||||||
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
||||||
@ -141,7 +142,8 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
|
|||||||
write.Flush()
|
write.Flush()
|
||||||
req.Dockerfile = dir
|
req.Dockerfile = dir
|
||||||
} else {
|
} else {
|
||||||
req.Dockerfile = strings.ReplaceAll(req.Dockerfile, "/Dockerfile", "")
|
fileName = path.Base(req.Dockerfile)
|
||||||
|
req.Dockerfile = path.Dir(req.Dockerfile)
|
||||||
}
|
}
|
||||||
tar, err := archive.TarWithOptions(req.Dockerfile+"/", &archive.TarOptions{})
|
tar, err := archive.TarWithOptions(req.Dockerfile+"/", &archive.TarOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -149,7 +151,7 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
opts := types.ImageBuildOptions{
|
opts := types.ImageBuildOptions{
|
||||||
Dockerfile: "Dockerfile",
|
Dockerfile: fileName,
|
||||||
Tags: []string{req.Name},
|
Tags: []string{req.Name},
|
||||||
Remove: true,
|
Remove: true,
|
||||||
Labels: stringsToMap(req.Tags),
|
Labels: stringsToMap(req.Tags),
|
||||||
|
@ -33,6 +33,7 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) {
|
|||||||
|
|
||||||
baRouter.POST("/compose/search", baseApi.SearchCompose)
|
baRouter.POST("/compose/search", baseApi.SearchCompose)
|
||||||
baRouter.POST("/compose", baseApi.CreateCompose)
|
baRouter.POST("/compose", baseApi.CreateCompose)
|
||||||
|
baRouter.POST("/compose/test", baseApi.TestCompose)
|
||||||
baRouter.POST("/compose/operate", baseApi.OperatorCompose)
|
baRouter.POST("/compose/operate", baseApi.OperatorCompose)
|
||||||
baRouter.POST("/compose/update", baseApi.ComposeUpdate)
|
baRouter.POST("/compose/update", baseApi.ComposeUpdate)
|
||||||
|
|
||||||
|
@ -1038,6 +1038,48 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/containers/compose/test": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "测试 compose 是否可用",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Container Compose"
|
||||||
|
],
|
||||||
|
"summary": "Test compose",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.ComposeCreate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFuntions": [],
|
||||||
|
"bodyKeys": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"formatEN": "create compose [name]",
|
||||||
|
"formatZH": "创建 compose [name]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/containers/compose/update": {
|
"/containers/compose/update": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
|
@ -1031,6 +1031,48 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/containers/compose/test": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "测试 compose 是否可用",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Container Compose"
|
||||||
|
],
|
||||||
|
"summary": "Test compose",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.ComposeCreate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFuntions": [],
|
||||||
|
"bodyKeys": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"formatEN": "create compose [name]",
|
||||||
|
"formatZH": "创建 compose [name]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/containers/compose/update": {
|
"/containers/compose/update": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
|
@ -3286,6 +3286,33 @@ paths:
|
|||||||
summary: Page composes
|
summary: Page composes
|
||||||
tags:
|
tags:
|
||||||
- Container Compose
|
- Container Compose
|
||||||
|
/containers/compose/test:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 测试 compose 是否可用
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.ComposeCreate'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: ""
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Test compose
|
||||||
|
tags:
|
||||||
|
- Container Compose
|
||||||
|
x-panel-log:
|
||||||
|
BeforeFuntions: []
|
||||||
|
bodyKeys:
|
||||||
|
- name
|
||||||
|
formatEN: create compose [name]
|
||||||
|
formatZH: 创建 compose [name]
|
||||||
|
paramKeys: []
|
||||||
/containers/compose/update:
|
/containers/compose/update:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
|
@ -117,7 +117,10 @@ export const searchCompose = (params: SearchWithPage) => {
|
|||||||
return http.post<ResPage<Container.ComposeInfo>>(`/containers/compose/search`, params);
|
return http.post<ResPage<Container.ComposeInfo>>(`/containers/compose/search`, params);
|
||||||
};
|
};
|
||||||
export const upCompose = (params: Container.ComposeCreate) => {
|
export const upCompose = (params: Container.ComposeCreate) => {
|
||||||
return http.post<string>(`/containers/compose`, params, 600000);
|
return http.post<string>(`/containers/compose`, params);
|
||||||
|
};
|
||||||
|
export const testCompose = (params: Container.ComposeCreate) => {
|
||||||
|
return http.post<boolean>(`/containers/compose/test`, params);
|
||||||
};
|
};
|
||||||
export const composeOperator = (params: Container.ComposeOpration) => {
|
export const composeOperator = (params: Container.ComposeOpration) => {
|
||||||
return http.post(`/containers/compose/operate`, params);
|
return http.post(`/containers/compose/operate`, params);
|
||||||
|
@ -521,6 +521,8 @@ const message = {
|
|||||||
registrieHelper: 'One in a row, for example:\n172.16.10.111:8081 \n172.16.10.112:8081',
|
registrieHelper: 'One in a row, for example:\n172.16.10.111:8081 \n172.16.10.112:8081',
|
||||||
|
|
||||||
compose: 'Compose',
|
compose: 'Compose',
|
||||||
|
composeHelper:
|
||||||
|
'The current content has passed the format verification. Please click Submit to complete the creation',
|
||||||
apps: 'Apps',
|
apps: 'Apps',
|
||||||
local: 'Local',
|
local: 'Local',
|
||||||
createCompose: 'Create compose',
|
createCompose: 'Create compose',
|
||||||
|
@ -532,6 +532,7 @@ const message = {
|
|||||||
registrieHelper: '一行一个,例:\n172.16.10.111:8081 \n172.16.10.112:8081',
|
registrieHelper: '一行一个,例:\n172.16.10.111:8081 \n172.16.10.112:8081',
|
||||||
|
|
||||||
compose: '编排',
|
compose: '编排',
|
||||||
|
composeHelper: '当前内容已通过格式验证,请点击确认完成创建',
|
||||||
composePathHelper: '容器编排将保存在: {0}',
|
composePathHelper: '容器编排将保存在: {0}',
|
||||||
apps: '应用商店',
|
apps: '应用商店',
|
||||||
local: '本地',
|
local: '本地',
|
||||||
@ -842,7 +843,7 @@ const message = {
|
|||||||
versionHelper: '1Panel 版本号命名规则为: [大版本].[功能版本].[Bug 修复版本],示例如下:',
|
versionHelper: '1Panel 版本号命名规则为: [大版本].[功能版本].[Bug 修复版本],示例如下:',
|
||||||
versionHelper1: 'v1.0.1 是 v1.0.0 之后的 Bug 修复版本',
|
versionHelper1: 'v1.0.1 是 v1.0.0 之后的 Bug 修复版本',
|
||||||
versionHelper2: 'v1.1.0 是 v1.0.0 之后的功能版本',
|
versionHelper2: 'v1.1.0 是 v1.0.0 之后的功能版本',
|
||||||
newVersion: '(Bug fix version)',
|
newVersion: '(Bug 修复版本)',
|
||||||
latestVersion: '(功能版本)',
|
latestVersion: '(功能版本)',
|
||||||
upgradeCheck: '检查更新',
|
upgradeCheck: '检查更新',
|
||||||
upgradeNotes: '更新内容',
|
upgradeNotes: '更新内容',
|
||||||
|
@ -9,12 +9,12 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<DrawerHeader :header="$t('container.compose')" :back="handleClose" />
|
<DrawerHeader :header="$t('container.compose')" :back="handleClose" />
|
||||||
</template>
|
</template>
|
||||||
<div>
|
<div v-loading="loading">
|
||||||
<el-row type="flex" justify="center">
|
<el-row type="flex" justify="center">
|
||||||
<el-col :span="22">
|
<el-col :span="22">
|
||||||
<el-form ref="formRef" label-position="top" :model="form" :rules="rules" label-width="80px">
|
<el-form ref="formRef" label-position="top" :model="form" :rules="rules" label-width="80px">
|
||||||
<el-form-item :label="$t('container.from')">
|
<el-form-item :label="$t('container.from')">
|
||||||
<el-radio-group v-model="form.from">
|
<el-radio-group v-model="form.from" @change="hasChecked = false">
|
||||||
<el-radio label="edit">{{ $t('commons.button.edit') }}</el-radio>
|
<el-radio label="edit">{{ $t('commons.button.edit') }}</el-radio>
|
||||||
<el-radio label="path">{{ $t('container.pathSelect') }}</el-radio>
|
<el-radio label="path">{{ $t('container.pathSelect') }}</el-radio>
|
||||||
<el-radio label="template">{{ $t('container.composeTemplate') }}</el-radio>
|
<el-radio label="template">{{ $t('container.composeTemplate') }}</el-radio>
|
||||||
@ -37,7 +37,7 @@
|
|||||||
<span class="input-help">{{ $t('container.composePathHelper', [composeFile]) }}</span>
|
<span class="input-help">{{ $t('container.composePathHelper', [composeFile]) }}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="form.from === 'template'" prop="template">
|
<el-form-item v-if="form.from === 'template'" prop="template">
|
||||||
<el-select v-model="form.template">
|
<el-select v-model="form.template" @change="hasChecked = false">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in templateOptions"
|
v-for="item in templateOptions"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
@ -52,10 +52,11 @@
|
|||||||
placeholder="#Define or paste the content of your docker-compose file here"
|
placeholder="#Define or paste the content of your docker-compose file here"
|
||||||
:indent-with-tab="true"
|
:indent-with-tab="true"
|
||||||
:tabSize="4"
|
:tabSize="4"
|
||||||
style="width: 100%; height: 200px"
|
style="width: 100%; height: 250px"
|
||||||
:lineWrapping="true"
|
:lineWrapping="true"
|
||||||
:matchBrackets="true"
|
:matchBrackets="true"
|
||||||
theme="cobalt"
|
theme="cobalt"
|
||||||
|
@change="hasChecked = false"
|
||||||
:styleActiveLine="true"
|
:styleActiveLine="true"
|
||||||
:extensions="extensions"
|
:extensions="extensions"
|
||||||
v-model="form.file"
|
v-model="form.file"
|
||||||
@ -63,12 +64,28 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<codemirror
|
<codemirror
|
||||||
v-if="logVisiable"
|
v-if="logVisiable && form.from !== 'edit'"
|
||||||
:autofocus="true"
|
:autofocus="true"
|
||||||
placeholder="Waiting for build output..."
|
placeholder="Waiting for docker-compose up output..."
|
||||||
:indent-with-tab="true"
|
:indent-with-tab="true"
|
||||||
:tabSize="4"
|
:tabSize="4"
|
||||||
style="max-height: calc(100vh - 537px)"
|
style="height: calc(100vh - 370px)"
|
||||||
|
:lineWrapping="true"
|
||||||
|
:matchBrackets="true"
|
||||||
|
theme="cobalt"
|
||||||
|
:styleActiveLine="true"
|
||||||
|
:extensions="extensions"
|
||||||
|
@ready="handleReady"
|
||||||
|
v-model="logInfo"
|
||||||
|
:readOnly="true"
|
||||||
|
/>
|
||||||
|
<codemirror
|
||||||
|
v-if="logVisiable && form.from === 'edit'"
|
||||||
|
:autofocus="true"
|
||||||
|
placeholder="Waiting for docker-compose up output..."
|
||||||
|
:indent-with-tab="true"
|
||||||
|
:tabSize="4"
|
||||||
|
style="height: calc(100vh - 590px)"
|
||||||
:lineWrapping="true"
|
:lineWrapping="true"
|
||||||
:matchBrackets="true"
|
:matchBrackets="true"
|
||||||
theme="cobalt"
|
theme="cobalt"
|
||||||
@ -86,7 +103,10 @@
|
|||||||
<el-button @click="drawerVisiable = false">
|
<el-button @click="drawerVisiable = false">
|
||||||
{{ $t('commons.button.cancel') }}
|
{{ $t('commons.button.cancel') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" :disabled="buttonDisabled" @click="onSubmit(formRef)">
|
<el-button :disabled="buttonDisabled" @click="onTest(formRef)">
|
||||||
|
{{ $t('commons.button.verify') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" :disabled="buttonDisabled || !hasChecked" @click="onSubmit(formRef)">
|
||||||
{{ $t('commons.button.confirm') }}
|
{{ $t('commons.button.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
@ -104,10 +124,13 @@ import { Rules } from '@/global/form-rules';
|
|||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { ElForm } from 'element-plus';
|
import { ElForm } from 'element-plus';
|
||||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||||
import { listComposeTemplate, upCompose } from '@/api/modules/container';
|
import { listComposeTemplate, testCompose, upCompose } from '@/api/modules/container';
|
||||||
import { loadBaseDir } from '@/api/modules/setting';
|
import { loadBaseDir } from '@/api/modules/setting';
|
||||||
import { LoadFile } from '@/api/modules/files';
|
import { LoadFile } from '@/api/modules/files';
|
||||||
import { formatImageStdout } from '@/utils/docker';
|
import { formatImageStdout } from '@/utils/docker';
|
||||||
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
|
||||||
|
const loading = ref();
|
||||||
|
|
||||||
const extensions = [javascript(), oneDark];
|
const extensions = [javascript(), oneDark];
|
||||||
const view = shallowRef();
|
const view = shallowRef();
|
||||||
@ -124,14 +147,9 @@ const buttonDisabled = ref(false);
|
|||||||
const baseDir = ref();
|
const baseDir = ref();
|
||||||
const composeFile = ref();
|
const composeFile = ref();
|
||||||
|
|
||||||
let timer: NodeJS.Timer | null = null;
|
const hasChecked = ref();
|
||||||
|
|
||||||
const varifyPath = (rule: any, value: any, callback: any) => {
|
let timer: NodeJS.Timer | null = null;
|
||||||
if (value.indexOf('docker-compose.yml') === -1) {
|
|
||||||
callback(new Error(i18n.global.t('commons.rule.selectHelper', ['docker-compose.yml'])));
|
|
||||||
}
|
|
||||||
callback();
|
|
||||||
};
|
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
name: '',
|
name: '',
|
||||||
@ -142,7 +160,7 @@ const form = reactive({
|
|||||||
});
|
});
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
name: [Rules.requiredInput, Rules.imageName],
|
name: [Rules.requiredInput, Rules.imageName],
|
||||||
path: [Rules.requiredSelect, { validator: varifyPath, trigger: 'change', required: true }],
|
path: [Rules.requiredSelect],
|
||||||
});
|
});
|
||||||
|
|
||||||
const loadTemplates = async () => {
|
const loadTemplates = async () => {
|
||||||
@ -160,6 +178,7 @@ const acceptParams = (): void => {
|
|||||||
form.path = '';
|
form.path = '';
|
||||||
form.file = '';
|
form.file = '';
|
||||||
logVisiable.value = false;
|
logVisiable.value = false;
|
||||||
|
hasChecked.value = false;
|
||||||
logInfo.value = '';
|
logInfo.value = '';
|
||||||
loadTemplates();
|
loadTemplates();
|
||||||
loadPath();
|
loadPath();
|
||||||
@ -186,6 +205,25 @@ const changePath = async () => {
|
|||||||
type FormInstance = InstanceType<typeof ElForm>;
|
type FormInstance = InstanceType<typeof ElForm>;
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
const onTest = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.validate(async (valid) => {
|
||||||
|
if (!valid) return;
|
||||||
|
loading.value = true;
|
||||||
|
await testCompose(form)
|
||||||
|
.then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
if (res.data) {
|
||||||
|
MsgSuccess(i18n.global.t('container.composeHelper'));
|
||||||
|
hasChecked.value = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const onSubmit = async (formEl: FormInstance | undefined) => {
|
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||||
if (!formEl) return;
|
if (!formEl) return;
|
||||||
formEl.validate(async (valid) => {
|
formEl.validate(async (valid) => {
|
||||||
@ -224,6 +262,7 @@ const loadLogs = async (path: string) => {
|
|||||||
|
|
||||||
const loadDir = async (path: string) => {
|
const loadDir = async (path: string) => {
|
||||||
form.path = path;
|
form.path = path;
|
||||||
|
hasChecked.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
@ -116,16 +116,11 @@ const form = reactive({
|
|||||||
tagStr: '',
|
tagStr: '',
|
||||||
tags: [] as Array<string>,
|
tags: [] as Array<string>,
|
||||||
});
|
});
|
||||||
const varifyPath = (rule: any, value: any, callback: any) => {
|
|
||||||
if (value.indexOf('Dockerfile') === -1) {
|
|
||||||
callback(new Error(i18n.global.t('commons.rule.selectHelper', ['Dockerfile'])));
|
|
||||||
}
|
|
||||||
callback();
|
|
||||||
};
|
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
name: [Rules.requiredInput, Rules.imageName],
|
name: [Rules.requiredInput, Rules.imageName],
|
||||||
from: [Rules.requiredSelect],
|
from: [Rules.requiredSelect],
|
||||||
dockerfile: [Rules.requiredInput, { validator: varifyPath, trigger: 'change', required: true }],
|
dockerfile: [Rules.requiredInput],
|
||||||
});
|
});
|
||||||
const acceptParams = async () => {
|
const acceptParams = async () => {
|
||||||
logVisiable.value = false;
|
logVisiable.value = false;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user