mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-03-14 01:34:47 +08:00
feat: 负载均衡增加源文编辑 (#6153)
This commit is contained in:
parent
bf82fe743c
commit
7fb42d7b47
@ -917,3 +917,43 @@ func (b *BaseApi) DeleteLoadBalance(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update website upstream
|
||||
// @Description 更新网站 upstream
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteLBUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/lbs/update [post]
|
||||
func (b *BaseApi) UpdateLoadBalance(c *gin.Context) {
|
||||
var req request.WebsiteLBUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateLoadBalance(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update website upstream file
|
||||
// @Description 更新网站 upstream 文件
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteLBUpdateFile true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/lbs/file [post]
|
||||
func (b *BaseApi) UpdateLoadBalanceFile(c *gin.Context) {
|
||||
var req request.WebsiteLBUpdateFile
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateLoadBalanceFile(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
@ -69,6 +69,7 @@ type NginxUpstream struct {
|
||||
Name string `json:"name"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
Servers []NginxUpstreamServer `json:"servers"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type NginxUpstreamServer struct {
|
||||
|
@ -264,7 +264,20 @@ type WebsiteLBCreate struct {
|
||||
Servers []dto.NginxUpstreamServer `json:"servers"`
|
||||
}
|
||||
|
||||
type WebsiteLBUpdate struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
Servers []dto.NginxUpstreamServer `json:"servers"`
|
||||
}
|
||||
|
||||
type WebsiteLBDelete struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteLBUpdateFile struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
}
|
||||
|
@ -112,6 +112,8 @@ type IWebsiteService interface {
|
||||
GetLoadBalances(id uint) ([]dto.NginxUpstream, error)
|
||||
CreateLoadBalance(req request.WebsiteLBCreate) error
|
||||
DeleteLoadBalance(req request.WebsiteLBDelete) error
|
||||
UpdateLoadBalance(req request.WebsiteLBUpdate) error
|
||||
UpdateLoadBalanceFile(req request.WebsiteLBUpdateFile) error
|
||||
}
|
||||
|
||||
func NewIWebsiteService() IWebsiteService {
|
||||
@ -2934,6 +2936,11 @@ func (w WebsiteService) GetLoadBalances(id uint) ([]dto.NginxUpstream, error) {
|
||||
Name: upstreamName,
|
||||
}
|
||||
upstreamPath := path.Join(includeDir, name)
|
||||
content, err := fileOp.GetContent(upstreamPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
upstream.Content = string(content)
|
||||
nginxParser, err := parser.NewParser(upstreamPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -3019,6 +3026,9 @@ func (w WebsiteService) CreateLoadBalance(req request.WebsiteLBCreate) error {
|
||||
upstream := components.Upstream{
|
||||
UpstreamName: req.Name,
|
||||
}
|
||||
if req.Algorithm != "default" {
|
||||
upstream.UpdateDirective(req.Algorithm, []string{})
|
||||
}
|
||||
|
||||
servers := make([]*components.UpstreamServer, 0)
|
||||
|
||||
@ -3064,6 +3074,78 @@ func (w WebsiteService) CreateLoadBalance(req request.WebsiteLBCreate) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w WebsiteService) UpdateLoadBalance(req request.WebsiteLBUpdate) error {
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
includeDir := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "upstream")
|
||||
fileOp := files.NewFileOp()
|
||||
filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name))
|
||||
if !fileOp.Stat(filePath) {
|
||||
return nil
|
||||
}
|
||||
parser, err := parser.NewParser(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config, err := parser.Parse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
upstreams := config.FindUpstreams()
|
||||
for _, up := range upstreams {
|
||||
if up.UpstreamName == req.Name {
|
||||
directives := up.GetDirectives()
|
||||
for _, d := range directives {
|
||||
dName := d.GetName()
|
||||
if _, ok := dto.LBAlgorithms[dName]; ok {
|
||||
up.RemoveDirective(dName, nil)
|
||||
}
|
||||
}
|
||||
if req.Algorithm != "default" {
|
||||
up.UpdateDirective(req.Algorithm, []string{})
|
||||
}
|
||||
var servers []*components.UpstreamServer
|
||||
for _, server := range req.Servers {
|
||||
upstreamServer := &components.UpstreamServer{
|
||||
Address: server.Server,
|
||||
}
|
||||
parameters := make(map[string]string)
|
||||
if server.Weight > 0 {
|
||||
parameters["weight"] = strconv.Itoa(server.Weight)
|
||||
}
|
||||
if server.MaxFails > 0 {
|
||||
parameters["max_fails"] = strconv.Itoa(server.MaxFails)
|
||||
}
|
||||
if server.FailTimeout != "" {
|
||||
parameters["fail_timeout"] = server.FailTimeout
|
||||
}
|
||||
if server.MaxConns > 0 {
|
||||
parameters["max_conns"] = strconv.Itoa(server.MaxConns)
|
||||
}
|
||||
if server.Flag != "" {
|
||||
upstreamServer.Flags = []string{server.Flag}
|
||||
}
|
||||
upstreamServer.Parameters = parameters
|
||||
servers = append(servers, upstreamServer)
|
||||
}
|
||||
up.UpstreamServers = servers
|
||||
}
|
||||
}
|
||||
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
|
||||
}
|
||||
if err = opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w WebsiteService) DeleteLoadBalance(req request.WebsiteLBDelete) error {
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||
if err != nil {
|
||||
@ -3082,8 +3164,32 @@ func (w WebsiteService) DeleteLoadBalance(req request.WebsiteLBDelete) error {
|
||||
if err = fileOp.DeleteFile(filePath); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {
|
||||
return opNginx(nginxInstall.ContainerName, constant.NginxReload)
|
||||
}
|
||||
|
||||
func (w WebsiteService) UpdateLoadBalanceFile(req request.WebsiteLBUpdateFile) error {
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
includeDir := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "upstream")
|
||||
filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name))
|
||||
fileOp := files.NewFileOp()
|
||||
oldContent, err := fileOp.GetContent(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = fileOp.WriteFile(filePath, strings.NewReader(req.Content), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
_ = fileOp.WriteFile(filePath, bytes.NewReader(oldContent), 0755)
|
||||
}
|
||||
}()
|
||||
return opNginx(nginxInstall.ContainerName, constant.NginxReload)
|
||||
}
|
||||
|
@ -72,5 +72,7 @@ func (a *WebsiteRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
websiteRouter.GET("/:id/lbs", baseApi.GetLoadBalances)
|
||||
websiteRouter.POST("/lbs/create", baseApi.CreateLoadBalance)
|
||||
websiteRouter.POST("/lbs/del", baseApi.DeleteLoadBalance)
|
||||
websiteRouter.POST("/lbs/update", baseApi.UpdateLoadBalance)
|
||||
websiteRouter.POST("/lbs/file", baseApi.UpdateLoadBalanceFile)
|
||||
}
|
||||
}
|
||||
|
@ -82,10 +82,10 @@ func (us *Upstream) FindDirectives(directiveName string) []IDirective {
|
||||
}
|
||||
|
||||
func (us *Upstream) UpdateDirective(key string, params []string) {
|
||||
if key == "" || len(params) == 0 {
|
||||
if key == "" {
|
||||
return
|
||||
}
|
||||
directives := us.GetDirectives()
|
||||
directives := us.Directives
|
||||
index := -1
|
||||
for i, dir := range directives {
|
||||
if dir.GetName() == key {
|
||||
@ -112,7 +112,7 @@ func (us *Upstream) UpdateDirective(key string, params []string) {
|
||||
}
|
||||
|
||||
func (us *Upstream) RemoveDirective(key string, params []string) {
|
||||
directives := us.GetDirectives()
|
||||
directives := us.Directives
|
||||
var newDirectives []IDirective
|
||||
for _, dir := range directives {
|
||||
if dir.GetName() == key {
|
||||
|
@ -573,6 +573,14 @@ export namespace Website {
|
||||
name: string;
|
||||
algorithm: string;
|
||||
servers: NginxUpstreamServer[];
|
||||
content?: string;
|
||||
websiteID?: number;
|
||||
}
|
||||
|
||||
export interface NginxUpstreamFile {
|
||||
name: string;
|
||||
content: string;
|
||||
websiteID: number;
|
||||
}
|
||||
|
||||
export interface LoadBalanceReq {
|
||||
@ -595,4 +603,10 @@ export namespace Website {
|
||||
websiteID: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface WebsiteLBUpdateFile {
|
||||
websiteID: number;
|
||||
name: string;
|
||||
content: string;
|
||||
}
|
||||
}
|
||||
|
@ -307,3 +307,11 @@ export const CreateLoadBalance = (req: Website.LoadBalanceReq) => {
|
||||
export const DeleteLoadBalance = (req: Website.LoadBalanceDel) => {
|
||||
return http.post(`/websites/lbs/del`, req);
|
||||
};
|
||||
|
||||
export const UpdateLoadBalance = (req: Website.LoadBalanceReq) => {
|
||||
return http.post(`/websites/lbs/update`, req);
|
||||
};
|
||||
|
||||
export const UpdateLoadBalanceFile = (req: Website.WebsiteLBUpdateFile) => {
|
||||
return http.post(`/websites/lbs/file`, req);
|
||||
};
|
||||
|
@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<DrawerPro v-model="open" :header="$t('website.proxyFile')" :back="handleClose" :size="mobile ? 'full' : 'normal'">
|
||||
<CodemirrorPro v-model="req.content" mode="nginx"></CodemirrorPro>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="submit()" :disabled="loading">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</DrawerPro>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import i18n from '@/lang';
|
||||
import { FormInstance } from 'element-plus';
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { UpdateLoadBalanceFile } from '@/api/modules/website';
|
||||
import { GlobalStore } from '@/store';
|
||||
import CodemirrorPro from '@/components/codemirror-pro/index.vue';
|
||||
import { Website } from '@/api/interface/website';
|
||||
const globalStore = GlobalStore();
|
||||
|
||||
const mobile = computed(() => {
|
||||
return globalStore.isMobile();
|
||||
});
|
||||
|
||||
const proxyForm = ref<FormInstance>();
|
||||
const open = ref(false);
|
||||
const loading = ref(false);
|
||||
const em = defineEmits(['close']);
|
||||
const handleClose = () => {
|
||||
proxyForm.value?.resetFields();
|
||||
open.value = false;
|
||||
em('close', false);
|
||||
};
|
||||
const req = reactive({
|
||||
name: '',
|
||||
websiteID: 0,
|
||||
content: '',
|
||||
});
|
||||
|
||||
const acceptParams = async (ups: Website.NginxUpstreamFile) => {
|
||||
req.name = ups.name;
|
||||
req.websiteID = ups.websiteID;
|
||||
req.content = ups.content;
|
||||
open.value = true;
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
loading.value = true;
|
||||
UpdateLoadBalanceFile(req)
|
||||
.then(() => {
|
||||
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
|
||||
handleClose();
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
@ -7,7 +7,11 @@
|
||||
</el-button>
|
||||
</template>
|
||||
<el-table-column :label="$t('commons.table.name')" prop="name"></el-table-column>
|
||||
<el-table-column :label="$t('website.algorithm')" prop="algorithm"></el-table-column>
|
||||
<el-table-column :label="$t('website.algorithm')" prop="algorithm">
|
||||
<template #default="{ row }">
|
||||
{{ getAlgorithm(row.algorithm) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('website.server')" prop="servers" minWidth="400px">
|
||||
<template #default="{ row }">
|
||||
<table>
|
||||
@ -45,8 +49,9 @@
|
||||
/>
|
||||
</ComplexTable>
|
||||
</div>
|
||||
<Operate ref="operateRef" @search="search()"></Operate>
|
||||
<Operate ref="operateRef" @close="search()" />
|
||||
<OpDialog ref="delRef" @search="search()" />
|
||||
<File ref="fileRef" @search="search()" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -55,6 +60,8 @@ import { defineProps, onMounted, ref } from 'vue';
|
||||
import Operate from './operate/index.vue';
|
||||
import i18n from '@/lang';
|
||||
import { Website } from '@/api/interface/website';
|
||||
import { Algorithms } from '@/global/mimetype';
|
||||
import File from './file/index.vue';
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
@ -67,8 +74,21 @@ const data = ref([]);
|
||||
const loading = ref(false);
|
||||
const operateRef = ref();
|
||||
const delRef = ref();
|
||||
const fileRef = ref();
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
label: i18n.global.t('website.proxyFile'),
|
||||
click: function (row: any) {
|
||||
openEditFile(row);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.edit'),
|
||||
click: (row: any) => {
|
||||
update(row);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.delete'),
|
||||
click: (row: any) => {
|
||||
@ -83,6 +103,19 @@ const search = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const getAlgorithm = (key: string) => {
|
||||
let label = '';
|
||||
Algorithms.forEach((algorithm) => {
|
||||
if (algorithm.value === key) {
|
||||
label = algorithm.label;
|
||||
}
|
||||
});
|
||||
if (label === '') {
|
||||
return i18n.global.t('commons.table.default');
|
||||
}
|
||||
return label;
|
||||
};
|
||||
|
||||
const deleteLb = async (row: Website.NginxUpstream) => {
|
||||
delRef.value.acceptParams({
|
||||
title: i18n.global.t('commons.msg.deleteTitle'),
|
||||
@ -97,7 +130,26 @@ const deleteLb = async (row: Website.NginxUpstream) => {
|
||||
};
|
||||
|
||||
const create = () => {
|
||||
operateRef.value.acceptParams(props.id);
|
||||
operateRef.value.acceptParams({
|
||||
websiteID: props.id,
|
||||
operate: 'create',
|
||||
});
|
||||
};
|
||||
|
||||
const update = (row: Website.NginxUpstream) => {
|
||||
operateRef.value.acceptParams({
|
||||
websiteID: props.id,
|
||||
operate: 'edit',
|
||||
upstream: row,
|
||||
});
|
||||
};
|
||||
|
||||
const openEditFile = (row: Website.NginxUpstream) => {
|
||||
fileRef.value.acceptParams({
|
||||
websiteID: props.id,
|
||||
name: row.name,
|
||||
content: row.content,
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -1,5 +1,11 @@
|
||||
<template>
|
||||
<DrawerPro v-model="open" :header="$t('website.addDomain')" :back="handleClose" size="large">
|
||||
<DrawerPro
|
||||
v-model="open"
|
||||
:back="handleClose"
|
||||
size="large"
|
||||
:header="$t('commons.button.' + item.operate) + $t('website.loadBalance')"
|
||||
:resource="item.operate == 'create' ? '' : item.name"
|
||||
>
|
||||
<el-form ref="lbForm" label-position="top" :model="item" :rules="rules">
|
||||
<el-form-item :label="$t('commons.table.name')" prop="name">
|
||||
<el-input v-model.trim="item.name" :disabled="item.operate === 'edit'"></el-input>
|
||||
@ -100,13 +106,14 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { CreateLoadBalance } from '@/api/modules/website';
|
||||
import { CreateLoadBalance, UpdateLoadBalance } from '@/api/modules/website';
|
||||
import i18n from '@/lang';
|
||||
import { FormInstance } from 'element-plus';
|
||||
import { ref } from 'vue';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { Rules, checkNumberRange } from '@/global/form-rules';
|
||||
import { Algorithms, StatusStrategy } from '@/global/mimetype';
|
||||
import { Website } from '@/api/interface/website';
|
||||
|
||||
const rules = ref<any>({
|
||||
name: [Rules.linuxName],
|
||||
@ -120,6 +127,12 @@ const rules = ref<any>({
|
||||
maxConns: [checkNumberRange(1, 1000)],
|
||||
});
|
||||
|
||||
interface LoadBalanceOperate {
|
||||
websiteID: number;
|
||||
operate: string;
|
||||
upstream?: Website.NginxUpstream;
|
||||
}
|
||||
|
||||
const lbForm = ref<FormInstance>();
|
||||
|
||||
const initServer = () => ({
|
||||
@ -163,27 +176,51 @@ const removeServer = (index: number) => {
|
||||
item.value.servers.splice(index, 1);
|
||||
};
|
||||
|
||||
const acceptParams = async (websiteId: number) => {
|
||||
item.value.websiteID = Number(websiteId);
|
||||
const acceptParams = async (req: LoadBalanceOperate) => {
|
||||
item.value.websiteID = req.websiteID;
|
||||
if (req.operate == 'edit') {
|
||||
item.value.operate = 'edit';
|
||||
item.value.name = req.upstream?.name || '';
|
||||
item.value.algorithm = req.upstream?.algorithm || 'default';
|
||||
let servers = [];
|
||||
req.upstream?.servers?.forEach((server) => {
|
||||
const weight = server.weight == 0 ? undefined : server.weight;
|
||||
const maxFails = server.maxFails == 0 ? undefined : server.maxFails;
|
||||
const maxConns = server.maxConns == 0 ? undefined : server.maxConns;
|
||||
servers.push({
|
||||
server: server.server,
|
||||
weight: weight,
|
||||
maxFails: maxFails,
|
||||
maxConns: maxConns,
|
||||
flag: server.flag,
|
||||
});
|
||||
});
|
||||
item.value.servers = servers;
|
||||
} else {
|
||||
item.value.servers = [initServer()];
|
||||
}
|
||||
open.value = true;
|
||||
};
|
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate((valid) => {
|
||||
await formEl.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
CreateLoadBalance(item.value)
|
||||
.then(() => {
|
||||
try {
|
||||
if (item.value.operate === 'edit') {
|
||||
await UpdateLoadBalance(item.value);
|
||||
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
|
||||
} else {
|
||||
await CreateLoadBalance(item.value);
|
||||
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
|
||||
}
|
||||
handleClose();
|
||||
})
|
||||
.finally(() => {
|
||||
} finally {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user