feat: OpenResty 支持新增扩展 (#6543)
Some checks failed
sync2gitee / repo-sync (push) Failing after -9m18s

This commit is contained in:
zhengkunwang 2024-09-20 17:24:57 +08:00 committed by GitHub
parent 6a4897b0aa
commit 3c0dca6992
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 531 additions and 13 deletions

View file

@ -116,3 +116,60 @@ func (b *BaseApi) ClearNginxProxyCache(c *gin.Context) {
} }
helper.SuccessWithOutData(c) helper.SuccessWithOutData(c)
} }
// @Tags OpenResty
// @Summary Build OpenResty
// @Description 构建 OpenResty
// @Accept json
// @Param request body request.NginxBuildReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /openresty/build [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"构建 OpenResty","formatEN":"Build OpenResty"}
func (b *BaseApi) BuildNginx(c *gin.Context) {
var req request.NginxBuildReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := nginxService.Build(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags OpenResty
// @Summary Update OpenResty module
// @Description 更新 OpenResty 模块
// @Accept json
// @Param request body request.NginxModuleUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /openresty/module/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 OpenResty 模块","formatEN":"Update OpenResty module"}
func (b *BaseApi) UpdateNginxModule(c *gin.Context) {
var req request.NginxModuleUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := nginxService.UpdateModule(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags OpenResty
// @Summary Get OpenResty modules
// @Description 获取 OpenResty 模块
// @Success 200 {array} response.NginxModule
// @Security ApiKeyAuth
// @Router /openresty/modules [get]
func (b *BaseApi) GetNginxModules(c *gin.Context) {
modules, err := nginxService.GetModules()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, modules)
}

View file

@ -106,3 +106,16 @@ type NginxRedirectUpdate struct {
Content string `json:"content" validate:"required"` Content string `json:"content" validate:"required"`
Name string `json:"name" validate:"required"` Name string `json:"name" validate:"required"`
} }
type NginxBuildReq struct {
TaskID string `json:"taskID" validate:"required"`
}
type NginxModuleUpdate struct {
Operate string `json:"operate" validate:"required,oneof=create delete update"`
Name string `json:"name" validate:"required"`
Script string `json:"script"`
Packages string `json:"packages"`
Enable bool `json:"enable"`
Params string `json:"params"`
}

View file

@ -67,3 +67,11 @@ type NginxProxyCache struct {
CacheExpire int `json:"cacheExpire" ` CacheExpire int `json:"cacheExpire" `
CacheExpireUnit string `json:"cacheExpireUnit" ` CacheExpireUnit string `json:"cacheExpireUnit" `
} }
type NginxModule struct {
Name string `json:"name"`
Script string `json:"script"`
Packages []string `json:"packages"`
Params string `json:"params"`
Enable bool `json:"enable"`
}

View file

@ -1,7 +1,13 @@
package service package service
import ( import (
"bufio"
"encoding/json"
"fmt" "fmt"
"github.com/1Panel-dev/1Panel/agent/app/task"
"github.com/1Panel-dev/1Panel/agent/buserr"
cmd2 "github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/subosito/gotenv"
"io" "io"
"net/http" "net/http"
"os" "os"
@ -29,6 +35,10 @@ type INginxService interface {
GetStatus() (response.NginxStatus, error) GetStatus() (response.NginxStatus, error)
UpdateConfigFile(req request.NginxConfigFileUpdate) error UpdateConfigFile(req request.NginxConfigFileUpdate) error
ClearProxyCache() error ClearProxyCache() error
Build(req request.NginxBuildReq) error
GetModules() ([]response.NginxModule, error)
UpdateModule(req request.NginxModuleUpdate) error
} }
func NewINginxService() INginxService { func NewINginxService() INginxService {
@ -152,3 +162,157 @@ func (n NginxService) ClearProxyCache() error {
} }
return nil return nil
} }
func (n NginxService) Build(req request.NginxBuildReq) error {
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return err
}
fileOp := files.NewFileOp()
buildPath := path.Join(nginxInstall.GetPath(), "build")
if !fileOp.Stat(buildPath) {
return buserr.New("ErrBuildDirNotFound")
}
moduleConfigPath := path.Join(buildPath, "module.json")
moduleContent, err := fileOp.GetContent(moduleConfigPath)
if err != nil {
return err
}
var (
modules []response.NginxModule
addModuleParams []string
addPackages []string
)
if len(moduleContent) > 0 {
_ = json.Unmarshal(moduleContent, &modules)
bashFile, err := os.OpenFile(path.Join(buildPath, "tmp", "pre.sh"), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
if err != nil {
return err
}
defer bashFile.Close()
bashFileWriter := bufio.NewWriter(bashFile)
for _, module := range modules {
if !module.Enable {
continue
}
_, err = bashFileWriter.WriteString(module.Script + "\n")
if err != nil {
return err
}
addModuleParams = append(addModuleParams, module.Params)
addPackages = append(addPackages, module.Packages...)
}
err = bashFileWriter.Flush()
if err != nil {
return err
}
}
envs, err := gotenv.Read(nginxInstall.GetEnvPath())
if err != nil {
return err
}
envs["RESTY_CONFIG_OPTIONS_MORE"] = ""
envs["RESTY_ADD_PACKAGE_BUILDDEPS"] = ""
if len(addModuleParams) > 0 {
envs["RESTY_CONFIG_OPTIONS_MORE"] = strings.Join(addModuleParams, " ")
}
if len(addPackages) > 0 {
envs["RESTY_ADD_PACKAGE_BUILDDEPS"] = strings.Join(addPackages, " ")
}
_ = gotenv.Write(envs, nginxInstall.GetEnvPath())
buildTask, err := task.NewTaskWithOps(nginxInstall.Name, task.TaskBuild, task.TaskScopeApp, req.TaskID, nginxInstall.ID)
if err != nil {
return err
}
buildTask.AddSubTask("", func(t *task.Task) error {
if err = cmd2.ExecWithLogFile(fmt.Sprintf("docker compose -f %s build", nginxInstall.GetComposePath()), 15*time.Minute, t.Task.LogFile); err != nil {
return err
}
_, err = compose.DownAndUp(nginxInstall.GetComposePath())
return err
}, nil)
go func() {
_ = buildTask.Execute()
}()
return nil
}
func (n NginxService) GetModules() ([]response.NginxModule, error) {
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return nil, err
}
fileOp := files.NewFileOp()
var modules []response.NginxModule
moduleConfigPath := path.Join(nginxInstall.GetPath(), "build", "module.json")
if !fileOp.Stat(moduleConfigPath) {
return modules, nil
}
moduleContent, err := fileOp.GetContent(moduleConfigPath)
if err != nil {
return nil, err
}
if len(moduleContent) > 0 {
_ = json.Unmarshal(moduleContent, &modules)
}
return modules, nil
}
func (n NginxService) UpdateModule(req request.NginxModuleUpdate) error {
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return err
}
fileOp := files.NewFileOp()
var modules []response.NginxModule
moduleConfigPath := path.Join(nginxInstall.GetPath(), "build", "module.json")
if !fileOp.Stat(moduleConfigPath) {
_ = fileOp.CreateFile(moduleConfigPath)
}
moduleContent, err := fileOp.GetContent(moduleConfigPath)
if err != nil {
return err
}
if len(moduleContent) > 0 {
_ = json.Unmarshal(moduleContent, &modules)
}
switch req.Operate {
case "create":
for _, module := range modules {
if module.Name == req.Name {
return buserr.New("ErrNameIsExist")
}
}
modules = append(modules, response.NginxModule{
Name: req.Name,
Script: req.Script,
Packages: strings.Split(req.Packages, " "),
Params: req.Params,
Enable: true,
})
case "update":
for i, module := range modules {
if module.Name == req.Name {
modules[i].Script = req.Script
modules[i].Packages = strings.Split(req.Packages, " ")
modules[i].Params = req.Params
modules[i].Enable = req.Enable
break
}
}
case "delete":
for i, module := range modules {
if module.Name == req.Name {
modules = append(modules[:i], modules[i+1:]...)
break
}
}
}
moduleByte, err := json.Marshal(modules)
if err != nil {
return err
}
return fileOp.SaveFileWithByte(moduleConfigPath, moduleByte, 0644)
}

