diff --git a/component/jwt.go b/component/jwt.go index 6eb3309..a4eb0a5 100644 --- a/component/jwt.go +++ b/component/jwt.go @@ -29,14 +29,18 @@ func JWT() JwtClaims { // @receiver Jwt // @return token // @return err -func (j JwtClaims) GenerateToken(userId string) (token string, err error) { +func (j JwtClaims) GenerateToken(userId string) (token string, expireTime *jwt.NumericDate, err error) { + timeNow := time.Now().Local() + expireTime = jwt.NewNumericDate(timeNow.Add(7 * time.Hour)) + notBefore := jwt.NewNumericDate(timeNow) + issuedAt := jwt.NewNumericDate(timeNow) claims := JwtClaims{ ID: userId, RegisteredClaims: jwt.RegisteredClaims{ Subject: "wireguard-dashboard", - ExpiresAt: jwt.NewNumericDate(time.Now().Local().Add(7 * time.Hour)), - NotBefore: jwt.NewNumericDate(time.Now().Local()), - IssuedAt: jwt.NewNumericDate(time.Now().Local()), + ExpiresAt: expireTime, + NotBefore: notBefore, + IssuedAt: issuedAt, }, } @@ -44,7 +48,7 @@ func (j JwtClaims) GenerateToken(userId string) (token string, err error) { token, err = t.SignedString([]byte(Secret)) if err != nil { log.Errorf("生成token失败: %v", err.Error()) - return "", errors.New("生成token失败") + return "", nil, errors.New("生成token失败") } client.Redis.Set(context.Background(), fmt.Sprintf("%s:%s", constant.Token, userId), token, 7*time.Hour) diff --git a/http/api/client.go b/http/api/client.go index cfdb9e8..1a37f1f 100644 --- a/http/api/client.go +++ b/http/api/client.go @@ -1,8 +1,11 @@ package api import ( + "gitee.ltd/lxh/logger/log" "github.com/gin-gonic/gin" "wireguard-dashboard/http/param" + "wireguard-dashboard/model/entity" + "wireguard-dashboard/queues" "wireguard-dashboard/repository" "wireguard-dashboard/utils" ) @@ -44,4 +47,23 @@ func (clients) Save(c *gin.Context) { return } + info, ok := c.Get("user") + if !ok { + utils.GinResponse(c).FailedWithMsg("获取信息失败") + return + } + + _, err := repository.Client().Save(p, info.(*entity.User).Id) + if err != nil { + utils.GinResponse(c).FailedWithMsg("操作失败") + return + } + + go func() { + if err = queues.PutAsyncWireguardConfigFile(p.ServerId); err != nil { + log.Errorf("[新增/编辑客户端]同步配置文件失败: %v", err.Error()) + } + }() + + utils.GinResponse(c).OK() } diff --git a/http/api/server.go b/http/api/server.go index ad20c03..e2fa3c8 100644 --- a/http/api/server.go +++ b/http/api/server.go @@ -65,7 +65,7 @@ func (server) SaveServer(c *gin.Context) { go func() { if err = queues.PutAsyncWireguardConfigFile(serverId); err != nil { - log.Errorf("投递同步配置文件任务失败: %s", err.Error()) + log.Errorf("[新增/编辑]投递同步配置文件任务失败: %s", err.Error()) } }() diff --git a/http/api/user.go b/http/api/user.go index 3ae283b..ffc7c5c 100644 --- a/http/api/user.go +++ b/http/api/user.go @@ -56,15 +56,16 @@ func (user) Login(c *gin.Context) { } // 生成token - token, err := component.JWT().GenerateToken(user.Id) + token, expireTime, err := component.JWT().GenerateToken(user.Id) if err != nil { utils.GinResponse(c).FailedWithMsg("登陆失败") return } utils.GinResponse(c).OKWithData(map[string]any{ - "token": token, - "type": "Bearer", + "token": token, + "type": "Bearer", + "expireAt": expireTime.Unix(), }) } diff --git a/http/param/user.go b/http/param/user.go index 3f0cbc7..f4a916b 100644 --- a/http/param/user.go +++ b/http/param/user.go @@ -20,7 +20,7 @@ type SaveUser struct { Avatar string `json:"avatar" form:"avatar" binding:"omitempty"` // 头像 Email string `json:"email" form:"email" binding:"omitempty"` // 联系邮箱 Password string `json:"password" form:"password" binding:"omitempty"` // 密码 - IsAdmin constant.UserType `json:"isAdmin" form:"isAdmin" binding:"required"` // 是否为管理员 0 - 否 | 1 - 是 + IsAdmin constant.UserType `json:"isAdmin" form:"isAdmin" binding:"omitempty"` // 是否为管理员 0 - 否 | 1 - 是 Status constant.UserStatus `json:"status" form:"status" binding:"required"` // 用户状态 0 - 禁用 | 1 - 正常 } diff --git a/model/template_data/wireguard.go b/model/template_data/wireguard.go index 1059bda..2080c04 100644 --- a/model/template_data/wireguard.go +++ b/model/template_data/wireguard.go @@ -28,6 +28,7 @@ type Client struct { } type Keys struct { - PrivateKey string `json:"privateKey"` - PublicKey string `json:"publicKey"` + PrivateKey string `json:"privateKey"` + PublicKey string `json:"publicKey"` + PresharedKey string `json:"presharedKey"` } diff --git a/model/vo/system.go b/model/vo/system.go index f4efb02..b8ccabc 100644 --- a/model/vo/system.go +++ b/model/vo/system.go @@ -1,11 +1,11 @@ package vo type ServerSetting struct { - EndpointAddress string `json:"endpointAddress"` - DnsServers []string `json:"dnsServers"` - 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/queues/async_wg_config.go b/queues/async_wg_config.go index e8a91d7..57ec709 100644 --- a/queues/async_wg_config.go +++ b/queues/async_wg_config.go @@ -80,7 +80,7 @@ func asyncWireguardConfigFile() { Name: v.Name, Email: v.Email, PublicKey: clientKey.PublicKey, - PresharedKey: clientKey.PrivateKey, + PresharedKey: clientKey.PresharedKey, AllowedIPS: v.AllowedIps, PersistentKeepalive: strconv.Itoa(globalSetting.PersistentKeepalive), Endpoint: v.Endpoint, diff --git a/repository/client.go b/repository/client.go index b932c16..d9e5eac 100644 --- a/repository/client.go +++ b/repository/client.go @@ -2,9 +2,13 @@ package repository import ( "encoding/json" + "github.com/spf13/cast" + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "gorm.io/gorm" "wireguard-dashboard/client" "wireguard-dashboard/http/param" + "wireguard-dashboard/model/entity" + "wireguard-dashboard/model/template_data" "wireguard-dashboard/model/vo" "wireguard-dashboard/utils" ) @@ -49,7 +53,69 @@ func (r clientRepo) List(p param.ClientList) (data []vo.Client, total int64, err // @description: 新增/编辑客户端 // @receiver r // @param p +// @param adminId // @return err -func (r clientRepo) Save(p param.SaveClient) (err error) { - return nil +func (r clientRepo) Save(p param.SaveClient, adminId string) (client *entity.Client, err error) { + + ent := &entity.Client{ + Base: entity.Base{ + Id: p.Id, + }, + ServerId: p.ServerId, + Name: p.Name, + Email: p.Email, + SubnetRange: p.SubnetRange, + IpAllocation: p.IpAllocation, + AllowedIps: p.AllowedIPS, + ExtraAllowedIps: p.ExtraAllowedIPS, + Endpoint: p.Endpoint, + UseServerDns: p.UseServerDNS, + EnableAfterCreation: p.EnabledAfterCreation, + UserId: adminId, + Enabled: cast.ToBool(p.Enabled), + } + + // id不为空,更新信息 + if p.Id != "" { + keys, _ := json.Marshal(p.Keys) + ent.Keys = string(keys) + if err = r.Model(&entity.Client{}).Where("id = ?", p.Id).Updates(ent).Error; err != nil { + return + } + return + } + + // 为空,新增 + privateKey, err := wgtypes.GeneratePrivateKey() + if err != nil { + return + } + publicKey := privateKey.PublicKey().String() + presharedKey, err := wgtypes.GenerateKey() + if err != nil { + return + } + keys := template_data.Keys{ + PublicKey: publicKey, + PresharedKey: presharedKey.String(), + } + keysStr, _ := json.Marshal(keys) + + ent = &entity.Client{ + ServerId: p.ServerId, + Name: p.Name, + Email: p.Email, + SubnetRange: p.SubnetRange, + IpAllocation: p.IpAllocation, + AllowedIps: p.AllowedIPS, + ExtraAllowedIps: p.ExtraAllowedIPS, + Endpoint: p.Endpoint, + UseServerDns: p.UseServerDNS, + EnableAfterCreation: p.EnabledAfterCreation, + Keys: string(keysStr), + UserId: adminId, + } + + err = r.Model(&entity.Client{}).Create(ent).Error + return } diff --git a/route/captcha.go b/route/captcha.go index d043e8d..b0f9c3e 100644 --- a/route/captcha.go +++ b/route/captcha.go @@ -8,7 +8,7 @@ import ( // CaptchaApi // @description: 验证码 // @param r -func CaptchaApi(r *gin.Engine) { +func CaptchaApi(r *gin.RouterGroup) { captcha := r.Group("captcha") { captcha.GET("", api.Captcha().GenerateCaptcha) // 生成验证码 diff --git a/route/client.go b/route/client.go index fe1cc0e..e402da0 100644 --- a/route/client.go +++ b/route/client.go @@ -6,9 +6,10 @@ import ( "wireguard-dashboard/middleware" ) -func ClientApi(r *gin.Engine) { +func ClientApi(r *gin.RouterGroup) { apiGroup := r.Group("client", middleware.Authorization()) { - apiGroup.GET("list", api.Client().List) // 客户端列表 + apiGroup.GET("list", api.Client().List) // 客户端列表 + apiGroup.POST("save", api.Client().Save) // 新增/编辑客户端 } } diff --git a/route/route.go b/route/route.go index c90f346..ecf312b 100644 --- a/route/route.go +++ b/route/route.go @@ -2,7 +2,7 @@ package route import "github.com/gin-gonic/gin" -type Option func(engine *gin.Engine) +type Option func(engine *gin.RouterGroup) var options []Option @@ -18,7 +18,7 @@ func InitRouter() *gin.Engine { r.Use(gin.Logger()) for _, opt := range options { - opt(r) + opt(r.Group("api")) } return r diff --git a/route/server.go b/route/server.go index 170cb47..1e57a1d 100644 --- a/route/server.go +++ b/route/server.go @@ -6,7 +6,7 @@ import ( "wireguard-dashboard/middleware" ) -func ServerApi(r *gin.Engine) { +func ServerApi(r *gin.RouterGroup) { apiGroup := r.Group("server", middleware.Authorization()) { apiGroup.GET("", api.Server().GetServer) // 获取服务端信息 diff --git a/route/user.go b/route/user.go index cf7de41..b0e2fc6 100644 --- a/route/user.go +++ b/route/user.go @@ -6,7 +6,7 @@ import ( "wireguard-dashboard/middleware" ) -func UserApi(r *gin.Engine) { +func UserApi(r *gin.RouterGroup) { // 登陆相关API login := r.Group("/login") { diff --git a/utils/wireguard.go b/utils/wireguard.go new file mode 100644 index 0000000..d4b585b --- /dev/null +++ b/utils/wireguard.go @@ -0,0 +1 @@ +package utils