feat: 面板设置增加监听地址 (#2663)

This commit is contained in:
ssongliu 2023-10-25 10:41:26 +08:00 committed by GitHub
parent b045261f16
commit d638796798
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 601 additions and 35 deletions

View File

@ -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 更新系统端口

View File

@ -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"`
}

View File

@ -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)

View File

@ -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"`

View File

@ -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)

View File

@ -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)

View File

@ -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
},
}

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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"
},

View File

@ -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"
},

View File

@ -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:

View File

@ -22,6 +22,8 @@ export namespace Setting {
lastCleanData: string;
serverPort: number;
ipv6: string;
bindAddress: string;
ssl: string;
sslType: string;
allowIPs: string;

View File

@ -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);
};

View File

@ -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,

View File

@ -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.',

View File

@ -1119,6 +1119,11 @@ const message = {
hasNewVersion: '有新版本',
safe: '安全',
bindInfo: '監聽地址',
bindAll: '監聽所有',
bindInfoHelper: '修改服務監聽地址或協議可能導致服務不可用是否繼續',
ipv6: '監聽 IPv6',
bindAddress: '監聽地址',
entrance: '安全入口',
showEntrance: '啟用概覽頁未開啟提醒',
entranceHelper: '開啟安全入口後只能通過指定安全入口登錄面板',

View File

@ -1120,6 +1120,11 @@ const message = {
hasNewVersion: '有新版本',
safe: '安全',
bindInfo: '监听地址',
bindAll: '监听所有',
bindInfoHelper: '修改服务监听地址或协议可能导致服务不可用是否继续',
ipv6: '监听 IPv6',
bindAddress: '监听地址',
entrance: '安全入口',
showEntrance: '启用概览页未开启提醒',
entranceHelper: '开启安全入口后只能通过指定安全入口登录面板',

View File

@ -0,0 +1,136 @@
<template>
<div>
<el-drawer v-model="drawerVisible" :destroy-on-close="true" :close-on-click-modal="false" size="30%">
<template #header>
<DrawerHeader :header="$t('setting.bindInfo')" :back="handleClose" />
</template>
<el-form ref="formRef" label-position="top" :model="form" @submit.prevent v-loading="loading">
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form-item :label="$t('setting.ipv6')" prop="ipv6" :rules="Rules.requiredSelect">
<el-radio-group style="width: 100%" v-model="form.ipv6" @change="onChangeMode()">
<el-radio label="enable">{{ $t('commons.button.enable') }}</el-radio>
<el-radio label="disable">{{ $t('commons.button.disable') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="form.ipv6 === 'disable'"
:label="$t('setting.bindAddress')"
prop="bindAddress"
:rules="Rules.ip"
>
<el-checkbox v-model="form.input" :label="$t('container.input')" />
<el-select v-if="!form.input" clearable v-model="form.bindAddress">
<el-option value="0.0.0.0" :label="$t('setting.bindAll') + ' (0.0.0.0)'"></el-option>
<div v-for="item in interfaceOptions" :key="item">
<el-option v-if="item.indexOf(':') === -1" :value="item" :label="item" />
</div>
</el-select>
<el-input v-else clearable v-model="form.bindAddress"></el-input>
</el-form-item>
<el-form-item v-else :label="$t('setting.bindAddress')" prop="bindAddress" :rules="Rules.ipV6">
<el-checkbox v-model="form.input" :label="$t('container.input')" />
<el-select v-if="!form.input" clearable v-model="form.bindAddress">
<el-option value="::" :label="$t('setting.bindAll') + ' (::)'"></el-option>
<div v-for="item in interfaceOptions" :key="item">
<el-option v-if="item.indexOf(':') !== -1" :value="item" :label="item" />
</div>
</el-select>
<el-input v-else clearable v-model="form.bindAddress"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
<el-button :disabled="loading" type="primary" @click="onSavePort(formRef)">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-drawer>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { updateBindInfo, loadInterfaceAddr } from '@/api/modules/setting';
import { ElMessageBox, FormInstance } from 'element-plus';
import { Rules } from '@/global/form-rules';
import { GlobalStore } from '@/store';
import DrawerHeader from '@/components/drawer-header/index.vue';
const globalStore = GlobalStore();
interface DialogProps {
ipv6: string;
bindAddress: string;
}
const drawerVisible = ref();
const loading = ref();
const interfaceOptions = ref();
const form = reactive({
ipv6: '',
bindAddress: '',
input: false,
});
const formRef = ref<FormInstance>();
const acceptParams = (params: DialogProps): void => {
form.ipv6 = params.ipv6;
form.bindAddress = params.bindAddress;
loadInterface();
drawerVisible.value = true;
};
const loadInterface = async () => {
const res = await loadInterfaceAddr();
interfaceOptions.value = res.data || [];
};
const onChangeMode = () => {
form.bindAddress = form.ipv6 === 'enable' ? '::' : '0.0.0.0';
};
const onSavePort = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
ElMessageBox.confirm(i18n.global.t('setting.bindInfoHelper'), i18n.global.t('setting.bindInfo'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
}).then(async () => {
loading.value = true;
await updateBindInfo(form.ipv6, form.bindAddress)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
globalStore.isLogin = false;
let href = window.location.href;
let address = '';
if (globalStore.entrance) {
address = href.replaceAll('settings/safe', globalStore.entrance);
} else {
address = href.replaceAll('settings/safe', 'login');
}
window.open(address, '_self');
})
.catch(() => {
loading.value = false;
});
});
});
};
const handleClose = () => {
drawerVisible.value = false;
};
defineExpose({
acceptParams,
});
</script>

View File

@ -15,7 +15,15 @@
</template>
</el-input>
</el-form-item>
<el-form-item :label="$t('setting.bindInfo')" prop="bindAddress">
<el-input disabled v-model="form.bindAddressItem">
<template #append>
<el-button @click="onChangeBind" icon="Setting">
{{ $t('commons.button.set') }}
</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item :label="$t('setting.entrance')">
<el-input
type="password"
@ -148,10 +156,11 @@
</LayoutContent>
<PortSetting ref="portRef" />
<BindSetting ref="bindRef" />
<MfaSetting ref="mfaRef" @search="search" />
<SSLSetting ref="sslRef" @search="search" />
<EntranceSetting ref="entranceRef" @search="search" />
<TimeoutSetting ref="timeoutref" @search="search" />
<TimeoutSetting ref="timeoutRef" @search="search" />
<DomainSetting ref="domainRef" @search="search" />
<AllowIPsSetting ref="allowIPsRef" @search="search" />
</div>
@ -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() {

1
go.mod
View File

@ -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

2
go.sum
View File

@ -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=