diff --git a/.gitignore b/.gitignore index 09bcaa1..8d63a33 100644 --- a/.gitignore +++ b/.gitignore @@ -118,4 +118,3 @@ $RECYCLE.BIN/ # Air tmp -conf/nuxbt.yml diff --git a/README.md b/README.md index d584ad6..b986643 100644 --- a/README.md +++ b/README.md @@ -1 +1,27 @@ # NuxBT-Backend + +### Dev + +lint + +```shell +make lint +``` + +test + +```shell +make test +``` + +gorm gen + +```shell +make gen +``` + +build + +```shell +make build +``` diff --git a/conf/nuxbt.yml b/conf/nuxbt.yml index 21ebfbe..0532a6c 100644 --- a/conf/nuxbt.yml +++ b/conf/nuxbt.yml @@ -17,7 +17,7 @@ log: db: type: mysql # mysql, postgres - host: 192.168.178.131 + host: 127.0.0.1 port: 5432 username: root password: 123456 @@ -25,14 +25,14 @@ db: ssl: false redis: - host: 192.168.178.131 + host: 127.0.0.1 port: 6379 password: poolSize: 1000 oss: type: minio # minio, cos - endpoint: 192.168.178.131:9000 + endpoint: 127.0.0.1:9000 accessKey: ChYm7ufIwNAOzq6PQPCA secretKey: udicP52IwRbmo2hf6lFvjUS7NP5BhlAdsGNIuDE5 region: local diff --git a/internal/middleware/cache/ip_limiter.go b/internal/middleware/cache/ip_limiter.go index 4cf8438..a7f48cd 100644 --- a/internal/middleware/cache/ip_limiter.go +++ b/internal/middleware/cache/ip_limiter.go @@ -11,9 +11,10 @@ import ( redisLimiter "github.com/ulule/limiter/v3/drivers/store/redis" ) -func NewRateLimiter(redisClient *cache.Client, limit int, slidingWindow time.Duration) gin.HandlerFunc { +// NewRateLimiter returns a new instance of a rate limiter middleware. +func NewRateLimiter(redisClient *cache.Client, limit int, t time.Duration) gin.HandlerFunc { rate := limiter.Rate{ - Period: slidingWindow, + Period: t, Limit: int64(limit), } store, err := redisLimiter.NewStore(redisClient.C) diff --git a/internal/middleware/cache/jwt_blacklist.go b/internal/middleware/cache/jwt_blacklist.go deleted file mode 100644 index 864d299..0000000 --- a/internal/middleware/cache/jwt_blacklist.go +++ /dev/null @@ -1,47 +0,0 @@ -package cache - -import ( - "github.com/TensoRaws/NuxBT-Backend/internal/middleware/jwt" - "github.com/TensoRaws/NuxBT-Backend/module/cache" - "github.com/TensoRaws/NuxBT-Backend/module/log" - "github.com/TensoRaws/NuxBT-Backend/module/util" - "github.com/gin-gonic/gin" -) - -// JWTBlacklist 检查JWT是否在黑名单中 -func JWTBlacklist(redisClient *cache.Client, addBlacklist bool) gin.HandlerFunc { - return func(c *gin.Context) { - // 从输入的 url 中查询 token 值 - token := c.Query("token") - if len(token) == 0 { - // 从输入的表单中查询 token 值 - token = c.PostForm("token") - } - - if len(token) == 0 { - util.AbortWithMsg(c, "JSON WEB TOKEN IS NULL") - return - } - - log.Logger.Info("Get token successfully") - - // 检查 Token 是否存在于 Redis 黑名单中 - exists := redisClient.Exists(token).Val() - if exists > 0 { - log.Logger.Info("Token has been blacklisted") - util.AbortWithMsg(c, "Token has been blacklisted") - return - } - - // 如果 Token 不在黑名单中,继续处理请求 - c.Next() - - // 如果启用拉黑模式,处理请求拉黑 Token - if addBlacklist { - err := redisClient.Set(token, "", jwt.GetJWTTokenExpiredDuration()).Err() - if err != nil { - log.Logger.Error("Error adding token to blacklist: " + err.Error()) - } - } - } -} diff --git a/internal/middleware/cache/response.go b/internal/middleware/cache/response.go index 534b27a..8d2ecc9 100644 --- a/internal/middleware/cache/response.go +++ b/internal/middleware/cache/response.go @@ -11,7 +11,7 @@ import ( "github.com/gin-gonic/gin" ) -// Response 缓存接口响应的中间件 +// Response 缓存接口响应的中间件,queryFilter 为需要去除的 query 参数,使用其他的来构建缓存 key func Response(redisClient *cache.Client, ttl time.Duration, queryFilter ...string) gin.HandlerFunc { redisStore := persist.NewRedisStore(redisClient.C) diff --git a/internal/middleware/jwt/auth.go b/internal/middleware/jwt/auth.go index 147ed37..ffba746 100644 --- a/internal/middleware/jwt/auth.go +++ b/internal/middleware/jwt/auth.go @@ -1,27 +1,47 @@ package jwt import ( + "github.com/TensoRaws/NuxBT-Backend/module/cache" + "github.com/TensoRaws/NuxBT-Backend/module/log" "github.com/TensoRaws/NuxBT-Backend/module/util" "github.com/gin-gonic/gin" ) // RequireAuth 鉴权中间件 // 如果用户携带的 token 验证通过,将 user_id 存入上下文中然后执行下一个 Handler -func RequireAuth() gin.HandlerFunc { +func RequireAuth(redisClient *cache.Client, addBlacklist bool) gin.HandlerFunc { return func(c *gin.Context) { - // 从输入的 url 中查询 token 值 - token := c.Query("token") - // auth = [[header][claims][signature]] - // 解析 token + // 从请求头中获取 token + token := c.Request.Header.Get("Authorization") + + log.Logger.Info("Get token successfully") + + // 检查 Token 是否存在于 Redis 黑名单中 + exists := redisClient.Exists(token).Val() + if exists > 0 { + log.Logger.Info("Token has been blacklisted") + util.AbortWithMsg(c, "Token has been blacklisted") + return + } + // 如果 Token 不在黑名单中,继续处理请求 claims, err := ParseToken(token) if err != nil { util.AbortWithMsg(c, "TOKEN IS INVALID, Please Log In") return } - userID := claims.ID + // 在上下文中存储 token 和 user_id + c.Set("token", token) c.Set("user_id", userID) // 放行 c.Next() + + // 如果启用拉黑模式,处理请求拉黑 Token + if addBlacklist { + err := redisClient.Set(token, "", GetJWTTokenExpiredDuration()).Err() + if err != nil { + log.Logger.Error("Error adding token to blacklist: " + err.Error()) + } + } } } diff --git a/internal/middleware/jwt/jwt.go b/internal/middleware/jwt/jwt.go index a5c0cb0..71b1f95 100644 --- a/internal/middleware/jwt/jwt.go +++ b/internal/middleware/jwt/jwt.go @@ -12,27 +12,14 @@ import ( "github.com/golang-jwt/jwt/v5" ) -var ( - TokenExpiredDuration time.Duration - mySigningKey []byte -) - // GetJWTTokenExpiredDuration 根据配置文件获取 jwt 的过期时间 func GetJWTTokenExpiredDuration() time.Duration { - if TokenExpiredDuration != 0 { - return TokenExpiredDuration - } - TokenExpiredDuration = time.Minute * time.Duration(config.JwtConfig.Timeout) - return TokenExpiredDuration + return time.Minute * time.Duration(config.JwtConfig.Timeout) } // GetJWTSigningKey 根据配置文件获取 jwt 的签名密钥 func GetJWTSigningKey() []byte { - if len(mySigningKey) != 0 { - return mySigningKey - } - mySigningKey = []byte(config.JwtConfig.Key) - return mySigningKey + return []byte(config.JwtConfig.Key) } // GenerateToken 生成 jwt(json web token) diff --git a/internal/router/api/v1/api.go b/internal/router/api/v1/api.go index 4640a06..2a8a081 100644 --- a/internal/router/api/v1/api.go +++ b/internal/router/api/v1/api.go @@ -36,23 +36,18 @@ func NewAPI() *gin.Engine { user.POST("login", user_service.Login) // 用户登出 user.POST("logout", - middleware_cache.JWTBlacklist(cache.Clients[cache.JWTBlacklist], true), // 把 token 拉黑 - jwt.RequireAuth(), + jwt.RequireAuth(cache.Clients[cache.JWTBlacklist], true), // 把 token 拉黑 user_service.Logout, ) // 用户信息 user.GET("profile/me", - middleware_cache.JWTBlacklist(cache.Clients[cache.JWTBlacklist], false), - jwt.RequireAuth(), - middleware_cache.Response(cache.Clients[cache.RespCache], 1*time.Minute), + jwt.RequireAuth(cache.Clients[cache.JWTBlacklist], false), user_service.ProfileMe, ) - //修改密码 + // 修改密码 user.POST("password/reset", - middleware_cache.JWTBlacklist(cache.Clients[cache.JWTBlacklist], true), // 把 token 拉黑 - jwt.RequireAuth(), - middleware_cache.Response(cache.Clients[cache.RespCache], 1*time.Minute), - user_service.ReSetPass) + jwt.RequireAuth(cache.Clients[cache.JWTBlacklist], true), // 把 token 拉黑 + user_service.ResetPassword) } } diff --git a/internal/service/user/dao.go b/internal/service/common/dao/user.go similarity index 82% rename from internal/service/user/dao.go rename to internal/service/common/dao/user.go index 572a10e..2f922c4 100644 --- a/internal/service/user/dao.go +++ b/internal/service/common/dao/user.go @@ -1,4 +1,4 @@ -package user +package dao import ( "github.com/TensoRaws/NuxBT-Backend/dal/model" @@ -13,14 +13,17 @@ func CreateUser(user *model.User) (err error) { return err } -// 修改用户密码 -func SetUserPass(user *model.User, newpass string) (err error) { +// SetUserPassword 修改用户密码 +func SetUserPassword(user *model.User, newpass string) (err error) { u := query.User password, err := bcrypt.GenerateFromPassword([]byte(newpass), bcrypt.DefaultCost) if err != nil { return err } - u.Where(u.UserID.Eq(user.UserID)).Update(u.Password, string(password)) + _, err = u.Where(u.UserID.Eq(user.UserID)).Update(u.Password, string(password)) + if err != nil { + return err + } return err } diff --git a/internal/service/user/login.go b/internal/service/user/login.go index 878b4e9..c506c7f 100644 --- a/internal/service/user/login.go +++ b/internal/service/user/login.go @@ -2,6 +2,7 @@ package user import ( "github.com/TensoRaws/NuxBT-Backend/internal/middleware/jwt" + "github.com/TensoRaws/NuxBT-Backend/internal/service/common/dao" "github.com/TensoRaws/NuxBT-Backend/module/util" "github.com/gin-gonic/gin" "golang.org/x/crypto/bcrypt" @@ -25,7 +26,7 @@ func Login(c *gin.Context) { } // GORM 查询 - user, err := GetUserByEmail(req.Email) + user, err := dao.GetUserByEmail(req.Email) if err != nil { util.AbortWithMsg(c, "User not found") return diff --git a/internal/service/user/profile.go b/internal/service/user/profile.go index 1d04ed0..259e834 100644 --- a/internal/service/user/profile.go +++ b/internal/service/user/profile.go @@ -3,6 +3,7 @@ package user import ( "strconv" + "github.com/TensoRaws/NuxBT-Backend/internal/service/common/dao" "github.com/TensoRaws/NuxBT-Backend/module/log" "github.com/TensoRaws/NuxBT-Backend/module/util" "github.com/gin-gonic/gin" @@ -31,13 +32,13 @@ func ProfileMe(c *gin.Context) { return } - user, err := GetUserByID(int32(userID)) + user, err := dao.GetUserByID(int32(userID)) if err != nil { util.AbortWithMsg(c, "User not found") return } - roles, err := GetUserRolesByID(int32(userID)) + roles, err := dao.GetUserRolesByID(int32(userID)) if err != nil { log.Logger.Info("Failed to get user roles: " + err.Error()) roles = []string{} diff --git a/internal/service/user/register.go b/internal/service/user/register.go index 47cd45a..2ad0425 100644 --- a/internal/service/user/register.go +++ b/internal/service/user/register.go @@ -5,6 +5,7 @@ import ( "time" "github.com/TensoRaws/NuxBT-Backend/dal/model" + "github.com/TensoRaws/NuxBT-Backend/internal/service/common/dao" "github.com/TensoRaws/NuxBT-Backend/module/config" "github.com/TensoRaws/NuxBT-Backend/module/log" "github.com/TensoRaws/NuxBT-Backend/module/util" @@ -12,7 +13,8 @@ import ( "golang.org/x/crypto/bcrypt" ) -// RegisterRequest Query binding 需要打 form 标签 +// RegisterRequest +// Query binding 需要打 form 标签,Body json binding 需要打 json 标签 type RegisterRequest struct { Username string `json:"username" binding:"required"` Password string `json:"password" binding:"required"` @@ -45,7 +47,7 @@ func Register(c *gin.Context) { // do something // 未实现 // OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO - log.Logger.Info("invitation code: ", *req.InvitationCode) + log.Logger.Info("invitation code: " + *req.InvitationCode) } password, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) if err != nil { @@ -54,7 +56,7 @@ func Register(c *gin.Context) { return } // 注册 - err = CreateUser(&model.User{ + err = dao.CreateUser(&model.User{ Username: req.Username, Email: req.Email, Password: string(password), @@ -66,7 +68,7 @@ func Register(c *gin.Context) { return } - user, err := GetUserByEmail(req.Email) + user, err := dao.GetUserByEmail(req.Email) if err != nil { util.AbortWithMsg(c, "failed to get user by email") log.Logger.Error("failed to get user by email: " + err.Error()) diff --git a/internal/service/user/reset.go b/internal/service/user/reset.go index 580cc10..f290508 100644 --- a/internal/service/user/reset.go +++ b/internal/service/user/reset.go @@ -1,42 +1,44 @@ package user import ( - "fmt" - + "github.com/TensoRaws/NuxBT-Backend/internal/service/common/dao" + "github.com/TensoRaws/NuxBT-Backend/module/log" "github.com/TensoRaws/NuxBT-Backend/module/util" "github.com/gin-gonic/gin" ) -type SetRequest struct { - Newpassword string `json:"new_password" binding:"required"` +type ResetPasswordRequest struct { + NewPassword string `json:"new_password" binding:"required"` } -// 重置密码 (POST /password/reset) -func ReSetPass(c *gin.Context) { +// ResetPassword 重置密码 (POST /password/reset) +func ResetPassword(c *gin.Context) { // 绑定参数 - var req SetRequest + var req ResetPasswordRequest if err := c.ShouldBindJSON(&req); err != nil { util.AbortWithMsg(c, "invalid request") return } - fmt.Println(req.Newpassword) + // 鉴权 userID, err := util.GetUserIDFromGinContext(c) if err != nil { util.AbortWithMsg(c, "Please login first") return } - user, err := GetUserByID(int32(userID)) + + user, err := dao.GetUserByID(int32(userID)) if err != nil { util.AbortWithMsg(c, "User not found") return } // 修改密码 - err = SetUserPass(user, req.Newpassword) + err = dao.SetUserPassword(user, req.NewPassword) if err != nil { util.AbortWithMsg(c, "reset password fail") } // 返回 util.OKWithMsg(c, "reset password success") + log.Logger.Info("Reset password success: " + util.StructToString(user)) }