diff --git a/go.mod b/go.mod index 0853b89..d109de9 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,8 @@ require ( github.com/google/uuid v1.6.0 github.com/mojocn/base64Captcha v1.3.6 github.com/redis/go-redis/v9 v9.5.1 + github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e + github.com/spf13/cast v1.6.0 golang.org/x/crypto v0.21.0 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 gopkg.in/yaml.v3 v3.0.1 @@ -45,14 +47,14 @@ require ( github.com/google/go-cmp v0.5.9 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect - github.com/jackc/pgx/v5 v5.4.3 // indirect + github.com/jackc/pgx/v5 v5.5.5 // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/josharian/native v1.1.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect - github.com/kr/pretty v0.3.1 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/lixh00/loki-client-go v1.0.1 // indirect github.com/mattn/go-isatty v0.0.19 // indirect @@ -66,6 +68,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/natefinch/lumberjack v2.0.0+incompatible // indirect + github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.13.0 // indirect @@ -74,7 +77,6 @@ require ( github.com/prometheus/procfs v0.8.0 // indirect github.com/prometheus/prometheus v1.8.2-0.20201028100903-3245b3267b24 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect - github.com/spf13/cast v1.6.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect go.uber.org/atomic v1.10.0 // indirect @@ -92,6 +94,6 @@ require ( google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20221018160656-63c7b68cfc55 // indirect google.golang.org/grpc v1.50.1 // indirect - google.golang.org/protobuf v1.30.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index a77a677..13ef60a 100644 --- a/go.sum +++ b/go.sum @@ -156,7 +156,6 @@ github.com/cowardmrx/go_aliyun_oss v1.0.7 h1:MCSKUWi4RZnHhwe4fd7VAsgeRXL0kT9z56T github.com/cowardmrx/go_aliyun_oss v1.0.7/go.mod h1:xz6B8H840TX7yPcgSLUbK7q6nnEsxFutaltR08Aetdg= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -194,6 +193,8 @@ github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/ github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= @@ -473,8 +474,10 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY= -github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= +github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= +github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= @@ -621,6 +624,8 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= @@ -663,7 +668,6 @@ github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHu github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -727,7 +731,6 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= @@ -750,6 +753,8 @@ github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -1256,8 +1261,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/http/api/client.go b/http/api/client.go index 1a37f1f..65d62da 100644 --- a/http/api/client.go +++ b/http/api/client.go @@ -1,10 +1,15 @@ package api import ( + "encoding/json" + "fmt" "gitee.ltd/lxh/logger/log" "github.com/gin-gonic/gin" + "os" + "strings" "wireguard-dashboard/http/param" "wireguard-dashboard/model/entity" + "wireguard-dashboard/model/template_data" "wireguard-dashboard/queues" "wireguard-dashboard/repository" "wireguard-dashboard/utils" @@ -55,7 +60,7 @@ func (clients) Save(c *gin.Context) { _, err := repository.Client().Save(p, info.(*entity.User).Id) if err != nil { - utils.GinResponse(c).FailedWithMsg("操作失败") + utils.GinResponse(c).FailedWithErr("操作失败", err) return } @@ -67,3 +72,176 @@ func (clients) Save(c *gin.Context) { utils.GinResponse(c).OK() } + +// Delete +// @description: 删除客户端 +// @receiver clients +// @param c +func (clients) Delete(c *gin.Context) { + var id = c.Param("id") + if id == "" || id == "undefined" { + utils.GinResponse(c).FailedWithMsg("参数错误") + return + } + + if err := repository.Client().Delete(id); err != nil { + utils.GinResponse(c).FailedWithMsg("操作失败") + return + } + + utils.GinResponse(c).OK() +} + +// Download +// @description: 下载配置文件 +// @receiver clients +// @param c +func (clients) Download(c *gin.Context) { + var id = c.Param("id") + if id == "" || id == "undefined" { + utils.GinResponse(c).FailedWithMsg("参数错误") + return + } + + data, err := repository.Client().GetById(id) + if err != nil { + utils.GinResponse(c).FailedWithMsg("获取失败") + return + } + + var keys template_data.Keys + _ = json.Unmarshal([]byte(data.Keys), &keys) + + setting, err := repository.System().GetServerSetting() + if err != nil { + utils.GinResponse(c).FailedWithMsg("获取设置失败") + return + } + + // 处理一下数据 + execData := template_data.ClientConfig{ + PrivateKey: keys.PrivateKey, + IpAllocation: data.IpAllocation, + MTU: setting.MTU, + DNS: strings.Join(setting.DnsServer, ","), + PublicKey: keys.PublicKey, + PresharedKey: keys.PresharedKey, + AllowedIPS: data.AllowedIps, + Endpoint: setting.EndpointAddress, + ListenPort: data.Server.ListenPort, + PersistentKeepalive: setting.PersistentKeepalive, + } + + // 不同环境下处理文件路径 + var outPath = "/tmp/" + fmt.Sprintf("%s.conf", data.Name) + var templatePath = "./template/wg.client.conf" + if os.Getenv("GIN_MODE") != "release" { + outPath = "E:\\Workspace\\Go\\wireguard-dashboard\\template\\" + fmt.Sprintf("%s.conf", data.Name) + templatePath = "E:\\Workspace\\Go\\wireguard-dashboard\\template\\wg.client.conf" + } + + // 渲染数据 + parseTemplate, err := utils.Template().Parse(templatePath) + if err != nil { + utils.GinResponse(c).FailedWithMsg("读取模板文件失败") + return + } + + err = utils.Template().Execute(parseTemplate, execData, outPath) + if err != nil { + utils.GinResponse(c).FailedWithMsg("文件渲染失败") + return + } + + // 输出文件流 + c.Header("Content-Type", "application/octet-stream") + c.Header("Content-Disposition", "attachment; filename="+outPath) + c.Header("Content-Transfer-Encoding", "binary") + c.Header("Connection", "keep-alive") + c.File(outPath) + if err = os.Remove(outPath); err != nil { + log.Errorf("删除临时文件失败: %s", err.Error()) + } +} + +// GenerateQrCode +// @description: 生成客户端信息二维码 +// @receiver clients +// @param c +func (clients) GenerateQrCode(c *gin.Context) { + var id = c.Param("id") + if id == "" || id == "undefined" { + utils.GinResponse(c).FailedWithMsg("参数错误") + return + } + + data, err := repository.Client().GetById(id) + if err != nil { + utils.GinResponse(c).FailedWithMsg("获取失败") + return + } + + var keys template_data.Keys + _ = json.Unmarshal([]byte(data.Keys), &keys) + + setting, err := repository.System().GetServerSetting() + if err != nil { + utils.GinResponse(c).FailedWithMsg("获取设置失败") + return + } + + // 处理一下数据 + execData := template_data.ClientConfig{ + PrivateKey: keys.PrivateKey, + IpAllocation: data.IpAllocation, + MTU: setting.MTU, + PublicKey: keys.PublicKey, + PresharedKey: keys.PresharedKey, + AllowedIPS: data.AllowedIps, + Endpoint: setting.EndpointAddress, + ListenPort: data.Server.ListenPort, + PersistentKeepalive: setting.PersistentKeepalive, + } + + // 不同环境下处理文件路径 + var outPath = "/tmp/" + fmt.Sprintf("%s.conf", data.Name) + var templatePath = "./template/wg.client.conf" + if os.Getenv("GIN_MODE") != "release" { + outPath = "E:\\Workspace\\Go\\wireguard-dashboard\\template\\" + fmt.Sprintf("%s.conf", data.Name) + templatePath = "E:\\Workspace\\Go\\wireguard-dashboard\\template\\wg.client.conf" + } + + // 渲染数据 + parseTemplate, err := utils.Template().Parse(templatePath) + if err != nil { + utils.GinResponse(c).FailedWithMsg("读取模板文件失败") + return + } + + err = utils.Template().Execute(parseTemplate, execData, outPath) + if err != nil { + utils.GinResponse(c).FailedWithMsg("文件渲染失败") + return + } + + // 读取文件内容 + fileContent, err := os.ReadFile(outPath) + if err != nil { + utils.GinResponse(c).FailedWithMsg("读取文件失败") + return + } + + png, err := utils.QRCode().GenerateQrCodeBase64(fileContent, 256) + if err != nil { + utils.GinResponse(c).FailedWithErr("生成二维码失败", err) + return + } + + if err = os.Remove(outPath); err != nil { + log.Errorf("删除临时文件失败: %s", err.Error()) + } + + utils.GinResponse(c).OKWithData(map[string]interface{}{ + "qrCode": png, + }) +} diff --git a/http/api/server.go b/http/api/server.go index e2fa3c8..de070d4 100644 --- a/http/api/server.go +++ b/http/api/server.go @@ -4,6 +4,7 @@ import ( "gitee.ltd/lxh/logger/log" "github.com/gin-gonic/gin" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" + "strings" "wireguard-dashboard/http/param" "wireguard-dashboard/model/entity" "wireguard-dashboard/queues" @@ -43,7 +44,7 @@ func (server) SaveServer(c *gin.Context) { } publicKey := privateKey.PublicKey() serverInfo := &entity.Server{ - IpScope: p.IpScope, + IpScope: strings.Join(p.IpScope, ","), ListenPort: p.ListenPort, PrivateKey: privateKey.String(), PublicKey: publicKey.String(), @@ -82,17 +83,8 @@ func (server) GetServer(c *gin.Context) { log.Errorf("获取服务端信息失败: %v", err.Error()) } - utils.GinResponse(c).OKWithData(data) -} - -// GetGlobalSetting -// @description: 获取全局设置配置 -// @receiver server -// @param c -func (server) GetGlobalSetting(c *gin.Context) { - data, err := repository.System().GetServerSetting() - if err != nil { - log.Errorf("获取配置失败: %v", err.Error()) + if data.IpScopeStr != "" { + data.IpScope = strings.Split(data.IpScopeStr, ",") } utils.GinResponse(c).OKWithData(data) diff --git a/http/api/setting.go b/http/api/setting.go new file mode 100644 index 0000000..2aaa333 --- /dev/null +++ b/http/api/setting.go @@ -0,0 +1,51 @@ +package api + +import ( + "gitee.ltd/lxh/logger/log" + "github.com/gin-gonic/gin" + "wireguard-dashboard/http/param" + "wireguard-dashboard/repository" + "wireguard-dashboard/utils" +) + +type setting struct{} + +func Setting() setting { + return setting{} +} + +// GetGlobalSetting +// @description: 获取全局设置配置 +// @receiver setting +// @param c +func (setting) GetGlobalSetting(c *gin.Context) { + data, err := repository.System().GetServerSetting() + if err != nil { + log.Errorf("获取配置失败: %v", err.Error()) + } + + utils.GinResponse(c).OKWithData(data) +} + +// SetGlobalServerSetting +// @description: 设置全局服务配置 +// @receiver setting +// @param c +func (setting) SetGlobalServerSetting(c *gin.Context) { + var p param.SetSetting + if err := c.ShouldBind(&p); err != nil { + utils.GinResponse(c).FailedWithErr("参数错误", err) + return + } + +} + +// GetPublicNetworkIP +// @description: 获取当前机器的公网IP +// @receiver setting +// @param c +func (setting) GetPublicNetworkIP(c *gin.Context) { + utils.GinResponse(c).OKWithData(map[string]string{ + "IP": utils.Network().GetHostPublicIP(), + }) +} diff --git a/http/param/client.go b/http/param/client.go index 05cf7ae..ca60c3a 100644 --- a/http/param/client.go +++ b/http/param/client.go @@ -16,9 +16,9 @@ type SaveClient struct { Name string `json:"name" form:"name" binding:"required"` Email string `json:"email" form:"email" binding:"omitempty"` SubnetRange string `json:"subnetRange" form:"subnetRange" binding:"omitempty"` - IpAllocation string `json:"ipAllocation" form:"ipAllocation" binding:"required"` - AllowedIPS string `json:"allowedIPS" form:"allowedIPS" binding:"required"` - ExtraAllowedIPS string `json:"extraAllowedIPS" form:"extraAllowedIPS" binding:"omitempty"` + IpAllocation []string `json:"ipAllocation" form:"ipAllocation" binding:"required"` + AllowedIPS []string `json:"allowedIPS" form:"allowedIPS" binding:"required"` + ExtraAllowedIPS []string `json:"extraAllowedIPS" form:"extraAllowedIPS" binding:"omitempty"` Endpoint string `json:"endpoint" form:"endpoint" binding:"omitempty"` UseServerDNS int `json:"useServerDNS" form:"useServerDNS" binding:"required,oneof=1 0"` EnabledAfterCreation int `json:"enabledAfterCreation" form:"enabledAfterCreation" binding:"required,oneof=1 0"` diff --git a/http/param/server.go b/http/param/server.go index 0414ee0..db47877 100644 --- a/http/param/server.go +++ b/http/param/server.go @@ -1,10 +1,10 @@ package param type SaveServer struct { - Id string `json:"id" form:"id" binding:"omitempty"` // id - IpScope string `json:"ipScope" form:"ipScope" binding:"required"` // 内网ip范围段 - ListenPort int `json:"listenPort" form:"listenPort" binding:"required"` // 监听端口 - PostUpScript string `json:"postUpScript" form:"postUpScript" binding:"omitempty"` - PreDownScript string `json:"preDownScript" form:"preDownScript" binding:"omitempty"` - PostDownScript string `json:"postDownScript" form:"postDownScript" binding:"omitempty"` + Id string `json:"id" form:"id" binding:"omitempty"` // id + IpScope []string `json:"ipScope" form:"ipScope" binding:"required"` // 内网ip范围段 + ListenPort int `json:"listenPort" form:"listenPort" binding:"required"` // 监听端口 + PostUpScript string `json:"postUpScript" form:"postUpScript" binding:"omitempty"` + PreDownScript string `json:"preDownScript" form:"preDownScript" binding:"omitempty"` + PostDownScript string `json:"postDownScript" form:"postDownScript" binding:"omitempty"` } diff --git a/http/param/setting.go b/http/param/setting.go new file mode 100644 index 0000000..d273966 --- /dev/null +++ b/http/param/setting.go @@ -0,0 +1,8 @@ +package param + +// SetSetting +// @description: 设置 +type SetSetting struct { + Code string `json:"code" form:"code" binding:"required"` + Data string `json:"data" form:"data" binding:"required"` +} diff --git a/main.go b/main.go index d6726c8..771139a 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,7 @@ func main() { route.UserApi, route.ServerApi, route.ClientApi, + route.SettingApi, ) handler := route.InitRouter() diff --git a/model/entity/wireguard.go b/model/entity/wireguard.go index a3b9992..600975f 100644 --- a/model/entity/wireguard.go +++ b/model/entity/wireguard.go @@ -4,7 +4,7 @@ package entity // @description: 服务端 type Server struct { Base - IpScope string `json:"ipScope" gorm:"type:varchar(30);not null;comment:'ip范围'"` + IpScope string `json:"ipScope" gorm:"type:varchar(255);not null;comment:'ip范围'"` ListenPort int `json:"listenPort" gorm:"type:int(10);not null;comment:'服务监听端口'"` PrivateKey string `json:"privateKey" gorm:"type:text;not null;comment:'密钥'"` PublicKey string `json:"publicKey" gorm:"type:text;not null;comment:'公钥'"` @@ -22,20 +22,21 @@ func (*Server) TableName() string { // @description: 客户端 type Client struct { Base - ServerId string `json:"serverId" gorm:"type:varchar(36);not null;comment:'服务端id'"` - Name string `json:"name" gorm:"type:varchar(100);not null;comment:'客户端名称'"` - Email string `json:"email" gorm:"type:varchar(100);default null;comment:'联系邮箱'"` - SubnetRange string `json:"subnetRange" gorm:"type:varchar(255);default null;comment:'子网范围'"` - IpAllocation string `json:"ipAllocation" gorm:"type:varchar(30);not null;comment:'客户端ip'"` - AllowedIps string `json:"allowedIps" gorm:"type:varchar(30);not null;comment:'允许访问的ip'"` - ExtraAllowedIps string `json:"extraAllowedIps" gorm:"type:varchar(30);default null;comment:'额外允许的ip范围'"` - Endpoint string `json:"endpoint" gorm:"type:varchar(255);default null;comment:'端点'"` - UseServerDns int `json:"useServerDns" gorm:"type:int(1);default 1;comment:'是否使用服务端dns'"` - EnableAfterCreation int `json:"enableAfterCreation" gorm:"type:int(1);default 1;comment:'是否创建后启用'"` - Keys string `json:"keys" gorm:"type:text;default null;comment:'公钥和密钥的json串'"` - UserId string `json:"userId" gorm:"type:char(36);not null;comment:'创建人id'"` - Enabled bool `json:"enabled" gorm:"type:tinyint(1);default 1;comment:'状态(0 - 禁用 | 1 - 正常)'"` - User *User `json:"user" gorm:"foreignKey:UserId"` + ServerId string `json:"serverId" gorm:"type:varchar(36);not null;comment:'服务端id'"` + Name string `json:"name" gorm:"type:varchar(100);not null;comment:'客户端名称'"` + Email string `json:"email" gorm:"type:varchar(100);default null;comment:'联系邮箱'"` + SubnetRange string `json:"subnetRange" gorm:"type:varchar(255);default null;comment:'子网范围'"` + IpAllocation string `json:"ipAllocation" gorm:"type:varchar(255);not null;comment:'客户端ip'"` + AllowedIps string `json:"allowedIps" gorm:"type:varchar(255);not null;comment:'允许访问的ip'"` + ExtraAllowedIps string `json:"extraAllowedIps" gorm:"type:varchar(255);default null;comment:'额外允许的ip范围'"` + Endpoint string `json:"endpoint" gorm:"type:varchar(255);default null;comment:'端点'"` + UseServerDns int `json:"useServerDns" gorm:"type:int(1);default 1;comment:'是否使用服务端dns'"` + EnableAfterCreation int `json:"enableAfterCreation" gorm:"type:int(1);default 1;comment:'是否创建后启用'"` + Keys string `json:"keys" gorm:"type:text;default null;comment:'公钥和密钥的json串'"` + UserId string `json:"userId" gorm:"type:char(36);not null;comment:'创建人id'"` + Enabled bool `json:"enabled" gorm:"type:tinyint(1);default 1;comment:'状态(0 - 禁用 | 1 - 正常)'"` + User *User `json:"user" gorm:"foreignKey:UserId"` + Server *Server `json:"server" gorm:"foreignKey:ServerId"` } func (*Client) TableName() string { diff --git a/model/template_data/wireguard.go b/model/template_data/wireguard.go index 2080c04..a5b1005 100644 --- a/model/template_data/wireguard.go +++ b/model/template_data/wireguard.go @@ -32,3 +32,16 @@ type Keys struct { PublicKey string `json:"publicKey"` PresharedKey string `json:"presharedKey"` } + +type ClientConfig struct { + PrivateKey string `json:"privateKey"` + IpAllocation string `json:"ipAllocation"` + MTU int `json:"MTU"` + DNS string `json:"DNS"` + PublicKey string `json:"publicKey"` + PresharedKey string `json:"presharedKey"` + AllowedIPS string `json:"allowedIPS"` + Endpoint string `json:"endpoint"` + ListenPort int `json:"listenPort"` + PersistentKeepalive int `json:"persistentKeepalive"` +} diff --git a/model/vo/client.go b/model/vo/client.go index 5c497da..347261c 100644 --- a/model/vo/client.go +++ b/model/vo/client.go @@ -10,9 +10,12 @@ 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"` + IpAllocation []string `json:"ipAllocation" gorm:"-"` + IpAllocationStr string `json:"-" gorm:"ipAllocationStr"` + AllowedIps []string `json:"allowedIPS" gorm:"-"` + AllowedIpsStr string `json:"-" gorm:"allowedIPSStr"` + ExtraAllowedIps []string `json:"extraAllowedIPS"` + ExtraAllowedIpsStr string `json:"-" gorm:"extraAllowedIPSStr"` Endpoint string `json:"endpoint"` UseServerDNS int `json:"useServerDNS"` EnableAfterCreation int `json:"enableAfterCreation"` diff --git a/model/vo/system.go b/model/vo/system.go index b8ccabc..ce8d2ab 100644 --- a/model/vo/system.go +++ b/model/vo/system.go @@ -1,11 +1,11 @@ package vo type ServerSetting struct { - EndpointAddress string `json:"endpointAddress"` - DnsServer string `json:"dnsServer"` - MTU int `json:"MTU"` - PersistentKeepalive int `json:"persistentKeepalive"` - FirewallMark string `json:"firewallMark"` - Table string `json:"table"` - ConfigFilePath string `json:"configFilePath"` + EndpointAddress string `json:"endpointAddress"` + DnsServer []string `json:"dnsServer"` + MTU int `json:"MTU"` + PersistentKeepalive int `json:"persistentKeepalive"` + FirewallMark string `json:"firewallMark"` + Table string `json:"table"` + ConfigFilePath string `json:"configFilePath"` } diff --git a/model/vo/wireguard.go b/model/vo/wireguard.go index 80227d7..7e3dc4c 100644 --- a/model/vo/wireguard.go +++ b/model/vo/wireguard.go @@ -3,12 +3,13 @@ package vo // Server // @description: 服务端返回信息 type Server struct { - Id string `json:"id"` // id - IpScope string `json:"ipScope"` // ip范围 - ListenPort int `json:"listenPort"` // 服务监听端口 - PrivateKey string `json:"privateKey"` // 私钥 - PublicKey string `json:"publicKey"` // 公钥 - PostUpScript string `json:"postUpScript"` - PreDownScript string `json:"preDownScript"` - PostDownScript string `json:"postDownScript"` + Id string `json:"id"` // id + IpScope []string `json:"ipScope" gorm:"-"` // ip范围 + IpScopeStr string `json:"-" gorm:"ipScope"` + ListenPort int `json:"listenPort"` // 服务监听端口 + PrivateKey string `json:"privateKey"` // 私钥 + PublicKey string `json:"publicKey"` // 公钥 + PostUpScript string `json:"postUpScript"` + PreDownScript string `json:"preDownScript"` + PostDownScript string `json:"postDownScript"` } diff --git a/repository/client.go b/repository/client.go index c5f1c83..2382730 100644 --- a/repository/client.go +++ b/repository/client.go @@ -2,9 +2,12 @@ package repository import ( "encoding/json" + "errors" + "gitee.ltd/lxh/logger/log" "github.com/spf13/cast" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "gorm.io/gorm" + "strings" "wireguard-dashboard/client" "wireguard-dashboard/http/param" "wireguard-dashboard/model/entity" @@ -32,9 +35,9 @@ func Client() clientRepo { // @return err func (r clientRepo) List(p param.ClientList) (data []vo.Client, total int64, err error) { err = r.Table("t_wg_client as twc").Scopes(utils.Page(p.Current, p.Size)).Joins("LEFT JOIN t_user as tu ON twc.user_id = tu.id"). - Select("twc.id", "twc.created_at", "twc.updated_at", "twc.name", "twc.email", "twc.subnet_range", "twc.ip_allocation", "twc.allowed_ips", - "twc.extra_allowed_ips", "twc.endpoint", "twc.use_server_dns", "twc.enable_after_creation", "twc.enabled", "twc.keys as keys_str", "tu.name as create_user"). - Find(&data).Offset(-1).Limit(-1).Count(&total).Error + Select("twc.id", "twc.created_at", "twc.updated_at", "twc.name", "twc.email", "twc.subnet_range", "twc.ip_allocation as ip_allocation_str", "twc.allowed_ips as allowed_ips_str", + "twc.extra_allowed_ips as extra_allowed_ips_str", "twc.endpoint", "twc.use_server_dns", "twc.enable_after_creation", "twc.enabled", "twc.keys as keys_str", "tu.name as create_user"). + Order("twc.created_at DESC").Find(&data).Offset(-1).Limit(-1).Count(&total).Error if err != nil { return @@ -44,6 +47,15 @@ func (r clientRepo) List(p param.ClientList) (data []vo.Client, total int64, err if v.KeysStr != "" { _ = json.Unmarshal([]byte(v.KeysStr), &data[i].Keys) } + if v.IpAllocationStr != "" { + data[i].IpAllocation = strings.Split(v.IpAllocationStr, ",") + } + if v.AllowedIpsStr != "" { + data[i].AllowedIps = strings.Split(v.AllowedIpsStr, ",") + } + if v.ExtraAllowedIpsStr != "" { + data[i].ExtraAllowedIps = strings.Split(v.ExtraAllowedIpsStr, ",") + } } return @@ -65,9 +77,9 @@ func (r clientRepo) Save(p param.SaveClient, adminId string) (client *entity.Cli Name: p.Name, Email: p.Email, SubnetRange: p.SubnetRange, - IpAllocation: p.IpAllocation, - AllowedIps: p.AllowedIPS, - ExtraAllowedIps: p.ExtraAllowedIPS, + IpAllocation: strings.Join(p.IpAllocation, ","), + AllowedIps: strings.Join(p.AllowedIPS, ","), + ExtraAllowedIps: strings.Join(p.ExtraAllowedIPS, ","), Endpoint: p.Endpoint, UseServerDns: p.UseServerDNS, EnableAfterCreation: p.EnabledAfterCreation, @@ -85,6 +97,17 @@ func (r clientRepo) Save(p param.SaveClient, adminId string) (client *entity.Cli return } + // 查询新增的ip地址是否已经存在了 + var count int64 + if err = r.Model(&entity.Client{}).Where("ip_allocation in (?)", p.IpAllocation).Count(&count).Error; err != nil { + log.Errorf("查询IP地址是否存在失败: %v", err.Error()) + return + } + + if count > 0 { + return nil, errors.New("该客户端的IP已经存在,请检查后再添加!") + } + // 为空,新增 privateKey, err := wgtypes.GeneratePrivateKey() if err != nil { @@ -107,9 +130,9 @@ func (r clientRepo) Save(p param.SaveClient, adminId string) (client *entity.Cli Name: p.Name, Email: p.Email, SubnetRange: p.SubnetRange, - IpAllocation: p.IpAllocation, - AllowedIps: p.AllowedIPS, - ExtraAllowedIps: p.ExtraAllowedIPS, + IpAllocation: strings.Join(p.IpAllocation, ","), + AllowedIps: strings.Join(p.AllowedIPS, ","), + ExtraAllowedIps: strings.Join(p.ExtraAllowedIPS, ","), Endpoint: p.Endpoint, UseServerDns: p.UseServerDNS, EnableAfterCreation: p.EnabledAfterCreation, @@ -121,3 +144,26 @@ func (r clientRepo) Save(p param.SaveClient, adminId string) (client *entity.Cli err = r.Model(&entity.Client{}).Create(ent).Error return } + +// Delete +// @description: 删除客户端 +// @receiver r +// @param id +// @return err +func (r clientRepo) Delete(id string) (err error) { + return r.Model(&entity.Client{}).Where("id = ?", id).Delete(&entity.Client{}).Error +} + +// GetById +// @description: 根据id获取客户端详情 +// @receiver r +// @param id +// @return data +// @return err +func (r clientRepo) GetById(id string) (data entity.Client, err error) { + err = r.Model(&entity.Client{}).Where("id = ?", id).Preload("Server").First(&data).Error + if err != nil { + return + } + return +} diff --git a/repository/server.go b/repository/server.go index 5fcc00d..187d85a 100644 --- a/repository/server.go +++ b/repository/server.go @@ -24,7 +24,7 @@ func Server() server { // @return data // @return err func (r server) GetServer() (data *vo.Server, err error) { - err = r.Model(&entity.Server{}).First(&data).Error + err = r.Model(&entity.Server{}).Select("id", "ip_scope as ip_scope_str", "listen_port", "private_key", "public_key", "post_up_script", "pre_down_script", "post_down_script").First(&data).Error return } diff --git a/route/client.go b/route/client.go index e402da0..6693476 100644 --- a/route/client.go +++ b/route/client.go @@ -9,7 +9,10 @@ import ( func ClientApi(r *gin.RouterGroup) { apiGroup := r.Group("client", middleware.Authorization()) { - apiGroup.GET("list", api.Client().List) // 客户端列表 - apiGroup.POST("save", api.Client().Save) // 新增/编辑客户端 + apiGroup.GET("list", api.Client().List) // 客户端列表 + apiGroup.POST("save", api.Client().Save) // 新增/编辑客户端 + apiGroup.DELETE(":id", api.Client().Delete) // 删除客户端 + apiGroup.POST("download/:id", api.Client().Download) // 下载客户端配置文件 + apiGroup.POST("generate-qrcode/:id", api.Client().GenerateQrCode) // 生成客户端二维码 } } diff --git a/route/server.go b/route/server.go index 1e57a1d..bf1fea4 100644 --- a/route/server.go +++ b/route/server.go @@ -9,8 +9,7 @@ import ( func ServerApi(r *gin.RouterGroup) { apiGroup := r.Group("server", middleware.Authorization()) { - apiGroup.GET("", api.Server().GetServer) // 获取服务端信息 - apiGroup.GET("global-setting", api.Server().GetGlobalSetting) // 获取服务端全局配置 - apiGroup.POST("", api.Server().SaveServer) // 新增/更新服务端信息 + apiGroup.GET("", api.Server().GetServer) // 获取服务端信息 + apiGroup.POST("", api.Server().SaveServer) // 新增/更新服务端信息 } } diff --git a/route/setting.go b/route/setting.go new file mode 100644 index 0000000..ea1c973 --- /dev/null +++ b/route/setting.go @@ -0,0 +1,18 @@ +package route + +import ( + "github.com/gin-gonic/gin" + "wireguard-dashboard/http/api" + "wireguard-dashboard/middleware" +) + +// SettingApi +// @description: 设置相关API +// @param r +func SettingApi(r *gin.RouterGroup) { + apiGroup := r.Group("setting", middleware.Authorization()) + { + apiGroup.GET("server", api.Setting().GetGlobalSetting) // 获取全局服务端配置 + apiGroup.GET("public-ip", api.Setting().GetPublicNetworkIP) // 获取公网IP + } +} diff --git a/script/script.go b/script/script.go index 0e6840f..672ca55 100644 --- a/script/script.go +++ b/script/script.go @@ -106,7 +106,7 @@ func (s Script) InitServer() error { // 初始化服务端的全局配置 var data = map[string]any{ "endpointAddress": utils.Network().GetHostPublicIP(), - "dnsServer": "10.10.10.1/24", + "dnsServer": []string{"10.10.10.1/24"}, "MTU": 1450, "persistentKeepalive": 15, "firewallMark": "", diff --git a/template/wg.client.conf b/template/wg.client.conf index 298518f..05950e8 100644 --- a/template/wg.client.conf +++ b/template/wg.client.conf @@ -1,6 +1,7 @@ [Interface] PrivateKey = {{ .PrivateKey|html }} Address = {{ .IpAllocation|html }} +{{ if .DNS }}DNS = {{ .DNS|html }} {{ end }} MTU = {{ .MTU }} [Peer] diff --git a/utils/qr_code.go b/utils/qr_code.go new file mode 100644 index 0000000..5521854 --- /dev/null +++ b/utils/qr_code.go @@ -0,0 +1,38 @@ +package utils + +import ( + "encoding/base64" + "gitee.ltd/lxh/logger/log" + "github.com/skip2/go-qrcode" +) + +type qrCode struct{} + +func QRCode() qrCode { + return qrCode{} +} + +// GenerateQrCodeBase64 +// @description: 生成二维码 +// @receiver qr +// @param content +// @param size +// @return imgStr +// @return err +func (qr qrCode) GenerateQrCodeBase64(content []byte, size int) (imgStr string, err error) { + q, err := qrcode.New(string(content), qrcode.Highest) + if err != nil { + log.Errorf("初始化二维码对象失败: %v", err.Error()) + return + } + q.DisableBorder = false + + png, err := q.PNG(size) + if err != nil { + log.Errorf("生成二维码失败: %v", err.Error()) + return "", err + } + + imgStr = "data:image/png;base64," + base64.StdEncoding.EncodeToString(png) + return +} diff --git a/utils/template.go b/utils/template.go index 27d9031..e40b746 100644 --- a/utils/template.go +++ b/utils/template.go @@ -24,7 +24,7 @@ func (templateUtils) Parse(filepath string) (parseTemplate *template.Template, e return } - parseTemplate, err = template.New("wg0.conf").Parse(string(file)) + parseTemplate, err = template.New("wg.conf").Parse(string(file)) return }