View file

@ -51,6 +51,7 @@ const (
TaskRestart = "TaskRestart" TaskRestart = "TaskRestart"
TaskBackup = "TaskBackup" TaskBackup = "TaskBackup"
TaskSync = "TaskSync" TaskSync = "TaskSync"
TaskBuild = "TaskBuild"
) )
const ( const (

View file

@ -99,6 +99,7 @@ ErrDomainIsUsed: "Domain is already used by website {{ .name }}"
ErrDomainFormat: "{{ .name }} domain format error" ErrDomainFormat: "{{ .name }} domain format error"
ErrDefaultAlias: "default is a reserved code name, please use another code name" ErrDefaultAlias: "default is a reserved code name, please use another code name"
ErrParentWebsite: "You need to delete the subsite(s) {{ .name }} first" ErrParentWebsite: "You need to delete the subsite(s) {{ .name }} first"
ErrBuildDirNotFound: "Build directory does not exist"
#ssl #ssl
ErrSSLCannotDelete: "The certificate {{ .name }} is being used by the website and cannot be removed" ErrSSLCannotDelete: "The certificate {{ .name }} is being used by the website and cannot be removed"
@ -244,4 +245,5 @@ TaskSync: "Sync"
LocalApp: "Local App" LocalApp: "Local App"
SubTask: "Subtask" SubTask: "Subtask"
RuntimeExtension: "Runtime Extension" RuntimeExtension: "Runtime Extension"
TaskBuild: "Build"

View file

@ -99,6 +99,7 @@ ErrDomainIsUsed: "域名已被網站【{{ .name }}】使用"
ErrDomainFormat: "{{ .name }} 域名格式不正確" ErrDomainFormat: "{{ .name }} 域名格式不正確"
ErrDefaultAlias: "default 為保留代號,請使用其他代號" ErrDefaultAlias: "default 為保留代號,請使用其他代號"
ErrParentWebsite: "需要先刪除子網站 {{ .name }}" ErrParentWebsite: "需要先刪除子網站 {{ .name }}"
ErrBuildDirNotFound: "編譯目錄不存在"
#ssl #ssl
ErrSSLCannotDelete: "{{ .name }} 證書正在被網站使用,無法刪除" ErrSSLCannotDelete: "{{ .name }} 證書正在被網站使用,無法刪除"
@ -246,4 +247,6 @@ TaskSync: "同步"
LocalApp: "本地應用" LocalApp: "本地應用"
SubTask: "子任務" SubTask: "子任務"
RuntimeExtension: "運行環境擴展" RuntimeExtension: "運行環境擴展"
TaskBuild: "構建"

View file

@ -99,6 +99,7 @@ ErrDomainIsUsed: "域名已被网站【{{ .name }}】使用"
ErrDomainFormat: "{{ .name }} 域名格式不正确" ErrDomainFormat: "{{ .name }} 域名格式不正确"
ErrDefaultAlias: "default 为保留代号,请使用其他代号" ErrDefaultAlias: "default 为保留代号,请使用其他代号"
ErrParentWebsite: "需要先删除子网站 {{ .name }}" ErrParentWebsite: "需要先删除子网站 {{ .name }}"
ErrBuildDirNotFound: "构建目录不存在"
#ssl #ssl
ErrSSLCannotDelete: "{{ .name }} 证书正在被网站使用,无法删除" ErrSSLCannotDelete: "{{ .name }} 证书正在被网站使用,无法删除"
@ -248,3 +249,4 @@ TaskSync: "同步"
LocalApp: "本地应用" LocalApp: "本地应用"
SubTask: "子任务" SubTask: "子任务"
RuntimeExtension: "运行环境扩展" RuntimeExtension: "运行环境扩展"
TaskBuild: "构建"

View file

@ -19,5 +19,8 @@ func (a *NginxRouter) InitRouter(Router *gin.RouterGroup) {
groupRouter.GET("/status", baseApi.GetNginxStatus) groupRouter.GET("/status", baseApi.GetNginxStatus)
groupRouter.POST("/file", baseApi.UpdateNginxFile) groupRouter.POST("/file", baseApi.UpdateNginxFile)
groupRouter.POST("/clear", baseApi.ClearNginxProxyCache) groupRouter.POST("/clear", baseApi.ClearNginxProxyCache)
groupRouter.POST("/build", baseApi.BuildNginx)
groupRouter.POST("/modules/update", baseApi.UpdateNginxModule)
groupRouter.GET("/modules", baseApi.GetNginxModules)
} }
} }

