mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-22 16:00:51 +00:00
feat: Website Load balancer config support fail_timeout (#10796)
Refs https://github.com/1Panel-dev/1Panel/issues/10565
This commit is contained in:
@@ -73,12 +73,13 @@ type NginxUpstream struct {
|
||||
}
|
||||
|
||||
type NginxUpstreamServer struct {
|
||||
Server string `json:"server"`
|
||||
Weight int `json:"weight"`
|
||||
FailTimeout string `json:"failTimeout"`
|
||||
MaxFails int `json:"maxFails"`
|
||||
MaxConns int `json:"maxConns"`
|
||||
Flag string `json:"flag"`
|
||||
Server string `json:"server"`
|
||||
Weight int `json:"weight"`
|
||||
FailTimeout int `json:"failTimeout"`
|
||||
FailTimeoutUnit string `json:"failTimeoutUnit"`
|
||||
MaxFails int `json:"maxFails"`
|
||||
MaxConns int `json:"maxConns"`
|
||||
Flag string `json:"flag"`
|
||||
}
|
||||
|
||||
var LBAlgorithms = map[string]struct{}{"ip_hash": {}, "least_conn": {}}
|
||||
|
||||
@@ -3094,39 +3094,7 @@ func (w WebsiteService) GetLoadBalances(id uint) ([]dto.NginxUpstream, error) {
|
||||
upstream.Algorithm = dName
|
||||
}
|
||||
}
|
||||
var servers []dto.NginxUpstreamServer
|
||||
for _, ups := range up.UpstreamServers {
|
||||
server := dto.NginxUpstreamServer{
|
||||
Server: ups.Address,
|
||||
}
|
||||
parameters := ups.Parameters
|
||||
if weight, ok := parameters["weight"]; ok {
|
||||
num, err := strconv.Atoi(weight)
|
||||
if err == nil {
|
||||
server.Weight = num
|
||||
}
|
||||
}
|
||||
if maxFails, ok := parameters["max_fails"]; ok {
|
||||
num, err := strconv.Atoi(maxFails)
|
||||
if err == nil {
|
||||
server.MaxFails = num
|
||||
}
|
||||
}
|
||||
if failTimeout, ok := parameters["fail_timeout"]; ok {
|
||||
server.FailTimeout = failTimeout
|
||||
}
|
||||
if maxConns, ok := parameters["max_conns"]; ok {
|
||||
num, err := strconv.Atoi(maxConns)
|
||||
if err == nil {
|
||||
server.MaxConns = num
|
||||
}
|
||||
}
|
||||
for _, flag := range ups.Flags {
|
||||
server.Flag = flag
|
||||
}
|
||||
servers = append(servers, server)
|
||||
}
|
||||
upstream.Servers = servers
|
||||
upstream.Servers = getNginxUpstreamServers(up.UpstreamServers)
|
||||
}
|
||||
}
|
||||
res = append(res, upstream)
|
||||
@@ -3160,33 +3128,7 @@ func (w WebsiteService) CreateLoadBalance(req request.WebsiteLBCreate) error {
|
||||
if req.Algorithm != "default" {
|
||||
upstream.UpdateDirective(req.Algorithm, []string{})
|
||||
}
|
||||
|
||||
servers := make([]*components.UpstreamServer, 0)
|
||||
|
||||
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)
|
||||
}
|
||||
upstream.UpstreamServers = servers
|
||||
upstream.UpstreamServers = parseUpstreamServers(req.Servers)
|
||||
config.Block.Directives = append(config.Block.Directives, &upstream)
|
||||
|
||||
defer func() {
|
||||
@@ -3245,31 +3187,7 @@ func (w WebsiteService) UpdateLoadBalance(req request.WebsiteLBUpdate) error {
|
||||
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
|
||||
up.UpstreamServers = parseUpstreamServers(req.Servers)
|
||||
}
|
||||
}
|
||||
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
@@ -1533,3 +1534,89 @@ func getServer(website model.Website) (*components.Server, error) {
|
||||
server := servers[0]
|
||||
return server, nil
|
||||
}
|
||||
|
||||
func parseTimeString(input string) (int, string, error) {
|
||||
input = strings.TrimSpace(input)
|
||||
|
||||
re := regexp.MustCompile(`^(\d+)([smhdw]?)$`)
|
||||
matches := re.FindStringSubmatch(input)
|
||||
|
||||
if len(matches) < 2 {
|
||||
return 0, "", fmt.Errorf("invalid time format: %s", input)
|
||||
}
|
||||
|
||||
value, err := strconv.Atoi(matches[1])
|
||||
if err != nil {
|
||||
return 0, "", fmt.Errorf("invalid number: %s", matches[1])
|
||||
}
|
||||
|
||||
unit := matches[2]
|
||||
if unit == "" {
|
||||
unit = "s"
|
||||
}
|
||||
return value, unit, nil
|
||||
}
|
||||
|
||||
func parseUpstreamServers(reqServers []dto.NginxUpstreamServer) []*components.UpstreamServer {
|
||||
var servers []*components.UpstreamServer
|
||||
for _, server := range reqServers {
|
||||
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 > 0 {
|
||||
parameters["fail_timeout"] = fmt.Sprintf("%d%s", server.FailTimeout, server.FailTimeoutUnit)
|
||||
}
|
||||
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)
|
||||
}
|
||||
return servers
|
||||
}
|
||||
|
||||
func getNginxUpstreamServers(upstreamServers []*components.UpstreamServer) []dto.NginxUpstreamServer {
|
||||
var servers []dto.NginxUpstreamServer
|
||||
for _, ups := range upstreamServers {
|
||||
server := dto.NginxUpstreamServer{
|
||||
Server: ups.Address,
|
||||
}
|
||||
parameters := ups.Parameters
|
||||
if weight, ok := parameters["weight"]; ok {
|
||||
num, err := strconv.Atoi(weight)
|
||||
if err == nil {
|
||||
server.Weight = num
|
||||
}
|
||||
}
|
||||
if maxFails, ok := parameters["max_fails"]; ok {
|
||||
num, err := strconv.Atoi(maxFails)
|
||||
if err == nil {
|
||||
server.MaxFails = num
|
||||
}
|
||||
}
|
||||
if failTimeout, ok := parameters["fail_timeout"]; ok {
|
||||
server.FailTimeout, server.FailTimeoutUnit, _ = parseTimeString(failTimeout)
|
||||
}
|
||||
if maxConns, ok := parameters["max_conns"]; ok {
|
||||
num, err := strconv.Atoi(maxConns)
|
||||
if err == nil {
|
||||
server.MaxConns = num
|
||||
}
|
||||
}
|
||||
for _, flag := range ups.Flags {
|
||||
server.Flag = flag
|
||||
}
|
||||
servers = append(servers, server)
|
||||
}
|
||||
return servers
|
||||
}
|
||||
|
||||
@@ -607,7 +607,8 @@ export namespace Website {
|
||||
interface NginxUpstreamServer {
|
||||
server: string;
|
||||
weight: number;
|
||||
failTimeout: string;
|
||||
failTimeout: number;
|
||||
failTimeoutUnit: string;
|
||||
maxFails: number;
|
||||
maxConns: number;
|
||||
flag: string;
|
||||
|
||||
@@ -2600,6 +2600,9 @@ const message = {
|
||||
strategyDown: 'Down',
|
||||
strategyBackup: 'Backup',
|
||||
ipHashBackupErr: 'IP hash does not support backup nodes',
|
||||
failTimeout: 'Failure timeout',
|
||||
failTimeoutHelper:
|
||||
'The time window length for server health checks. When the cumulative number of failures reaches the threshold within this period, the server will be temporarily removed and retried after the same duration. Default 10 seconds',
|
||||
|
||||
staticChangePHPHelper: 'Currently a static website, you can switch to a PHP website',
|
||||
proxyCache: 'Reverse Proxy Cache',
|
||||
|
||||
@@ -2585,6 +2585,9 @@ const message = {
|
||||
strategyDown: 'Baja',
|
||||
strategyBackup: 'Backup',
|
||||
ipHashBackupErr: 'IP hash does not support backup nodes',
|
||||
failTimeout: 'Tiempo de espera de fallo',
|
||||
failTimeoutHelper:
|
||||
'La duración de la ventana de tiempo para las comprobaciones de estado del servidor. Cuando el número acumulado de fallos alcanza el umbral dentro de este período, el servidor se eliminará temporalmente y se reintentará después de la misma duración. Por defecto 10 segundos',
|
||||
|
||||
staticChangePHPHelper: 'Actualmente es un sitio estático, puedes cambiarlo a PHP',
|
||||
proxyCache: 'Caché de proxy inverso',
|
||||
|
||||
@@ -2517,6 +2517,9 @@ const message = {
|
||||
strategyDown: '無効',
|
||||
strategyBackup: 'バックアップ',
|
||||
ipHashBackupErr: 'IPハッシュはバックアップノードをサポートしていません',
|
||||
failTimeout: '障害タイムアウト',
|
||||
failTimeoutHelper:
|
||||
'サーバーのヘルスチェックの時間ウィンドウの長さ。この期間内に累積障害回数がしきい値に達すると、サーバーは一時的に削除され、同じ時間経過後に再試行されます。デフォルト 10 秒',
|
||||
|
||||
staticChangePHPHelper: '現在は静的ウェブサイトですが、PHPウェブサイトに切り替えることができます。',
|
||||
proxyCache: 'リバースプロキシキャッシュ',
|
||||
|
||||
@@ -2473,6 +2473,9 @@ const message = {
|
||||
strategyDown: '비활성화',
|
||||
strategyBackup: '백업',
|
||||
ipHashBackupErr: 'IP 해시는 백업 노드를 지원하지 않습니다',
|
||||
failTimeout: '장애 시간 초과',
|
||||
failTimeoutHelper:
|
||||
'서버 상태 점검 시간 창 길이. 이 기간 동안 누적 실패 횟수가 임계값에 도달하면 서버가 일시적으로 제거되고 동일한 시간 후에 재시도됩니다. 기본값 10초',
|
||||
|
||||
staticChangePHPHelper: '현재 정적 웹사이트이며 PHP 웹사이트로 전환할 수 있습니다.',
|
||||
proxyCache: '리버스 프록시 캐시',
|
||||
|
||||
@@ -2577,6 +2577,9 @@ const message = {
|
||||
strategyDown: 'Lumpuh',
|
||||
strategyBackup: 'Sandaran',
|
||||
ipHashBackupErr: 'Hash IP tidak menyokong nod sandaran',
|
||||
failTimeout: 'Masa tamat kegagalan',
|
||||
failTimeoutHelper:
|
||||
'Panjang tetingkap masa untuk pemeriksaan kesihatan pelayan. Apabila bilangan kegagalan terkumpul mencapai ambang dalam tempoh ini, pelayan akan dikeluarkan buat sementara waktu dan dicuba semula selepas tempoh yang sama. Lalai 10 saat',
|
||||
|
||||
staticChangePHPHelper: 'Kini laman web statik, boleh ditukar ke laman web PHP.',
|
||||
proxyCache: 'Cache Proksi Terbalik',
|
||||
|
||||
@@ -2581,6 +2581,9 @@ const message = {
|
||||
strategyDown: 'Desativar',
|
||||
strategyBackup: 'Backup',
|
||||
ipHashBackupErr: 'Hash IP não suporta nós de backup',
|
||||
failTimeout: 'Tempo limite de falha',
|
||||
failTimeoutHelper:
|
||||
'O comprimento da janela de tempo para verificações de integridade do servidor. Quando o número acumulado de falhas atinge o limite dentro deste período, o servidor será removido temporariamente e repetido após a mesma duração. Padrão 10 segundos',
|
||||
|
||||
staticChangePHPHelper: 'Atualmente um site estático, pode ser alterado para um site PHP.',
|
||||
proxyCache: 'Cache de Proxy Reverso',
|
||||
|
||||
@@ -2578,6 +2578,9 @@ const message = {
|
||||
strategyDown: 'Отключить',
|
||||
strategyBackup: 'Резервный',
|
||||
ipHashBackupErr: 'IP хэш не поддерживает резервные узлы',
|
||||
failTimeout: 'Таймаут отказа',
|
||||
failTimeoutHelper:
|
||||
'Длина временного окна для проверки работоспособности сервера. Когда кумулятивное количество отказов достигает порога в течение этого периода, сервер будет временно удален и повторно проверен через тот же промежуток времени. По умолчанию 10 секунд',
|
||||
|
||||
staticChangePHPHelper: 'В настоящее время статический сайт, можно переключить на PHP сайт.',
|
||||
proxyCache: 'Кэш Обратного Прокси',
|
||||
|
||||
@@ -2634,6 +2634,9 @@ const message = {
|
||||
strategyDown: 'Kapalı',
|
||||
strategyBackup: 'Yedek',
|
||||
ipHashBackupErr: 'IP 哈希不支持备用节点',
|
||||
failTimeout: 'Hata zaman aşımı',
|
||||
failTimeoutHelper:
|
||||
'Sunucu sağlık kontrolleri için zaman penceresi uzunluğu. Bu süre içinde biriken hata sayısı eşiğe ulaştığında, sunucu geçici olarak kaldırılacak ve aynı süre sonunda yeniden denenir. Varsayılan 10 saniye',
|
||||
|
||||
staticChangePHPHelper: 'Şu anda statik bir web sitesi, PHP web sitesine geçiş yapabilirsiniz',
|
||||
proxyCache: 'Ters Vekil Önbelleği',
|
||||
|
||||
@@ -2418,6 +2418,9 @@ const message = {
|
||||
strategyDown: '停用',
|
||||
strategyBackup: '備用',
|
||||
ipHashBackupErr: 'IP 雜湊不支援備用節點',
|
||||
failTimeout: '故障超時',
|
||||
failTimeoutHelper:
|
||||
'服務器健康檢查的時間窗口長度。在該時間段內累計失敗次數達到閾值時,服務器將被暫時移除,並在經過相同時長後重新嘗試。默認 10 秒',
|
||||
|
||||
staticChangePHPHelper: '目前為靜態網站,可切換為 PHP 網站',
|
||||
proxyCache: '反向代理快取',
|
||||
|
||||
@@ -2411,6 +2411,9 @@ const message = {
|
||||
strategyDown: '停用',
|
||||
strategyBackup: '备用',
|
||||
ipHashBackupErr: 'IP 哈希不支持备用节点',
|
||||
failTimeout: '故障超时',
|
||||
failTimeoutHelper:
|
||||
'服务器健康检查的时间窗口长度。在该时间段内累计失败次数达到阈值时,服务器将被暂时移除,并在经过相同时长后重新尝试。默认 10 秒',
|
||||
|
||||
staticChangePHPHelper: '当前为静态网站,可以切换为 PHP 网站',
|
||||
proxyCache: '反代缓存',
|
||||
|
||||
@@ -30,7 +30,9 @@
|
||||
<el-tag type="success">{{ $t('website.weight') }}: {{ item.weight }}</el-tag>
|
||||
</td>
|
||||
<td v-if="item.failTimeout != ''">
|
||||
<el-tag type="warning">{{ $t('website.failTimeout') }}: {{ item.failTimeout }}</el-tag>
|
||||
<el-tag type="warning">
|
||||
{{ $t('website.failTimeout') }}: {{ item.failTimeout }} {{ item.failTimeoutUnit }}
|
||||
</el-tag>
|
||||
</td>
|
||||
<td v-if="item.maxFails > 0">
|
||||
<el-tag type="danger">{{ $t('website.maxFails') }}: {{ item.maxFails }}</el-tag>
|
||||
|
||||
@@ -21,78 +21,126 @@
|
||||
</el-select>
|
||||
<span class="input-help">{{ getHelper(item.algorithm) }}</span>
|
||||
</el-form-item>
|
||||
<el-row :gutter="20" v-for="(server, index) of item.servers" :key="index">
|
||||
<el-col :span="7">
|
||||
<el-form-item
|
||||
:label="index == 0 ? $t('setting.address') : ''"
|
||||
:prop="`servers.${index}.server`"
|
||||
:rules="rules.server"
|
||||
>
|
||||
<el-input
|
||||
type="string"
|
||||
v-model="item.servers[index].server"
|
||||
:placeholder="index > 0 ? $t('setting.address') : ''"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="3">
|
||||
<el-form-item
|
||||
:label="index == 0 ? $t('website.weight') : ''"
|
||||
:prop="`servers.${index}.weight`"
|
||||
:rules="rules.weight"
|
||||
>
|
||||
<el-input type="number" v-model.number="item.servers[index].weight"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-form-item
|
||||
:label="index == 0 ? $t('website.maxFails') : ''"
|
||||
:prop="`servers.${index}.maxFails`"
|
||||
:rules="rules.maxFails"
|
||||
>
|
||||
<el-input type="number" v-model.number="item.servers[index].maxFails"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-form-item
|
||||
:label="index == 0 ? $t('website.maxConns') : ''"
|
||||
:prop="`servers.${index}.maxConns`"
|
||||
:rules="rules.maxConns"
|
||||
>
|
||||
<el-input type="number" v-model.number="item.servers[index].maxConns"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="3">
|
||||
<el-form-item
|
||||
:label="index == 0 ? $t('website.strategy') : ''"
|
||||
:prop="`servers.${index}.flag`"
|
||||
:rules="rules.flag"
|
||||
>
|
||||
<el-select v-model="item.servers[index].flag" clearable>
|
||||
<el-option
|
||||
v-for="flag in getStatusStrategy()"
|
||||
:label="flag.label"
|
||||
:key="flag.value"
|
||||
:value="flag.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="3" v-if="index == 0">
|
||||
<el-form-item :label="$t('commons.button.add') + $t('website.server')">
|
||||
<el-button @click="addServer">
|
||||
<el-icon><Plus /></el-icon>
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="3" v-else>
|
||||
<el-form-item>
|
||||
<el-button @click="removeServer(index)" link type="primary">
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div>
|
||||
<el-card v-for="(server, index) of item.servers" :key="index" class="server-card" shadow="hover">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="server-title">
|
||||
<el-icon><Monitor /></el-icon>
|
||||
<span class="ml-2">{{ $t('website.server') }} - {{ server.server }}</span>
|
||||
</span>
|
||||
<el-button
|
||||
v-if="item.servers.length > 1"
|
||||
text
|
||||
type="danger"
|
||||
icon="Delete"
|
||||
size="small"
|
||||
@click="removeServer(index)"
|
||||
></el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="24">
|
||||
<el-form-item
|
||||
:label="$t('setting.address')"
|
||||
:prop="`servers.${index}.server`"
|
||||
:rules="rules.server"
|
||||
>
|
||||
<el-input
|
||||
v-model="item.servers[index].server"
|
||||
:placeholder="'example.com:8080'"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
|
||||
<el-form-item
|
||||
:label="$t('website.weight')"
|
||||
:prop="`servers.${index}.weight`"
|
||||
:rules="rules.weight"
|
||||
>
|
||||
<el-input type="number" v-model.number="item.servers[index].weight"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
|
||||
<el-form-item
|
||||
:label="$t('website.strategy')"
|
||||
:prop="`servers.${index}.flag`"
|
||||
:rules="rules.flag"
|
||||
>
|
||||
<el-select v-model="item.servers[index].flag" clearable>
|
||||
<el-option
|
||||
v-for="flag in getStatusStrategy()"
|
||||
:label="flag.label"
|
||||
:key="flag.value"
|
||||
:value="flag.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
|
||||
<el-form-item
|
||||
:label="$t('website.maxFails')"
|
||||
:prop="`servers.${index}.maxFails`"
|
||||
:rules="rules.maxFails"
|
||||
>
|
||||
<el-input type="number" v-model.number="item.servers[index].maxFails">
|
||||
<template #append>{{ $t('commons.units.time') }}</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
|
||||
<el-form-item :prop="`servers.${index}.failTimeout`" :rules="rules.failTimeout">
|
||||
<template #label>
|
||||
<span class="inline-flex items-center">
|
||||
{{ $t('website.failTimeout') }}
|
||||
<el-tooltip :content="$t('website.failTimeoutHelper')" placement="top">
|
||||
<el-icon><QuestionFilled /></el-icon>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<el-input type="number" v-model.number="item.servers[index].failTimeout">
|
||||
<template #append>
|
||||
<el-select
|
||||
v-model.number="item.servers[index].failTimeoutUnit"
|
||||
class="!w-24"
|
||||
>
|
||||
<el-option
|
||||
v-for="(unit, index) in Units"
|
||||
:key="index"
|
||||
:label="unit.label"
|
||||
:value="unit.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
|
||||
<el-form-item
|
||||
:label="$t('website.maxConns')"
|
||||
:prop="`servers.${index}.maxConns`"
|
||||
:rules="rules.maxConns"
|
||||
>
|
||||
<el-input type="number" v-model.number="item.servers[index].maxConns"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-button class="add-server-btn" type="primary" plain @click="addServer" icon="Plus">
|
||||
{{ $t('commons.button.add') + $t('website.server') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
@@ -114,6 +162,8 @@ import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
import { Rules, checkNumberRange } from '@/global/form-rules';
|
||||
import { getAlgorithms, getStatusStrategy } from '@/global/mimetype';
|
||||
import { Website } from '@/api/interface/website';
|
||||
import { Monitor, QuestionFilled } from '@element-plus/icons-vue';
|
||||
import { Units } from '@/global/mimetype';
|
||||
|
||||
const rules = ref<any>({
|
||||
name: [Rules.appName],
|
||||
@@ -124,7 +174,8 @@ const rules = ref<any>({
|
||||
type: Array,
|
||||
},
|
||||
maxFails: [checkNumberRange(1, 1000)],
|
||||
maxConns: [checkNumberRange(1, 1000)],
|
||||
maxConns: [checkNumberRange(0, 10000)],
|
||||
failTimeout: [checkNumberRange(1, 300)],
|
||||
});
|
||||
|
||||
interface LoadBalanceOperate {
|
||||
@@ -137,6 +188,12 @@ const lbForm = ref<FormInstance>();
|
||||
|
||||
const initServer = () => ({
|
||||
server: '',
|
||||
weight: undefined,
|
||||
maxFails: undefined,
|
||||
maxConns: undefined,
|
||||
failTimeout: undefined,
|
||||
failTimeoutUnit: 's',
|
||||
flag: '',
|
||||
});
|
||||
|
||||
const open = ref(false);
|
||||
@@ -188,11 +245,15 @@ const acceptParams = async (req: LoadBalanceOperate) => {
|
||||
const weight = server.weight == 0 ? undefined : server.weight;
|
||||
const maxFails = server.maxFails == 0 ? undefined : server.maxFails;
|
||||
const maxConns = server.maxConns == 0 ? undefined : server.maxConns;
|
||||
const failTimeout = server.failTimeout == 0 ? undefined : server.failTimeout;
|
||||
const failTimeoutUnit = server.failTimeoutUnit || 's';
|
||||
servers.push({
|
||||
server: server.server,
|
||||
weight: weight,
|
||||
maxFails: maxFails,
|
||||
maxConns: maxConns,
|
||||
failTimeout: failTimeout,
|
||||
failTimeoutUnit: failTimeoutUnit,
|
||||
flag: server.flag,
|
||||
});
|
||||
});
|
||||
@@ -215,6 +276,9 @@ const handleServers = () => {
|
||||
if (!server.maxConns || server.maxConns == '') {
|
||||
server.maxConns = 0;
|
||||
}
|
||||
if (!server.failTimeout || server.failTimeout == '') {
|
||||
server.failTimeout = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -229,6 +293,9 @@ const rollBackServers = () => {
|
||||
if (server.maxConns == 0) {
|
||||
server.maxConns = undefined;
|
||||
}
|
||||
if (server.failTimeout == 0) {
|
||||
server.failTimeout = undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -271,3 +338,48 @@ defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.server-card {
|
||||
margin-bottom: 16px;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
:deep(.el-card__header) {
|
||||
padding: 12px 20px;
|
||||
background-color: var(--el-fill-color-light);
|
||||
}
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 20px;
|
||||
|
||||
.server-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.add-server-btn {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
border-style: dashed;
|
||||
font-size: 14px;
|
||||
margin-top: 8px;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user