From f2dcb13e0d3ffaa97a967699c36ff45693eedbbf Mon Sep 17 00:00:00 2001 From: coward Date: Mon, 23 Sep 2024 15:58:46 +0800 Subject: [PATCH] =?UTF-8?q?:new:=E5=AF=BC=E5=85=A5=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- component/validator.go | 12 +++ http/api/setting.go | 61 ++++++++++++++- http/param/setting.go | 8 ++ http/router/setting.go | 1 + http/vo/setting.go | 62 ++++++++------- service/setting.go | 59 ++++++++++++++ web/package.json | 1 + web/pnpm-lock.yaml | 8 ++ web/src/api/setting.js | 7 +- .../components/header/components/Export.vue | 78 ++++++++++++++++++- 10 files changed, 261 insertions(+), 36 deletions(-) diff --git a/component/validator.go b/component/validator.go index f1bf270..8f18311 100644 --- a/component/validator.go +++ b/component/validator.go @@ -36,6 +36,18 @@ func Error(err error) string { return errMsg } +// Validate +// @description: 校验 +// @param data +// @return string +func Validate(data any) error { + if v, ok := binding.Validator.Engine().(*validator.Validate); ok { + return v.Struct(data) + } + + return nil +} + // initValidatorTranslator // @description: 初始化翻译机 // @receiver vli diff --git a/http/api/setting.go b/http/api/setting.go index 3450e47..636ee91 100644 --- a/http/api/setting.go +++ b/http/api/setting.go @@ -2,9 +2,13 @@ package api import ( "encoding/json" + "errors" + "fmt" "gitee.ltd/lxh/logger/log" "github.com/gin-gonic/gin" + "os" "slices" + "wireguard-ui/component" "wireguard-ui/http/param" "wireguard-ui/http/response" "wireguard-ui/http/vo" @@ -152,11 +156,62 @@ func (setting) Export(c *gin.Context) { c.Header("Content-Transfer-Encoding", "binary") c.Header("Connection", "keep-alive") c.File(filepath) - //if err = os.Remove(filepath); err != nil { - // log.Errorf("删除临时文件失败: %s", err.Error()) - //} + if err = os.Remove(filepath); err != nil { + log.Errorf("删除临时文件失败: %s", err.Error()) + } } +// Import +// @description: 导入配置 +// @receiver setting +// @param c func (setting) Import(c *gin.Context) { + var p param.Import + if err := c.ShouldBind(&p); err != nil { + response.R(c).Validator(err) + return + } + // 校验文件是否合规 + if p.File.Filename != "config.json" { + response.R(c).Validator(errors.New("文件名不合规")) + return + } + + // 校验文件内容是否符合 + fileBytes, err := p.File.Open() + if err != nil { + response.R(c).FailedWithError(err) + return + } + + var data vo.Export + if err := json.NewDecoder(fileBytes).Decode(&data); err != nil { + response.R(c).FailedWithError(err) + return + } + + // 校验json串是否合规 + if err = component.Validate(&data); err != nil { + response.R(c).Validator(err) + return + } + + // 获取当前登陆用户 + var loginUser *vo.User + if loginUser = GetCurrentLoginUser(c); c.IsAborted() { + return + } + + if loginUser.Account != "admin" { + response.R(c).FailedWithError("非法操作,你被捕啦!") + return + } + + if err = service.Setting().Import(&data, loginUser); err != nil { + response.R(c).FailedWithError(fmt.Errorf("导入失败: %v", err.Error())) + return + } + + response.R(c).OK() } diff --git a/http/param/setting.go b/http/param/setting.go index 401ae99..8314ad5 100644 --- a/http/param/setting.go +++ b/http/param/setting.go @@ -1,5 +1,7 @@ package param +import "mime/multipart" + // SetSetting // @description: 添加/编辑设置 type SetSetting struct { @@ -7,3 +9,9 @@ type SetSetting struct { Data string `json:"data" form:"data" binding:"required"` Describe string `json:"describe" form:"describe" binding:"omitempty"` } + +// Import +// @description: 导入 +type Import struct { + File *multipart.FileHeader `json:"file" form:"file" binding:"required"` +} diff --git a/http/router/setting.go b/http/router/setting.go index 9cd3a3e..fd80496 100644 --- a/http/router/setting.go +++ b/http/router/setting.go @@ -18,5 +18,6 @@ func SettingApi(r *gin.RouterGroup) { setting.GET("/all", api.Setting().GetAllSetting) // 获取全部配置 setting.GET("/public-addr", api.Setting().GetPublicAddr) // 获取公网IP setting.GET("/export", api.Setting().Export) // 导出配置文件 + setting.POST("/import", api.Setting().Import) // 导入配置 } } diff --git a/http/vo/setting.go b/http/vo/setting.go index 6925e6f..a45c4d9 100644 --- a/http/vo/setting.go +++ b/http/vo/setting.go @@ -1,5 +1,7 @@ package vo +import "wireguard-ui/global/constant" + // SettingItem // @description: 设置单项 type SettingItem struct { @@ -9,44 +11,44 @@ type SettingItem struct { } type Export struct { - Global Global `json:"global"` - Server Server `json:"server"` - Clients []Client `json:"clients"` + Global *Global `json:"global" label:"全局配置" binding:"required"` + Server *Server `json:"server" label:"服务端配置" binding:"required"` + Clients []Client `json:"clients" label:"客户端" binding:"omitempty"` } type Global struct { - MTU int `json:"MTU"` - ConfigFilePath string `json:"configFilePath"` - DnsServer []string `json:"dnsServer"` - EndpointAddress string `json:"endpointAddress"` - FirewallMark string `json:"firewallMark"` - PersistentKeepalive int `json:"persistentKeepalive"` - Table string `json:"table"` + MTU int `json:"MTU" label:"MTU" binding:"required"` + ConfigFilePath string `json:"configFilePath" label:"配置文件路径" binding:"required"` + DnsServer []string `json:"dnsServer" label:"DNS" binding:"omitempty"` + EndpointAddress string `json:"endpointAddress" label:"公网地址" binding:"required"` + FirewallMark string `json:"firewallMark" label:"firewallMark" binding:"omitempty"` + PersistentKeepalive int `json:"persistentKeepalive" label:"persistentKeepalive" binding:"required"` + Table string `json:"table" label:"table" binding:"omitempty"` } type Server struct { - IpScope []string `json:"ipScope"` - ListenPort int `json:"listenPort"` - PrivateKey string `json:"privateKey"` - PublicKey string `json:"publicKey"` - PostUpScript string `json:"postUpScript"` - PostDownScript string `json:"postDownScript"` + IpScope []string `json:"ipScope" label:"ipScope" binding:"min=1,dive,required"` + ListenPort int `json:"listenPort" label:"listenPort" binding:"required"` + PrivateKey string `json:"privateKey" label:"privateKey" binding:"required"` + PublicKey string `json:"publicKey" label:"publicKey" binding:"required"` + PostUpScript string `json:"postUpScript" label:"postUpScript" binding:"omitempty"` + PostDownScript string `json:"postDownScript" label:"postDownScript" binding:"omitempty"` } type Client struct { - Name string `json:"name"` - Email string `json:"email"` - SubnetRange string `json:"subnetRange"` - IpAllocation []string `json:"ipAllocation"` - AllowedIps []string `json:"allowedIps"` - ExtraAllowedIps []string `json:"extraAllowedIps"` - Endpoint string `json:"endpoint"` - UseServerDns int `json:"useServerDns"` + Name string `json:"name" label:"name" binding:"required"` + Email string `json:"email" label:"email" binding:"omitempty"` + SubnetRange string `json:"subnetRange" label:"subnetRange" binding:"omitempty"` + IpAllocation []string `json:"ipAllocation" label:"ipAllocation" binding:"min=1,dive,required"` + AllowedIps []string `json:"allowedIps" label:"allowedIps" binding:"min=1,dive,required"` + ExtraAllowedIps []string `json:"extraAllowedIps" label:"extraAllowedIps" binding:"omitempty"` + Endpoint string `json:"endpoint" label:"endpoint" binding:"endpoint"` + UseServerDns *constant.Status `json:"useServerDns" label:"useServerDns" binding:"omitempty"` Keys struct { - PresharedKey string `json:"presharedKey"` - PrivateKey string `json:"privateKey"` - PublicKey string `json:"publicKey"` - } `json:"keys"` - Enabled int `json:"enabled"` - OfflineMonitoring int `json:"offlineMonitoring"` + PresharedKey string `json:"presharedKey" label:"presharedKey" binding:"required"` + PrivateKey string `json:"privateKey" label:"privateKey" binding:"required"` + PublicKey string `json:"publicKey" label:"publicKey" binding:"required"` + } `json:"keys" label:"keys" binding:"required"` + Enabled *constant.Status `json:"enabled" label:"enabled" binding:"required"` + OfflineMonitoring *constant.Status `json:"offlineMonitoring" label:"offlineMonitoring" binding:"required"` } diff --git a/service/setting.go b/service/setting.go index 6ca7296..c833c3e 100644 --- a/service/setting.go +++ b/service/setting.go @@ -2,9 +2,11 @@ package service import ( "encoding/json" + slog "gitee.ltd/lxh/logger/log" "gorm.io/gorm" "strings" gdb "wireguard-ui/global/client" + "wireguard-ui/http/param" "wireguard-ui/http/vo" "wireguard-ui/model" "wireguard-ui/template/render_data" @@ -142,3 +144,60 @@ func (s setting) Export() (data vo.Export, err error) { return } + +// Import +// @description: 导入 +// @receiver s +// @param data +// @return err +func (s setting) Import(data *vo.Export, loginUser *vo.User) (err error) { + // 先更新global配置 + gst, _ := json.Marshal(data.Global) + gs := &model.Setting{ + Code: "WG_SETTING", + Data: string(gst), + Describe: "服务端全局配置", + } + if err = s.SetData(gs); err != nil { + return + } + + st, _ := json.Marshal(data.Server) + ss := &model.Setting{ + Code: "WG_SERVER", + Data: string(st), + Describe: "服务端配置", + } + if err = s.SetData(ss); err != nil { + return + } + + // 更新client配置 + for _, v := range data.Clients { + keys := ¶m.Keys{ + PrivateKey: v.Keys.PrivateKey, + PublicKey: v.Keys.PublicKey, + PresharedKey: v.Keys.PresharedKey, + } + + cc := param.SaveClient{ + Name: v.Name, + Email: v.Email, + IpAllocation: v.IpAllocation, + AllowedIps: v.AllowedIps, + ExtraAllowedIps: v.ExtraAllowedIps, + Endpoint: v.Endpoint, + UseServerDns: v.UseServerDns, + Keys: keys, + Enabled: v.Enabled, + OfflineMonitoring: v.OfflineMonitoring, + } + + if err := Client().SaveClient(cc, loginUser); err != nil { + slog.Errorf("客户端[%s]导入失败: %v", v.Name, err) + continue + } + } + + return nil +} diff --git a/web/package.json b/web/package.json index 6244c83..1c90319 100644 --- a/web/package.json +++ b/web/package.json @@ -32,6 +32,7 @@ }, "dependencies": { "@unocss/eslint-config": "^0.55.7", + "@vicons/ionicons5": "^0.12.0", "@vueuse/core": "^10.4.1", "@wangeditor/editor": "^5.1.23", "@wangeditor/editor-for-vue": "5.1.12", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index eec360c..bebf0f7 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@unocss/eslint-config': specifier: ^0.55.7 version: 0.55.7(eslint@8.50.0)(typescript@5.2.2) + '@vicons/ionicons5': + specifier: ^0.12.0 + version: 0.12.0 '@vueuse/core': specifier: ^10.4.1 version: 10.4.1(vue@3.3.4) @@ -1046,6 +1049,9 @@ packages: '@vavt/util@1.4.0': resolution: {integrity: sha512-qLhaokwifMTFqoo4UE2JZUyaxCzX9T4WcIt2KzznbtBrCM4CG119pY/cKqq6jDa3c1phUvPCoIfjWfnF9nj4NA==} + '@vicons/ionicons5@0.12.0': + resolution: {integrity: sha512-Iy1EUVRpX0WWxeu1VIReR1zsZLMc4fqpt223czR+Rpnrwu7pt46nbnC2ycO7ItI/uqDLJxnbcMC7FujKs9IfFA==} + '@vite-plugin-vue-devtools/core@1.0.0-rc.7': resolution: {integrity: sha512-Tv9JeRZQ6KDwSkOQJvXc5TBcc4fkSazA96GDhi99v4VCthTgXjnhaah41CeZD3hFDKnNS0MHKFFqN+RHAgYDyQ==} peerDependencies: @@ -5490,6 +5496,8 @@ snapshots: '@vavt/util@1.4.0': {} + '@vicons/ionicons5@0.12.0': {} + '@vite-plugin-vue-devtools/core@1.0.0-rc.7(vite@4.4.11(@types/node@20.5.1)(sass@1.69.0)(terser@5.21.0))': dependencies: '@babel/parser': 7.23.0 diff --git a/web/src/api/setting.js b/web/src/api/setting.js index a8cdf78..f692d55 100644 --- a/web/src/api/setting.js +++ b/web/src/api/setting.js @@ -1,5 +1,10 @@ import { request } from '@/utils' export default { - exportConfig: () => request.get('/setting/export'), // 获取当前登陆用户信息 + exportConfig: () => request.get('/setting/export'), // 导出配置 + importConfig: (data) => request.post('/setting/import',data,{ + headers: { + 'Content-Type': 'multipart/form-data' + } + }), } diff --git a/web/src/layout/components/header/components/Export.vue b/web/src/layout/components/header/components/Export.vue index 6ee6e41..b65e848 100644 --- a/web/src/layout/components/header/components/Export.vue +++ b/web/src/layout/components/header/components/Export.vue @@ -5,13 +5,55 @@ + + + +
+ + + +
+ + 点击或者拖动文件到该区域来上传 + + + 注意:上传后将覆盖原有的配置以及客户端数据等,请谨慎操作! + +
+
+ 确定 +