diff --git a/config/config.go b/config/config.go index 4de3af1..b7f7409 100644 --- a/config/config.go +++ b/config/config.go @@ -7,6 +7,6 @@ type config struct { Database *database `yaml:"database"` Redis *redis `yaml:"redis"` File *file `yaml:"file"` - Mail *mail `yaml:"mail"` + Mail *mail `yaml:"email"` Wireguard *wireguard `yaml:"wireguard"` } diff --git a/go.mod b/go.mod index 8506c8d..4a3313a 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,8 @@ require ( github.com/go-resty/resty/v2 v2.11.0 github.com/golang-jwt/jwt/v5 v5.2.1 github.com/google/uuid v1.6.0 + github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible + github.com/json-iterator/go v1.1.12 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 @@ -18,7 +20,6 @@ require ( golang.org/x/crypto v0.21.0 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 gopkg.in/fsnotify.v1 v1.4.7 - gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/yaml.v3 v3.0.1 gorm.io/driver/mysql v1.5.4 gorm.io/driver/postgres v1.5.6 @@ -58,7 +59,6 @@ require ( 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/leodido/go-urn v1.2.4 // indirect github.com/lixh00/loki-client-go v1.0.1 // indirect @@ -99,7 +99,6 @@ require ( google.golang.org/genproto v0.0.0-20221018160656-63c7b68cfc55 // indirect google.golang.org/grpc v1.50.1 // indirect google.golang.org/protobuf v1.33.0 // indirect - gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/yaml.v2 v2.4.0 // indirect modernc.org/libc v1.22.5 // indirect modernc.org/mathutil v1.5.0 // indirect diff --git a/go.sum b/go.sum index cf34265..c3492b0 100644 --- a/go.sum +++ b/go.sum @@ -498,6 +498,8 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA= +github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= @@ -1273,8 +1275,6 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ 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/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= -gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= 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= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1286,8 +1286,6 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify/fsnotify.v1 v1.4.7/go.mod h1:Fyux9zXlo4rWoMSIzpn9fDAYjalPqJ/K1qJ27s+7ltE= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= -gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= diff --git a/http/api/client.go b/http/api/client.go index 7de1f7f..f83aa87 100644 --- a/http/api/client.go +++ b/http/api/client.go @@ -202,49 +202,15 @@ func (clients) Download(c *gin.Context) { var keys template_data.Keys _ = json.Unmarshal([]byte(data.Keys), &keys) - setting, err := repository.System().GetServerSetting() + serverSetting, err := repository.System().GetServerSetting() if err != nil { utils.GinResponse(c).FailedWithMsg("获取设置失败") return } - var serverDNS []string - if *data.UseServerDns == 1 { - serverDNS = setting.DnsServer - } - - // 处理一下数据 - execData := template_data.ClientConfig{ - PrivateKey: keys.PrivateKey, - IpAllocation: data.IpAllocation, - MTU: setting.MTU, - DNS: strings.Join(serverDNS, ","), - PublicKey: data.Server.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) + outPath, err := utils.Wireguard().GenerateClientFile(&data, serverSetting) if err != nil { - utils.GinResponse(c).FailedWithMsg("读取模板文件失败") - return - } - - err = utils.Template().Execute(parseTemplate, execData, outPath) - if err != nil { - utils.GinResponse(c).FailedWithMsg("文件渲染失败") + utils.GinResponse(c).FailedWithErr("生成失败", err) return } @@ -279,49 +245,15 @@ func (clients) GenerateQrCode(c *gin.Context) { var keys template_data.Keys _ = json.Unmarshal([]byte(data.Keys), &keys) - setting, err := repository.System().GetServerSetting() + serverSetting, err := repository.System().GetServerSetting() if err != nil { utils.GinResponse(c).FailedWithMsg("获取设置失败") return } - var serverDNS []string - if *data.UseServerDns == 1 { - serverDNS = setting.DnsServer - } - - // 处理一下数据 - execData := template_data.ClientConfig{ - PrivateKey: keys.PrivateKey, - IpAllocation: data.IpAllocation, - MTU: setting.MTU, - DNS: strings.Join(serverDNS, ","), - PublicKey: data.Server.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) + outPath, err := utils.Wireguard().GenerateClientFile(&data, serverSetting) if err != nil { - utils.GinResponse(c).FailedWithMsg("读取模板文件失败") - return - } - - err = utils.Template().Execute(parseTemplate, execData, outPath) - if err != nil { - utils.GinResponse(c).FailedWithMsg("文件渲染失败") + utils.GinResponse(c).FailedWithErr("生成失败", err) return } @@ -347,6 +279,59 @@ func (clients) GenerateQrCode(c *gin.Context) { }) } +// SendEmail +// @description: 发送邮件 +// @receiver clients +// @param c +func (clients) SendEmail(c *gin.Context) { + var id = c.Param("id") + if id == "" || id == "undefined" { + utils.GinResponse(c).FailedWithMsg("id不能为空") + return + } + + // 先校验一下邮箱发送是否可用 + if err := utils.Mail().VerifyConfig(); err != nil { + utils.GinResponse(c).FailedWithMsg(err.Error()) + return + } + + // 获取该客户端信息 + clientInfo, err := repository.Client().GetById(id) + if err != nil { + utils.GinResponse(c).FailedWithErr("获取失败", err) + return + } + + if clientInfo.Email == "" { + utils.GinResponse(c).FailedWithMsg("当前客户端未配置联系邮箱!") + return + } + + serverSetting, err := repository.System().GetServerSetting() + if err != nil { + utils.GinResponse(c).FailedWithMsg("获取设置失败") + return + } + + outPath, err := utils.Wireguard().GenerateClientFile(&clientInfo, serverSetting) + if err != nil { + utils.GinResponse(c).FailedWithErr("生成失败", err) + return + } + + err = utils.Mail().SendMail(clientInfo.Email, fmt.Sprintf("客户端: %s", clientInfo.Name), "请查收附件", outPath) + if err != nil { + utils.GinResponse(c).FailedWithErr("发送邮件失败", err) + return + } + + if err = os.Remove(outPath); err != nil { + log.Errorf("删除临时文件失败: %s", err.Error()) + } + utils.GinResponse(c).OK() +} + // Status // @description: 获取客户端状态信息,链接状态等 // @receiver clients diff --git a/http/api/user.go b/http/api/user.go index 3457d58..3f24635 100644 --- a/http/api/user.go +++ b/http/api/user.go @@ -1,8 +1,11 @@ package api import ( + "encoding/base64" + "fmt" "gitee.ltd/lxh/logger/log" "github.com/gin-gonic/gin" + "strings" "wireguard-dashboard/client" "wireguard-dashboard/component" "wireguard-dashboard/constant" @@ -157,6 +160,30 @@ func (user) Save(c *gin.Context) { } } + // 只有修改才有头像值 + if p.Avatar != "" && p.ID != "" { + // 判断头像是base64开头的就需要重新上传更新 + if strings.HasPrefix(p.Avatar, "data:image/png;base64,") { + avatar := strings.Replace(p.Avatar, "data:image/png;base64,", "", -1) + avatarByte, err := base64.StdEncoding.DecodeString(avatar) + if err != nil { + log.Errorf("反解析头像失败: %v", err.Error()) + utils.GinResponse(c).FailedWithMsg("上传头像失败") + return + } + + file, err := utils.FileSystem().UploadFile(avatarByte, ".png") + if err != nil { + log.Errorf("上传头像失败: %v", err.Error()) + utils.GinResponse(c).FailedWithMsg("上传头像失败") + return + } + + p.Avatar = file + } + + } + if err := repository.User().Save(&entity.User{ Base: entity.Base{ Id: p.ID, @@ -250,3 +277,17 @@ func (user) DeleteUser(c *gin.Context) { utils.GinResponse(c).OK() } + +// ChangeAvatar +// @description: 切换头像 +// @receiver user +// @param c +func (user) ChangeAvatar(c *gin.Context) { + avatar, err := utils.Avatar().GenerateAvatar(false) + if err != nil { + utils.GinResponse(c).FailedWithErr("生成头像失败", err) + return + } + + utils.GinResponse(c).OKWithData(fmt.Sprintf("data:image/png;base64,%s", base64.StdEncoding.EncodeToString([]byte(avatar)))) +} diff --git a/repository/user.go b/repository/user.go index 7010718..ebde2a8 100644 --- a/repository/user.go +++ b/repository/user.go @@ -86,7 +86,7 @@ func (r user) Save(ent *entity.User) (err error) { // 没有头像就生成一个头像 if ent.Avatar == "" { - ent.Avatar, _ = utils.Avatar().GenerateAvatar() + ent.Avatar, _ = utils.Avatar().GenerateAvatar(true) } // 创建 diff --git a/route/client.go b/route/client.go index 9254cb1..9c386b9 100644 --- a/route/client.go +++ b/route/client.go @@ -14,6 +14,7 @@ func ClientApi(r *gin.RouterGroup) { apiGroup.DELETE(":id", middleware.Permission(), api.Client().Delete) // 删除客户端 apiGroup.POST("download/:id", api.Client().Download) // 下载客户端配置文件 apiGroup.POST("generate-qrcode/:id", api.Client().GenerateQrCode) // 生成客户端二维码 + apiGroup.POST("to-email/:id", api.Client().SendEmail) //发送邮件 apiGroup.GET("status", api.Client().Status) // 获取客户端链接状态监听列表 apiGroup.POST("offline/:id", api.Client().Offline) // 强制下线指定客户端 apiGroup.POST("assignIP", api.Client().AssignIPAndAllowedIP) // 分配IP diff --git a/route/user.go b/route/user.go index eea5b82..7b1b332 100644 --- a/route/user.go +++ b/route/user.go @@ -23,5 +23,6 @@ func UserApi(r *gin.RouterGroup) { userApi.GET("list", middleware.Permission(), api.UserApi().List) // 用户列表 userApi.PUT("change-status", middleware.Permission(), api.UserApi().ChangeUserState) // 变更状态 userApi.DELETE("delete/:id", middleware.Permission(), api.UserApi().DeleteUser) // 删除用户 + userApi.POST("change-avatar", api.UserApi().ChangeAvatar) // 更换头像 } } diff --git a/script/script.go b/script/script.go index 24664e0..e728b00 100644 --- a/script/script.go +++ b/script/script.go @@ -69,7 +69,7 @@ func (s Script) CreateSuperAdmin() error { } // 生成一下头像 - avatarPath, err := utils.Avatar().GenerateAvatar() + avatarPath, err := utils.Avatar().GenerateAvatar(true) if err != nil { log.Errorf("生成头像失败: %v", err.Error()) return err diff --git a/utils/avatar.go b/utils/avatar.go index ea580bb..bfc0b9c 100644 --- a/utils/avatar.go +++ b/utils/avatar.go @@ -19,7 +19,7 @@ func Avatar() avatar { // @receiver avatar // @return path // @return err -func (avatar) GenerateAvatar() (path string, err error) { +func (avatar) GenerateAvatar(isUpload bool) (path string, err error) { rand.New(rand.NewSource(time.Now().UnixNano())) r := client.HttpClient.R() result, err := r.Get(fmt.Sprintf("https://api.dicebear.com/7.x/croodles/png?seed=%d&scale=120&size=200&clip=true&randomizeIds=true&beard=variant01,variant02,variant03&"+ @@ -30,10 +30,13 @@ func (avatar) GenerateAvatar() (path string, err error) { return "", err } - filePath, err := FileSystem().UploadFile(result.Body(), ".png") - if err != nil { - return "", err + if isUpload { + path, err = FileSystem().UploadFile(result.Body(), ".png") + if err != nil { + return "", err + } + return } - return filePath, nil + return string(result.Body()), nil } diff --git a/utils/mail.go b/utils/mail.go index 0268a77..14b7503 100644 --- a/utils/mail.go +++ b/utils/mail.go @@ -2,77 +2,74 @@ package utils import ( "crypto/tls" + "errors" "fmt" - "gitee.ltd/lxh/logger/log" - "gopkg.in/gomail.v2" + "github.com/jordan-wright/email" "mime" + "net/smtp" + "net/textproto" "path/filepath" "wireguard-dashboard/config" ) type mail struct { - md *gomail.Dialer + *email.Email + addr string + auth smtp.Auth } -func Mail() mail { - mailDialer := gomail.NewDialer(config.Config.Mail.Host, config.Config.Mail.Port, config.Config.Mail.User, config.Config.Mail.Password) - mailDialer.TLSConfig = &tls.Config{ - InsecureSkipVerify: config.Config.Mail.SkipTls, +func Mail() *mail { + var m mail + em := email.NewEmail() + m.Email = em + m.auth = smtp.PlainAuth("", config.Config.Mail.User, config.Config.Mail.Password, config.Config.Mail.Host) + m.addr = fmt.Sprintf("%s:%d", config.Config.Mail.Host, config.Config.Mail.Port) + return &m +} + +func (m *mail) VerifyConfig() (err error) { + if m == nil { + return errors.New("邮件客户端初始化失败") } - return mail{mailDialer} + + if m.auth == nil || m.addr == "" { + return errors.New("邮件客户端未完成初始化") + } + + return nil } // SendMail -// @description: 发送普通邮件 -// @receiver mail -// @param subject -// @param toAddress -// @param content -// @return err -func (m mail) SendMail(subject, toAddress, content string) (err error) { - msg := gomail.NewMessage() - msg.SetHeader("From", msg.FormatAddress(m.md.Username, "wireguard-dashboard")) - msg.SetHeader("To", toAddress) - msg.SetHeader("Subject", subject) - msg.SetBody("text/plain", content) - - if err = m.md.DialAndSend(msg); err != nil { - log.Errorf("发送普通邮件失败: %v", err.Error()) - return - } - - return -} - -// SendMailWithAttach -// @description: 发送并携带附件 +// @description: 发送附件 // @receiver m +// @param to // @param subject -// @param toAddress -// @param content -// @param attachPath +// @param attacheFilePath // @return err -func (m mail) SendMailWithAttach(subject, toAddress, content, attachPath string) (err error) { - msg := gomail.NewMessage() - msg.SetHeader("From", msg.FormatAddress(m.md.Username, "wireguard-dashboard")) - msg.SetHeader("To", toAddress) - msg.SetHeader("Subject", subject) - msg.SetBody("text/plain", content) - - rename := m.getFileName(attachPath) - - msg.Attach(attachPath, gomail.Rename(rename), gomail.SetHeader(map[string][]string{ - "Content-Disposition": { - fmt.Sprintf(`attachment; filename="%s"`, mime.BEncoding.Encode("UTF-8", rename)), - }, - })) - - if err = m.md.DialAndSend(msg); err != nil { - log.Errorf("发送普通邮件失败: %v", err.Error()) - return +func (m *mail) SendMail(to, subject, content, attacheFilePath string) (err error) { + m.From = fmt.Sprintf("wg-dashboard <%s>", config.Config.Mail.User) + m.To = []string{to} + m.Subject = subject + m.Text = []byte(content) + if attacheFilePath != "" { + atch, err := m.AttachFile(attacheFilePath) + if err != nil { + return fmt.Errorf("读取附件文件失败: %v", err.Error()) + } + emHeader := textproto.MIMEHeader{} + emHeader.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, mime.BEncoding.Encode("UTF-8", m.getFileName(attacheFilePath)))) + atch.Header = emHeader } - return + if config.Config.Mail.SkipTls { + return m.Send(m.addr, m.auth) + } + + tlsConfig := &tls.Config{} + tlsConfig.InsecureSkipVerify = config.Config.Mail.SkipTls + tlsConfig.ServerName = config.Config.Mail.Host + + return m.SendWithTLS(m.addr, m.auth, tlsConfig) } // getFileName @@ -80,6 +77,6 @@ func (m mail) SendMailWithAttach(subject, toAddress, content, attachPath string) // @receiver m // @param filePath // @return string -func (m mail) getFileName(filePath string) string { +func (m *mail) getFileName(filePath string) string { return filepath.Base(filePath) } diff --git a/utils/wireguard.go b/utils/wireguard.go index 885e26d..8f3e9d8 100644 --- a/utils/wireguard.go +++ b/utils/wireguard.go @@ -1,13 +1,19 @@ package utils import ( + "encoding/json" + "errors" "fmt" "github.com/spf13/cast" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "math/rand" + "os" "slices" "strings" "wireguard-dashboard/client" + "wireguard-dashboard/model/entity" + "wireguard-dashboard/model/template_data" + "wireguard-dashboard/model/vo" ) type wireguard struct{} @@ -68,6 +74,58 @@ func (w wireguard) GenerateClientIP(serverIP, rule string, assignedIPS ...string return fmt.Sprintf("%s.%s", prefix, suffix) } +// GenerateClientFile +// @description: 生成客户端临时配置文件 +// @receiver w +// @param clientInfo +// @param setting +// @return tmpFilePath +// @return err +func (w wireguard) GenerateClientFile(clientInfo *entity.Client, setting *vo.ServerSetting) (tmpFilePath string, err error) { + var keys template_data.Keys + _ = json.Unmarshal([]byte(clientInfo.Keys), &keys) + + var serverDNS []string + if *clientInfo.UseServerDns == 1 { + serverDNS = setting.DnsServer + } + + // 处理一下数据 + execData := template_data.ClientConfig{ + PrivateKey: keys.PrivateKey, + IpAllocation: clientInfo.IpAllocation, + MTU: setting.MTU, + DNS: strings.Join(serverDNS, ","), + PublicKey: clientInfo.Server.PublicKey, + PresharedKey: keys.PresharedKey, + AllowedIPS: clientInfo.AllowedIps, + Endpoint: setting.EndpointAddress, + ListenPort: clientInfo.Server.ListenPort, + PersistentKeepalive: setting.PersistentKeepalive, + } + + // 不同环境下处理文件路径 + var outPath = "/tmp/" + fmt.Sprintf("%s.conf", clientInfo.Name) + var templatePath = "./template/wg.client.conf" + if os.Getenv("GIN_MODE") != "release" { + outPath = "E:\\Workspace\\Go\\wireguard-dashboard\\template\\" + fmt.Sprintf("%s.conf", clientInfo.Name) + templatePath = "E:\\Workspace\\Go\\wireguard-dashboard\\template\\wg.client.conf" + } + + // 渲染数据 + parseTemplate, err := Template().Parse(templatePath) + if err != nil { + return "", errors.New("读取模板文件失败") + } + + err = Template().Execute(parseTemplate, execData, outPath) + if err != nil { + return "", errors.New("文件渲染失败") + } + + return outPath, nil +} + // random // @description: 随机模式 // @receiver w