From d638796798c96fa26fbbbf60ffa0cc153322b72a Mon Sep 17 00:00:00 2001
From: ssongliu <73214554+ssongliu@users.noreply.github.com>
Date: Wed, 25 Oct 2023 10:41:26 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E9=9D=A2=E6=9D=BF=E8=AE=BE=E7=BD=AE?=
=?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=9B=91=E5=90=AC=E5=9C=B0=E5=9D=80=20(#2663?=
=?UTF-8?q?)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
backend/app/api/v1/setting.go | 43 ++++++
backend/app/dto/setting.go | 7 +
backend/app/service/setting.go | 38 +++++
backend/configs/system.go | 2 +
backend/init/hook/hook.go | 10 ++
backend/init/migration/migrate.go | 2 +
backend/init/migration/migrations/v_1_8.go | 13 ++
backend/router/ro_setting.go | 2 +
backend/server/server.go | 50 ++++---
cmd/server/docs/docs.go | 94 +++++++++++-
cmd/server/docs/swagger.json | 90 ++++++++++++
cmd/server/docs/swagger.yaml | 58 ++++++++
frontend/src/api/interface/setting.ts | 2 +
frontend/src/api/modules/setting.ts | 8 ++
frontend/src/global/form-rules.ts | 33 +++++
frontend/src/lang/modules/en.ts | 6 +
frontend/src/lang/modules/tw.ts | 5 +
frontend/src/lang/modules/zh.ts | 5 +
.../src/views/setting/safe/bind/index.vue | 136 ++++++++++++++++++
frontend/src/views/setting/safe/index.vue | 29 +++-
go.mod | 1 -
go.sum | 2 -
22 files changed, 601 insertions(+), 35 deletions(-)
create mode 100644 frontend/src/views/setting/safe/bind/index.vue
diff --git a/backend/app/api/v1/setting.go b/backend/app/api/v1/setting.go
index 767365dc3..0b606485a 100644
--- a/backend/app/api/v1/setting.go
+++ b/backend/app/api/v1/setting.go
@@ -150,6 +150,49 @@ func (b *BaseApi) DownloadSSL(c *gin.Context) {
c.File(pathItem)
}
+// @Tags System Setting
+// @Summary Load system address
+// @Description 获取系统地址信息
+// @Accept json
+// @Success 200
+// @Security ApiKeyAuth
+// @Router /settings/interface [get]
+func (b *BaseApi) LoadInterfaceAddr(c *gin.Context) {
+ data, err := settingService.LoadInterfaceAddr()
+ if err != nil {
+ helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
+ return
+ }
+ helper.SuccessWithData(c, data)
+}
+
+// @Tags System Setting
+// @Summary Update system bind info
+// @Description 更新系统监听信息
+// @Accept json
+// @Param request body dto.BindInfo true "request"
+// @Success 200
+// @Security ApiKeyAuth
+// @Router /settings/bind/update [post]
+// @x-panel-log {"bodyKeys":["ipv6", "bindAddress"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改系统监听信息 => ipv6: [ipv6], 监听 IP: [bindAddress]","formatEN":"update system bind info => ipv6: [ipv6], 监听 IP: [bindAddress]"}
+func (b *BaseApi) UpdateBindInfo(c *gin.Context) {
+ var req dto.BindInfo
+ 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
+ }
+
+ if err := settingService.UpdateBindInfo(req); err != nil {
+ helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
+ return
+ }
+ helper.SuccessWithData(c, nil)
+}
+
// @Tags System Setting
// @Summary Update system port
// @Description 更新系统端口
diff --git a/backend/app/dto/setting.go b/backend/app/dto/setting.go
index 271ec1020..3e099dcaf 100644
--- a/backend/app/dto/setting.go
+++ b/backend/app/dto/setting.go
@@ -14,6 +14,8 @@ type SettingInfo struct {
NtpSite string `json:"ntpSite"`
Port string `json:"port"`
+ Ipv6 string `json:"ipv6"`
+ BindAddress string `json:"bindAddress"`
PanelName string `json:"panelName"`
Theme string `json:"theme"`
Language string `json:"language"`
@@ -136,6 +138,11 @@ type SyncTime struct {
NtpSite string `json:"ntpSite"`
}
+type BindInfo struct {
+ Ipv6 string `json:"ipv6" validate:"required,oneof=enable disable"`
+ BindAddress string `json:"bindAddress" validate:"required"`
+}
+
type Upgrade struct {
Version string `json:"version"`
}
diff --git a/backend/app/service/setting.go b/backend/app/service/setting.go
index 4f3ae4ef4..9001a14f3 100644
--- a/backend/app/service/setting.go
+++ b/backend/app/service/setting.go
@@ -6,6 +6,7 @@ import (
"encoding/json"
"encoding/pem"
"fmt"
+ "net"
"os"
"path"
"strconv"
@@ -30,10 +31,12 @@ type SettingService struct{}
type ISettingService interface {
GetSettingInfo() (*dto.SettingInfo, error)
+ LoadInterfaceAddr() ([]string, error)
LoadTimeZone() ([]string, error)
Update(key, value string) error
UpdatePassword(c *gin.Context, old, new string) error
UpdatePort(port uint) error
+ UpdateBindInfo(req dto.BindInfo) error
UpdateSSL(c *gin.Context, req dto.SSLUpdate) error
LoadFromCert() (*dto.SSLInfo, error)
HandlePasswordExpired(c *gin.Context, old, new string) error
@@ -160,6 +163,41 @@ func (u *SettingService) SyncTime(req dto.SyncTime) error {
return nil
}
+func (u *SettingService) LoadInterfaceAddr() ([]string, error) {
+ addrMap := make(map[string]struct{})
+ addrs, err := net.InterfaceAddrs()
+ if err != nil {
+ return nil, err
+ }
+ for _, addr := range addrs {
+ ipNet, ok := addr.(*net.IPNet)
+ if ok && ipNet.IP.To16() != nil {
+ addrMap[ipNet.IP.String()] = struct{}{}
+ }
+ }
+ var data []string
+ for key := range addrMap {
+ data = append(data, key)
+ }
+ return data, nil
+}
+
+func (u *SettingService) UpdateBindInfo(req dto.BindInfo) error {
+ if err := settingRepo.Update("Ipv6", req.Ipv6); err != nil {
+ return err
+ }
+ if err := settingRepo.Update("BindAddress", req.BindAddress); err != nil {
+ return err
+ }
+ go func() {
+ _, err := cmd.Exec("systemctl restart 1panel.service")
+ if err != nil {
+ global.LOG.Errorf("restart system with new bind info failed, err: %v", err)
+ }
+ }()
+ return nil
+}
+
func (u *SettingService) UpdatePort(port uint) error {
if common.ScanPort(int(port)) {
return buserr.WithDetail(constant.ErrPortInUsed, port, nil)
diff --git a/backend/configs/system.go b/backend/configs/system.go
index 3e46799dc..11ff98033 100644
--- a/backend/configs/system.go
+++ b/backend/configs/system.go
@@ -2,6 +2,8 @@ package configs
type System struct {
Port string `mapstructure:"port"`
+ Ipv6 string `mapstructure:"ipv6"`
+ BindAddress string `mapstructure:"bindAddress"`
SSL string `mapstructure:"ssl"`
DbFile string `mapstructure:"db_file"`
DbPath string `mapstructure:"db_path"`
diff --git a/backend/init/hook/hook.go b/backend/init/hook/hook.go
index 1bbf35dbc..7422826cb 100644
--- a/backend/init/hook/hook.go
+++ b/backend/init/hook/hook.go
@@ -18,6 +18,16 @@ func Init() {
global.LOG.Errorf("load service port from setting failed, err: %v", err)
}
global.CONF.System.Port = portSetting.Value
+ ipv6Setting, err := settingRepo.Get(settingRepo.WithByKey("Ipv6"))
+ if err != nil {
+ global.LOG.Errorf("load ipv6 status from setting failed, err: %v", err)
+ }
+ global.CONF.System.Ipv6 = ipv6Setting.Value
+ bindAddressSetting, err := settingRepo.Get(settingRepo.WithByKey("BindAddress"))
+ if err != nil {
+ global.LOG.Errorf("load bind address from setting failed, err: %v", err)
+ }
+ global.CONF.System.BindAddress = bindAddressSetting.Value
sslSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
if err != nil {
global.LOG.Errorf("load service ssl from setting failed, err: %v", err)
diff --git a/backend/init/migration/migrate.go b/backend/init/migration/migrate.go
index 568b0f09e..580b95099 100644
--- a/backend/init/migration/migrate.go
+++ b/backend/init/migration/migrate.go
@@ -47,7 +47,9 @@ func Init() {
migrations.AddDefaultNetwork,
migrations.UpdateRuntime,
migrations.UpdateTag,
+
migrations.AddFavorite,
+ migrations.AddBindAddress,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
diff --git a/backend/init/migration/migrations/v_1_8.go b/backend/init/migration/migrations/v_1_8.go
index 84dd19ff7..0549e3dac 100644
--- a/backend/init/migration/migrations/v_1_8.go
+++ b/backend/init/migration/migrations/v_1_8.go
@@ -15,3 +15,16 @@ var AddFavorite = &gormigrate.Migration{
return nil
},
}
+
+var AddBindAddress = &gormigrate.Migration{
+ ID: "20231024-add-bind-address",
+ Migrate: func(tx *gorm.DB) error {
+ if err := tx.Create(&model.Setting{Key: "BindAddress", Value: "0.0.0.0"}).Error; err != nil {
+ return err
+ }
+ if err := tx.Create(&model.Setting{Key: "Ipv6", Value: "disable"}).Error; err != nil {
+ return err
+ }
+ return nil
+ },
+}
diff --git a/backend/router/ro_setting.go b/backend/router/ro_setting.go
index 20b399813..9cb98ee50 100644
--- a/backend/router/ro_setting.go
+++ b/backend/router/ro_setting.go
@@ -22,6 +22,8 @@ func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) {
router.POST("/expired/handle", baseApi.HandlePasswordExpired)
settingRouter.GET("/search/available", baseApi.GetSystemAvailable)
settingRouter.POST("/update", baseApi.UpdateSetting)
+ settingRouter.GET("/interface", baseApi.LoadInterfaceAddr)
+ settingRouter.POST("/bind/update", baseApi.UpdateBindInfo)
settingRouter.POST("/port/update", baseApi.UpdatePort)
settingRouter.POST("/ssl/update", baseApi.UpdateSSL)
settingRouter.GET("/ssl/info", baseApi.LoadFromCert)
diff --git a/backend/server/server.go b/backend/server/server.go
index 8b4a6b78f..8e71f661f 100644
--- a/backend/server/server.go
+++ b/backend/server/server.go
@@ -4,10 +4,10 @@ import (
"crypto/tls"
"encoding/gob"
"fmt"
+ "net"
"net/http"
"os"
"path"
- "time"
"github.com/1Panel-dev/1Panel/backend/init/app"
"github.com/1Panel-dev/1Panel/backend/init/business"
@@ -26,7 +26,6 @@ import (
"github.com/1Panel-dev/1Panel/backend/init/validator"
"github.com/1Panel-dev/1Panel/backend/init/viper"
- "github.com/fvbock/endless"
"github.com/gin-gonic/gin"
)
@@ -46,19 +45,17 @@ func Start() {
hook.Init()
rootRouter := router.Routers()
- address := fmt.Sprintf(":%s", global.CONF.System.Port)
- s := endless.NewServer(address, rootRouter)
- s.ReadHeaderTimeout = 20 * time.Second
- s.WriteTimeout = 60 * time.Second
- s.MaxHeaderBytes = 1 << 20
- if global.CONF.System.SSL == "disable" {
- global.LOG.Infof("server run success on %s with http", global.CONF.System.Port)
- if err := s.ListenAndServe(); err != nil {
- global.LOG.Error(err)
- panic(err)
- }
- } else {
+ tcpItem := "tcp4"
+ if global.CONF.System.Ipv6 == "enable" {
+ tcpItem = "tcp"
+ global.CONF.System.BindAddress = fmt.Sprintf("[%s]", global.CONF.System.BindAddress)
+ }
+ server := &http.Server{
+ Addr: global.CONF.System.BindAddress + ":" + global.CONF.System.Port,
+ Handler: rootRouter,
+ }
+ if global.CONF.System.SSL == "enable" {
certificate, err := os.ReadFile(path.Join(global.CONF.System.BaseDir, "1panel/secret/server.crt"))
if err != nil {
panic(err)
@@ -71,18 +68,19 @@ func Start() {
if err != nil {
panic(err)
}
- s := &http.Server{
- Addr: address,
- Handler: rootRouter,
- TLSConfig: &tls.Config{
- Certificates: []tls.Certificate{cert},
- },
- }
-
- global.LOG.Infof("server run success on %s with https", global.CONF.System.Port)
- if err := s.ListenAndServeTLS("", ""); err != nil {
- global.LOG.Error(err)
- panic(err)
+ server.TLSConfig = &tls.Config{
+ Certificates: []tls.Certificate{cert},
}
}
+ global.LOG.Infof("listen at %s:%s [%s]", global.CONF.System.BindAddress, global.CONF.System.Port, tcpItem)
+ ln, err := net.Listen(tcpItem, global.CONF.System.BindAddress+":"+global.CONF.System.Port)
+ if err != nil {
+ panic(err)
+ }
+ type tcpKeepAliveListener struct {
+ *net.TCPListener
+ }
+ if err := server.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}); err != nil {
+ panic(err)
+ }
}
diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go
index aa0229a88..97ed1396a 100644
--- a/cmd/server/docs/docs.go
+++ b/cmd/server/docs/docs.go
@@ -1,5 +1,5 @@
-// Package docs GENERATED BY SWAG; DO NOT EDIT
-// This file was generated by swaggo/swag
+// Code generated by swaggo/swag. DO NOT EDIT.
+
package docs
import "github.com/swaggo/swag"
@@ -8931,6 +8931,49 @@ const docTemplate = `{
}
}
},
+ "/settings/bind/update": {
+ "post": {
+ "security": [
+ {
+ "ApiKeyAuth": []
+ }
+ ],
+ "description": "更新系统监听信息",
+ "consumes": [
+ "application/json"
+ ],
+ "tags": [
+ "System Setting"
+ ],
+ "summary": "Update system bind info",
+ "parameters": [
+ {
+ "description": "request",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/dto.BindInfo"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ },
+ "x-panel-log": {
+ "BeforeFunctions": [],
+ "bodyKeys": [
+ "ipv6",
+ "bindAddress"
+ ],
+ "formatEN": "update system bind info =\u003e ipv6: [ipv6], 监听 IP: [bindAddress]",
+ "formatZH": "修改系统监听信息 =\u003e ipv6: [ipv6], 监听 IP: [bindAddress]",
+ "paramKeys": []
+ }
+ }
+ },
"/settings/clean": {
"post": {
"security": [
@@ -9014,6 +9057,28 @@ const docTemplate = `{
}
}
},
+ "/settings/interface": {
+ "get": {
+ "security": [
+ {
+ "ApiKeyAuth": []
+ }
+ ],
+ "description": "获取系统地址信息",
+ "consumes": [
+ "application/json"
+ ],
+ "tags": [
+ "System Setting"
+ ],
+ "summary": "Load system address",
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
"/settings/mfa": {
"post": {
"security": [
@@ -12501,6 +12566,25 @@ const docTemplate = `{
}
}
},
+ "dto.BindInfo": {
+ "type": "object",
+ "required": [
+ "bindAddress",
+ "ipv6"
+ ],
+ "properties": {
+ "bindAddress": {
+ "type": "string"
+ },
+ "ipv6": {
+ "type": "string",
+ "enum": [
+ "enable",
+ "disable"
+ ]
+ }
+ }
+ },
"dto.CaptchaResponse": {
"type": "object",
"properties": {
@@ -15379,6 +15463,9 @@ const docTemplate = `{
"appStoreVersion": {
"type": "string"
},
+ "bindAddress": {
+ "type": "string"
+ },
"bindDomain": {
"type": "string"
},
@@ -15403,6 +15490,9 @@ const docTemplate = `{
"expirationTime": {
"type": "string"
},
+ "ipv6": {
+ "type": "string"
+ },
"language": {
"type": "string"
},
diff --git a/cmd/server/docs/swagger.json b/cmd/server/docs/swagger.json
index b873464d3..4391b1d9e 100644
--- a/cmd/server/docs/swagger.json
+++ b/cmd/server/docs/swagger.json
@@ -8924,6 +8924,49 @@
}
}
},
+ "/settings/bind/update": {
+ "post": {
+ "security": [
+ {
+ "ApiKeyAuth": []
+ }
+ ],
+ "description": "更新系统监听信息",
+ "consumes": [
+ "application/json"
+ ],
+ "tags": [
+ "System Setting"
+ ],
+ "summary": "Update system bind info",
+ "parameters": [
+ {
+ "description": "request",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/dto.BindInfo"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ },
+ "x-panel-log": {
+ "BeforeFunctions": [],
+ "bodyKeys": [
+ "ipv6",
+ "bindAddress"
+ ],
+ "formatEN": "update system bind info =\u003e ipv6: [ipv6], 监听 IP: [bindAddress]",
+ "formatZH": "修改系统监听信息 =\u003e ipv6: [ipv6], 监听 IP: [bindAddress]",
+ "paramKeys": []
+ }
+ }
+ },
"/settings/clean": {
"post": {
"security": [
@@ -9007,6 +9050,28 @@
}
}
},
+ "/settings/interface": {
+ "get": {
+ "security": [
+ {
+ "ApiKeyAuth": []
+ }
+ ],
+ "description": "获取系统地址信息",
+ "consumes": [
+ "application/json"
+ ],
+ "tags": [
+ "System Setting"
+ ],
+ "summary": "Load system address",
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
"/settings/mfa": {
"post": {
"security": [
@@ -12494,6 +12559,25 @@
}
}
},
+ "dto.BindInfo": {
+ "type": "object",
+ "required": [
+ "bindAddress",
+ "ipv6"
+ ],
+ "properties": {
+ "bindAddress": {
+ "type": "string"
+ },
+ "ipv6": {
+ "type": "string",
+ "enum": [
+ "enable",
+ "disable"
+ ]
+ }
+ }
+ },
"dto.CaptchaResponse": {
"type": "object",
"properties": {
@@ -15372,6 +15456,9 @@
"appStoreVersion": {
"type": "string"
},
+ "bindAddress": {
+ "type": "string"
+ },
"bindDomain": {
"type": "string"
},
@@ -15396,6 +15483,9 @@
"expirationTime": {
"type": "string"
},
+ "ipv6": {
+ "type": "string"
+ },
"language": {
"type": "string"
},
diff --git a/cmd/server/docs/swagger.yaml b/cmd/server/docs/swagger.yaml
index 4198ad127..b48b35689 100644
--- a/cmd/server/docs/swagger.yaml
+++ b/cmd/server/docs/swagger.yaml
@@ -122,6 +122,19 @@ definitions:
required:
- type
type: object
+ dto.BindInfo:
+ properties:
+ bindAddress:
+ type: string
+ ipv6:
+ enum:
+ - enable
+ - disable
+ type: string
+ required:
+ - bindAddress
+ - ipv6
+ type: object
dto.CaptchaResponse:
properties:
captchaID:
@@ -2064,6 +2077,8 @@ definitions:
type: string
appStoreVersion:
type: string
+ bindAddress:
+ type: string
bindDomain:
type: string
complexityVerification:
@@ -2080,6 +2095,8 @@ definitions:
type: string
expirationTime:
type: string
+ ipv6:
+ type: string
language:
type: string
lastCleanData:
@@ -9879,6 +9896,34 @@ paths:
summary: Load local backup dir
tags:
- System Setting
+ /settings/bind/update:
+ post:
+ consumes:
+ - application/json
+ description: 更新系统监听信息
+ parameters:
+ - description: request
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/dto.BindInfo'
+ responses:
+ "200":
+ description: OK
+ security:
+ - ApiKeyAuth: []
+ summary: Update system bind info
+ tags:
+ - System Setting
+ x-panel-log:
+ BeforeFunctions: []
+ bodyKeys:
+ - ipv6
+ - bindAddress
+ formatEN: 'update system bind info => ipv6: [ipv6], 监听 IP: [bindAddress]'
+ formatZH: '修改系统监听信息 => ipv6: [ipv6], 监听 IP: [bindAddress]'
+ paramKeys: []
/settings/clean:
post:
consumes:
@@ -9933,6 +9978,19 @@ paths:
formatEN: reset an expired Password
formatZH: 重置过期密码
paramKeys: []
+ /settings/interface:
+ get:
+ consumes:
+ - application/json
+ description: 获取系统地址信息
+ responses:
+ "200":
+ description: OK
+ security:
+ - ApiKeyAuth: []
+ summary: Load system address
+ tags:
+ - System Setting
/settings/mfa:
post:
consumes:
diff --git a/frontend/src/api/interface/setting.ts b/frontend/src/api/interface/setting.ts
index 5080f391a..3ec666ed7 100644
--- a/frontend/src/api/interface/setting.ts
+++ b/frontend/src/api/interface/setting.ts
@@ -22,6 +22,8 @@ export namespace Setting {
lastCleanData: string;
serverPort: number;
+ ipv6: string;
+ bindAddress: string;
ssl: string;
sslType: string;
allowIPs: string;
diff --git a/frontend/src/api/modules/setting.ts b/frontend/src/api/modules/setting.ts
index 712a46682..231cf4b45 100644
--- a/frontend/src/api/modules/setting.ts
+++ b/frontend/src/api/modules/setting.ts
@@ -21,6 +21,14 @@ export const updatePassword = (param: Setting.PasswordUpdate) => {
return http.post(`/settings/password/update`, param);
};
+export const loadInterfaceAddr = () => {
+ return http.get(`/settings/interface`);
+};
+
+export const updateBindInfo = (ipv6: string, bindAddress: string) => {
+ return http.post(`/settings/bind/update`, { ipv6: ipv6, bindAddress: bindAddress });
+};
+
export const updatePort = (param: Setting.PortUpdate) => {
return http.post(`/settings/port/update`, param);
};
diff --git a/frontend/src/global/form-rules.ts b/frontend/src/global/form-rules.ts
index 997865695..bbc50382b 100644
--- a/frontend/src/global/form-rules.ts
+++ b/frontend/src/global/form-rules.ts
@@ -22,6 +22,33 @@ const checkIp = (rule: any, value: any, callback: any) => {
}
};
+const checkIpV6 = (rule: any, value: any, callback: any) => {
+ if (value === '' || typeof value === 'undefined' || value == null) {
+ callback(new Error(i18n.global.t('commons.rule.requiredInput')));
+ } else {
+ const IPv4SegmentFormat = '(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';
+ const IPv4AddressFormat = `(${IPv4SegmentFormat}[.]){3}${IPv4SegmentFormat}`;
+ const IPv6SegmentFormat = '(?:[0-9a-fA-F]{1,4})';
+ const IPv6AddressRegExp = new RegExp(
+ '^(' +
+ `(?:${IPv6SegmentFormat}:){7}(?:${IPv6SegmentFormat}|:)|` +
+ `(?:${IPv6SegmentFormat}:){6}(?:${IPv4AddressFormat}|:${IPv6SegmentFormat}|:)|` +
+ `(?:${IPv6SegmentFormat}:){5}(?::${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,2}|:)|` +
+ `(?:${IPv6SegmentFormat}:){4}(?:(:${IPv6SegmentFormat}){0,1}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,3}|:)|` +
+ `(?:${IPv6SegmentFormat}:){3}(?:(:${IPv6SegmentFormat}){0,2}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,4}|:)|` +
+ `(?:${IPv6SegmentFormat}:){2}(?:(:${IPv6SegmentFormat}){0,3}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,5}|:)|` +
+ `(?:${IPv6SegmentFormat}:){1}(?:(:${IPv6SegmentFormat}){0,4}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,6}|:)|` +
+ `(?::((?::${IPv6SegmentFormat}){0,5}:${IPv4AddressFormat}|(?::${IPv6SegmentFormat}){1,7}|:))` +
+ ')(%[0-9a-zA-Z-.:]{1,})?$',
+ );
+ if (!IPv6AddressRegExp.test(value) && value !== '') {
+ callback(new Error(i18n.global.t('commons.rule.ip')));
+ } else {
+ callback();
+ }
+ }
+};
+
const checkIpV4V6OrDomain = (rule: any, value: any, callback: any) => {
if (value === '' || typeof value === 'undefined' || value == null) {
callback(new Error(i18n.global.t('commons.rule.requiredInput')));
@@ -453,6 +480,7 @@ interface CommonRule {
integerNumberWith0: FormItemRule;
floatNumber: FormItemRule;
ip: FormItemRule;
+ ipV6: FormItemRule;
ipV4V6OrDomain: FormItemRule;
host: FormItemRule;
illegal: FormItemRule;
@@ -576,6 +604,11 @@ export const Rules: CommonRule = {
required: true,
trigger: 'blur',
},
+ ipV6: {
+ validator: checkIpV6,
+ required: true,
+ trigger: 'blur',
+ },
ipV4V6OrDomain: {
validator: checkIpV4V6OrDomain,
required: true,
diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts
index e2f94d340..b6c3c2405 100644
--- a/frontend/src/lang/modules/en.ts
+++ b/frontend/src/lang/modules/en.ts
@@ -1125,6 +1125,12 @@ const message = {
path: 'Path',
safe: 'Security',
+ bindInfo: 'BindInfo',
+ bindAll: 'Listen All',
+ bindInfoHelper:
+ 'Changing the service listening address or protocol may result in service unavailability. Do you want to continue?',
+ ipv6: 'Listen IPv6',
+ bindAddress: 'Listen Address',
entrance: 'Entrance',
showEntrance: 'Enable Home Page Notification Not Enabled',
entranceHelper: 'Enabling secure entry will only allow logging in to the panel through specified secure entry.',
diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts
index 89eb6f1cb..cb6ed5944 100644
--- a/frontend/src/lang/modules/tw.ts
+++ b/frontend/src/lang/modules/tw.ts
@@ -1119,6 +1119,11 @@ const message = {
hasNewVersion: '有新版本',
safe: '安全',
+ bindInfo: '監聽地址',
+ bindAll: '監聽所有',
+ bindInfoHelper: '修改服務監聽地址或協議可能導致服務不可用,是否繼續?',
+ ipv6: '監聽 IPv6',
+ bindAddress: '監聽地址',
entrance: '安全入口',
showEntrance: '啟用概覽頁未開啟提醒',
entranceHelper: '開啟安全入口後只能通過指定安全入口登錄面板',
diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts
index 9cb12b729..6cdeba110 100644
--- a/frontend/src/lang/modules/zh.ts
+++ b/frontend/src/lang/modules/zh.ts
@@ -1120,6 +1120,11 @@ const message = {
hasNewVersion: '有新版本',
safe: '安全',
+ bindInfo: '监听地址',
+ bindAll: '监听所有',
+ bindInfoHelper: '修改服务监听地址或协议可能导致服务不可用,是否继续?',
+ ipv6: '监听 IPv6',
+ bindAddress: '监听地址',
entrance: '安全入口',
showEntrance: '启用概览页未开启提醒',
entranceHelper: '开启安全入口后只能通过指定安全入口登录面板',
diff --git a/frontend/src/views/setting/safe/bind/index.vue b/frontend/src/views/setting/safe/bind/index.vue
new file mode 100644
index 000000000..607e2cf47
--- /dev/null
+++ b/frontend/src/views/setting/safe/bind/index.vue
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('commons.button.enable') }}
+ {{ $t('commons.button.disable') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/views/setting/safe/index.vue b/frontend/src/views/setting/safe/index.vue
index 982df84cb..93edd3cc3 100644
--- a/frontend/src/views/setting/safe/index.vue
+++ b/frontend/src/views/setting/safe/index.vue
@@ -15,7 +15,15 @@
-
+
+
+
+
+ {{ $t('commons.button.set') }}
+
+
+
+
+
-
+
@@ -161,6 +170,7 @@
import { ref, reactive, onMounted } from 'vue';
import { ElForm, ElMessageBox } from 'element-plus';
import PortSetting from '@/views/setting/safe/port/index.vue';
+import BindSetting from '@/views/setting/safe/bind/index.vue';
import SSLSetting from '@/views/setting/safe/ssl/index.vue';
import MfaSetting from '@/views/setting/safe/mfa/index.vue';
import TimeoutSetting from '@/views/setting/safe/timeout/index.vue';
@@ -177,7 +187,8 @@ const globalStore = GlobalStore();
const loading = ref(false);
const entranceRef = ref();
const portRef = ref();
-const timeoutref = ref();
+const bindRef = ref();
+const timeoutRef = ref();
const mfaRef = ref();
const sslRef = ref();
@@ -187,6 +198,9 @@ const allowIPsRef = ref();
const form = reactive({
serverPort: 9999,
+ ipv6: 'disable',
+ bindAddress: '',
+ bindAddressItem: '',
ssl: 'disable',
sslType: 'self',
securityEntrance: '',
@@ -204,6 +218,10 @@ const unset = ref(i18n.global.t('setting.unSetting'));
const search = async () => {
const res = await getSettingInfo();
form.serverPort = Number(res.data.serverPort);
+ form.ipv6 = res.data.ipv6;
+ form.bindAddress = res.data.bindAddress;
+ let proto = form.ipv6 === 'enable' ? 'ipv6' : 'ipv4';
+ form.bindAddressItem = ' [' + proto + '] ' + res.data.bindAddress;
form.ssl = res.data.ssl;
form.sslType = res.data.sslType;
if (form.ssl === 'enable') {
@@ -259,6 +277,9 @@ const onChangeEntrance = () => {
const onChangePort = () => {
portRef.value.acceptParams({ serverPort: form.serverPort });
};
+const onChangeBind = () => {
+ bindRef.value.acceptParams({ ipv6: form.ipv6, bindAddress: form.bindAddress });
+};
const onChangeBindDomain = () => {
domainRef.value.acceptParams({ bindDomain: form.bindDomain });
};
@@ -305,7 +326,7 @@ const loadInfo = async () => {
};
const onChangeExpirationTime = async () => {
- timeoutref.value.acceptParams({ expirationDays: form.expirationDays });
+ timeoutRef.value.acceptParams({ expirationDays: form.expirationDays });
};
function loadTimeOut() {
diff --git a/go.mod b/go.mod
index aaec20818..4d6fb9df3 100644
--- a/go.mod
+++ b/go.mod
@@ -12,7 +12,6 @@ require (
github.com/docker/docker v23.0.3+incompatible
github.com/docker/go-connections v0.4.0
github.com/fsnotify/fsnotify v1.6.0
- github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6
github.com/gin-contrib/gzip v0.0.6
github.com/gin-contrib/i18n v0.0.1
github.com/gin-gonic/gin v1.9.1
diff --git a/go.sum b/go.sum
index 616975c39..c82630710 100644
--- a/go.sum
+++ b/go.sum
@@ -228,8 +228,6 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
-github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 h1:6VSn3hB5U5GeA6kQw4TwWIWbOhtvR2hmbBJnTOtqTWc=
-github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6/go.mod h1:YxOVT5+yHzKvwhsiSIWmbAYM3Dr9AEEbER2dVayfBkg=
github.com/fvbommel/sortorder v1.0.2 h1:mV4o8B2hKboCdkJm+a7uX/SIpZob4JzUpc5GGnM45eo=
github.com/fvbommel/sortorder v1.0.2/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=