View file

@ -28,4 +28,25 @@ export namespace Nginx {
content: string; content: string;
backup: boolean; backup: boolean;
} }
export interface NginxBuildReq {
taskID: string;
}
export interface NginxModule {
name: string;
script?: string;
packages?: string[];
enable: boolean;
params: string;
}
export interface NginxModuleUpdate {
operate: string;
name: string;
script?: string;
packages?: string;
enable: boolean;
params: string;
}
} }

View file

@ -11,7 +11,7 @@ export const GetNginxConfigByScope = (req: Nginx.NginxScopeReq) => {
}; };
export const UpdateNginxConfigByScope = (req: Nginx.NginxConfigReq) => { export const UpdateNginxConfigByScope = (req: Nginx.NginxConfigReq) => {
return http.post<any>(`/openresty/update`, req); return http.post(`/openresty/update`, req);
}; };
export const GetNginxStatus = () => { export const GetNginxStatus = () => {
@ -19,9 +19,21 @@ export const GetNginxStatus = () => {
}; };
export const UpdateNginxConfigFile = (req: Nginx.NginxFileUpdate) => { export const UpdateNginxConfigFile = (req: Nginx.NginxFileUpdate) => {
return http.post<any>(`/openresty/file`, req); return http.post(`/openresty/file`, req);
}; };
export const ClearNginxCache = () => { export const ClearNginxCache = () => {
return http.post<any>(`/openresty/clear`); return http.post(`/openresty/clear`);
};
export const BuildNginx = (req: Nginx.NginxBuildReq) => {
return http.post(`/openresty/build`, req);
};
export const GetNginxModules = () => {
return http.get<Nginx.NginxModule[]>(`/openresty/modules`);
};
export const UpdateNginxModule = (req: Nginx.NginxModuleUpdate) => {
return http.post(`/openresty/modules/update`, req);
}; };

View file

@ -9,13 +9,7 @@
:width="width" :width="width"
> >
<div> <div>
<highlightjs <highlightjs class="editor-main" ref="editorRef" :autodetect="false" :code="content"></highlightjs>
class="editor-main"
ref="editorRef"
language="JavaScript"
:autodetect="false"
:code="content"
></highlightjs>
</div> </div>
</el-dialog> </el-dialog>
</template> </template>
@ -98,7 +92,7 @@ const getContent = (pre: boolean) => {
}); });
data.value = res.data; data.value = res.data;
if (res.data.content != '') { if (res.data.content != '') {
if (stopSignals.some((signal) => res.data.content.endsWith(signal))) { if (stopSignals.some((signal) => res.data.content.includes(signal))) {
onCloseLog(); onCloseLog();
} }
if (end.value) { if (end.value) {

View file

@ -2251,6 +2251,15 @@ const message = {
clearProxyCache: 'Clear reverse proxy cache', clearProxyCache: 'Clear reverse proxy cache',
clearProxyCacheWarn: clearProxyCacheWarn:
'Clearing the reverse proxy cache will affect all websites configured with cache and requires restarting OpenResty. Do you want to continue? ', 'Clearing the reverse proxy cache will affect all websites configured with cache and requires restarting OpenResty. Do you want to continue? ',
create: 'Add a new module',
update: 'Edit a module',
params: 'Parameters',
packages: 'Packages',
script: 'Scripts',
module: 'Modules',
build: 'Build',
buildWarn:
'Building OpenResty requires reserving a certain amount of CPU and memory, which may take a long time, please be patient',
}, },
ssl: { ssl: {
create: 'Apply', create: 'Apply',

View file

@ -2096,6 +2096,14 @@ const message = {
saveAndReload: '保存並重載', saveAndReload: '保存並重載',
clearProxyCache: '清除反代快取', clearProxyCache: '清除反代快取',
clearProxyCacheWarn: '清除反代快取會影響所有配置快取的網站並且需要重新啟動 OpenResty 是否繼續 ', clearProxyCacheWarn: '清除反代快取會影響所有配置快取的網站並且需要重新啟動 OpenResty 是否繼續 ',
create: '新增模組',
update: '編輯模組',
params: '參數',
packages: '軟體包',
script: '腳本',
module: '模組',
build: '建構',
buildWarn: '建構 OpenResty 需要預留一定的 CPU 和內存時間較長請耐心等待',
}, },
ssl: { ssl: {
create: '申請證書', create: '申請證書',

View file

@ -2097,6 +2097,14 @@ const message = {
saveAndReload: '保存并重载', saveAndReload: '保存并重载',
clearProxyCache: '清除反代缓存', clearProxyCache: '清除反代缓存',
clearProxyCacheWarn: '清除反代缓存会影响所有配置缓存的网站并且需要重启 OpenResty 是否继续', clearProxyCacheWarn: '清除反代缓存会影响所有配置缓存的网站并且需要重启 OpenResty 是否继续',
create: '新增模块',
update: '编辑模块',
params: '参数',
packages: '软件包',
script: '脚本',
module: '模块',
build: '构建',
buildWarn: '构建 OpenResty 需要预留一定的 CPU 和内存时间较长请耐心等待',
}, },
ssl: { ssl: {
create: '申请证书', create: '申请证书',

View file

@ -23,6 +23,9 @@
<div v-if="row.status === 'Success'"> <div v-if="row.status === 'Success'">
<el-tag type="success">{{ $t('commons.status.success') }}</el-tag> <el-tag type="success">{{ $t('commons.status.success') }}</el-tag>
</div> </div>
<div v-else-if="row.status === 'Running'">
<el-tag type="primary">{{ $t('process.running') }}</el-tag>
</div>
<div v-else> <div v-else>
<el-tooltip <el-tooltip
class="box-item" class="box-item"

View file

@ -83,7 +83,12 @@
<el-text type="primary" class="cursor-pointer" @click="openConfig(row.id)"> <el-text type="primary" class="cursor-pointer" @click="openConfig(row.id)">
{{ row.primaryDomain }} {{ row.primaryDomain }}
</el-text> </el-text>
<el-popover placement="top-start" trigger="hover" @before-enter="searchDomains(row.id)"> <el-popover
placement="left"
trigger="hover"
:width="300"
@before-enter="searchDomains(row.id)"
>
<template #reference> <template #reference>
<el-button link icon="Promotion" class="ml-2.5"></el-button> <el-button link icon="Promotion" class="ml-2.5"></el-button>
</template> </template>
@ -95,6 +100,9 @@
{{ getUrl(domain, row) }} {{ getUrl(domain, row) }}
</el-button> </el-button>
</td> </td>
<td>
<CopyButton :content="getUrl(domain, row)" type="icon" />
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View file

@ -23,12 +23,16 @@
> >
{{ $t('website.log') }} {{ $t('website.log') }}
</el-button> </el-button>
<el-button type="primary" :plain="activeName !== '5'" @click="changeTab('5')">
{{ $t('runtime.module') }}
</el-button>
</template> </template>
<template #main> <template #main>
<Status v-if="activeName === '1'" :status="status" /> <Status v-if="activeName === '1'" :status="status" />
<Source v-if="activeName === '2'" /> <Source v-if="activeName === '2'" />
<NginxPer v-if="activeName === '3'" /> <NginxPer v-if="activeName === '3'" />
<ContainerLog v-if="activeName === '4'" ref="dialogContainerLogRef" /> <ContainerLog v-if="activeName === '4'" ref="dialogContainerLogRef" />
<Module v-if="activeName === '5'" />
</template> </template>
</LayoutContent> </LayoutContent>
</template> </template>
@ -39,6 +43,7 @@ import { nextTick, ref } from 'vue';
import ContainerLog from '@/components/container-log/index.vue'; import ContainerLog from '@/components/container-log/index.vue';
import NginxPer from './performance/index.vue'; import NginxPer from './performance/index.vue';
import Status from './status/index.vue'; import Status from './status/index.vue';
import Module from './module/index.vue';
const activeName = ref('1'); const activeName = ref('1');
const dialogContainerLogRef = ref(); const dialogContainerLogRef = ref();

View file

@ -0,0 +1,105 @@
<template>
<div>
<ComplexTable :data="data" @search="search()" :heightDiff="350" v-loading="loading">
<template #toolbar>
<el-button type="primary" @click="openOperate">{{ $t('commons.button.create') }}</el-button>
<el-button type="primary" plain @click="buildNginx">{{ $t('nginx.build') }}</el-button>
</template>
<el-table-column prop="name" :label="$t('commons.table.name')" />
<el-table-column prop="params" :label="$t('nginx.params')" />
<el-table-column :label="$t('commons.table.status')" fix>
<template #default="{ row }">
<el-switch v-model="row.enable" />
</template>
</el-table-column>
<fu-table-operations
:ellipsis="2"
width="100px"
:buttons="buttons"
:label="$t('commons.table.operate')"
fixed="right"
fix
/>
</ComplexTable>
<TaskLog ref="taskLogRef" />
<Operate ref="operateRef" @close="search" />
<OpDialog ref="deleteRef" @search="search" @cancel="search" />
</div>
</template>
<script lang="ts" setup>
import { BuildNginx, GetNginxModules, UpdateNginxModule } from '@/api/modules/nginx';
import { newUUID } from '@/utils/util';
import TaskLog from '@/components/task-log/index.vue';
import Operate from './operate/index.vue';
import i18n from '@/lang';
import { Nginx } from '@/api/interface/nginx';
const taskLogRef = ref();
const data = ref([]);
const loading = ref(false);
const buttons = [
{
label: i18n.global.t('commons.button.delete'),
click: function (row: Nginx.NginxModule) {
deleteModule(row);
},
},
];
const operateRef = ref();
const deleteRef = ref();
const buildNginx = async () => {
ElMessageBox.confirm(i18n.global.t('nginx.buildWarn'), i18n.global.t('nginx.build'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
}).then(async () => {
const taskID = newUUID();
try {
await BuildNginx({
taskID: taskID,
});
openTaskLog(taskID);
} catch (error) {}
});
};
const search = () => {
loading.value = true;
GetNginxModules()
.then((res) => {
data.value = res.data;
})
.finally(() => {
loading.value = false;
});
};
const openTaskLog = (taskID: string) => {
taskLogRef.value.openWithTaskID(taskID);
};
const openOperate = () => {
operateRef.value.acceptParams();
};
const deleteModule = async (row: Nginx.NginxModule) => {
const data = {
name: row.name,
operate: 'delete',
};
deleteRef.value.acceptParams({
title: i18n.global.t('commons.button.delete'),
names: [row.name],
msg: i18n.global.t('commons.msg.operatorHelper', [
i18n.global.t('nginx.module'),
i18n.global.t('commons.button.delete'),
]),
api: UpdateNginxModule,
params: data,
});
};
onMounted(() => {
search();
});
</script>

View file

@ -0,0 +1,92 @@
<template>
<DrawerPro
v-model="open"
:header="$t('nginx.' + mode)"
size="large"
:resource="mode === 'edit' ? module.name : ''"
:back="handleClose"
>
<el-form ref="moduleForm" label-position="top" :model="module" :rules="rules">
<el-form-item :label="$t('commons.table.name')" prop="name">
<el-input v-model.trim="module.name" :disabled="mode === 'edit'"></el-input>
</el-form-item>
<el-form-item :label="$t('nginx.params')" prop="params">
<el-input v-model.trim="module.params"></el-input>
</el-form-item>
<el-form-item :label="$t('nginx.packages')" prop="packages">
<el-input v-model.trim="module.packages"></el-input>
</el-form-item>
<el-form-item :label="$t('nginx.script')" prop="script">
<el-input v-model="module.script" type="textarea" :rows="10"></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(moduleForm)" :disabled="loading">
{{ $t('commons.button.confirm') }}
</el-button>
</template>
</DrawerPro>
</template>
<script lang="ts" setup>
import { UpdateNginxModule } from '@/api/modules/nginx';
import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { FormInstance } from 'element-plus';
const moduleForm = ref<FormInstance>();
const open = ref(false);
const em = defineEmits(['close']);
const mode = ref('create');
const loading = ref(false);
const module = ref({
name: '',
operate: 'create',
script: '',
enable: true,
params: '',
packages: '',
});
const rules = ref({
name: [Rules.requiredInput, Rules.simpleName],
params: [Rules.requiredInput],
});
const handleClose = () => {
open.value = false;
em('close', false);
};
const acceptParams = async () => {
open.value = true;
};
const submit = async (form: FormInstance) => {
await form.validate();
if (form.validate()) {
loading.value = true;
const data = {
...module.value,
operate: mode.value,
};
UpdateNginxModule(data)
.then(() => {
if (mode.value === 'edit') {
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
} else if (mode.value === 'create') {
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
}
handleClose();
})
.finally(() => {
loading.value = false;
});
}
};
defineExpose({
acceptParams,
});
</script>