diff --git a/internal/common/db/torrent.go b/internal/common/db/torrent.go index 8f4c473..735ddd2 100644 --- a/internal/common/db/torrent.go +++ b/internal/common/db/torrent.go @@ -22,6 +22,16 @@ func CreateTorrent(torrent *model.Torrent) (err error) { return err } +// PatchTorrent 更新种子信息,根据 torrentID 和 details 更新种子信息 +func PatchTorrent[T *model.Torrent | map[string]any | any](torrentID int32, details T) (err error) { + q := query.Torrent + _, err = q.Where(q.TorrentID.Eq(torrentID)).Updates(details) + if err != nil { + return err + } + return nil +} + // GetTorrentByHash 根据 Hash 获取种子 func GetTorrentByHash(hash string) (*model.Torrent, error) { q := query.Torrent diff --git a/internal/common/db/user.go b/internal/common/db/user.go index 35ed10d..1f8d2a1 100644 --- a/internal/common/db/user.go +++ b/internal/common/db/user.go @@ -24,7 +24,7 @@ func CreateUser(user *model.User) (err error) { } // PatchUser 更新用户信息,根据 userID 和 details 更新用户信息 -func PatchUser(userID int32, details *model.User) (err error) { +func PatchUser[T *model.User | map[string]any | any](userID int32, details T) (err error) { q := query.User ResultInfo, err := q.Where(q.UserID.Eq(userID)).Updates(details) if err != nil { diff --git a/internal/middleware/rbac/access_control.go b/internal/middleware/rbac/access_control.go index 7b64c5a..892edbb 100644 --- a/internal/middleware/rbac/access_control.go +++ b/internal/middleware/rbac/access_control.go @@ -9,7 +9,7 @@ import ( "github.com/gin-gonic/gin" ) -// RABC 获取用户角色,存入上下文,进行权限控制,allowRoles 为允许的角色 +// RABC 获取用户角色,存入上下文,进行权限控制,allowRoles 为允许的角色,为空则不限制 func RABC(allowRoles ...string) gin.HandlerFunc { return func(c *gin.Context) { userID, _ := resp.GetUserIDFromGinContext(c) @@ -23,20 +23,22 @@ func RABC(allowRoles ...string) gin.HandlerFunc { return } - // 检查用户是否有合适的角色 - hasAllowedRole := false - for _, role := range roles { - if util.CheckStringInSlice(role, allowRoles) { - hasAllowedRole = true - break + if len(allowRoles) > 0 { + // 检查用户是否有合适的角色 + hasAllowedRole := false + for _, role := range roles { + if util.CheckStringInSlice(role, allowRoles) { + hasAllowedRole = true + break + } } - } - // 用户没有合适的角色,拦截请求 - if !hasAllowedRole { - resp.AbortWithMsg(c, code.AuthErrorNoPermission, "Role has no permission") - log.Logger.Errorf("RABC Role has no permission, userID: %d, roles: %v", userID, roles) - return + // 用户没有合适的角色,拦截请求 + if !hasAllowedRole { + resp.AbortWithMsg(c, code.AuthErrorNoPermission, "Role has no permission") + log.Logger.Errorf("RABC Role has no permission, userID: %d, roles: %v", userID, roles) + return + } } // 将角色信息存储在 Gin 上下文中 diff --git a/internal/router/api/v1/torrent.go b/internal/router/api/v1/torrent.go index d1862e6..fb8fece 100644 --- a/internal/router/api/v1/torrent.go +++ b/internal/router/api/v1/torrent.go @@ -20,7 +20,11 @@ func TorrentRouterGroup(api *gin.RouterGroup) { jwt.RequireAuth(false), rbac.RABC(role.ADMIN, role.UPLOADER, role.ADVANCED_USER), torrent_service.Upload) - + // 种子编辑 + torrent.POST("edit", + jwt.RequireAuth(false), + rbac.RABC(), + torrent_service.Edit) // 获取种子文件列表 torrent.GET("filelist", jwt.RequireAuth(false), diff --git a/internal/service/torrent/edit.go b/internal/service/torrent/edit.go new file mode 100644 index 0000000..821387b --- /dev/null +++ b/internal/service/torrent/edit.go @@ -0,0 +1,68 @@ +package torrent + +import ( + "github.com/TensoRaws/NuxBT-Backend/internal/common/db" + "github.com/TensoRaws/NuxBT-Backend/module/code" + "github.com/TensoRaws/NuxBT-Backend/module/log" + "github.com/TensoRaws/NuxBT-Backend/module/resp" + "github.com/TensoRaws/NuxBT-Backend/module/role" + "github.com/TensoRaws/NuxBT-Backend/module/util" + "github.com/gin-gonic/gin" +) + +type EditRequest struct { + TorrentID int32 `form:"torrent_id" binding:"required"` + AnidbID int32 `form:"anidb_id" binding:"required"` + AudioCodec string `form:"audio_codec" binding:"required,oneof=FLAC AAC AC3 DTS DDP LPCM other"` + Description string `form:"description" binding:"required"` + Essay string `form:"essay" binding:"required"` + Genre string `form:"genre" binding:"required,oneof=BDrip WEBrip DVDrip Remux Blu-ray WEB-DL DVD HDTV other"` //nolint:lll + Img string `form:"img" binding:"required"` + Language string `form:"language" binding:"required,oneof=Chinese English Japanese other"` + Resolution string `form:"resolution" binding:"required,oneof=480p 720p 1080p 2160p other"` + Subtitle string `form:"subtitle" binding:"required"` + Title string `form:"title" binding:"required"` + VideoCodec string `form:"video_codec" binding:"required,oneof=H.265 H.264 AV1 VP9 other"` +} + +// Edit 更新种子信息 (POST /edit) +func Edit(c *gin.Context) { + // 参数绑定 + var req EditRequest + if err := c.ShouldBind(&req); err != nil { + resp.AbortWithMsg(c, code.RequestErrorInvalidParams, err.Error()) + return + } + + userID, _ := resp.GetUserIDFromGinContext(c) + + roles, err := resp.GetRolesFromGinContext(c) + if err != nil { + resp.AbortWithMsg(c, code.UnknownError, err.Error()) + log.Logger.Error("failed to get roles from gin context: " + err.Error()) + return + } + + bt, err := db.GetTorrentByID(req.TorrentID) + if err != nil { + resp.AbortWithMsg(c, code.DatabaseErrorRecordNotFound, err.Error()) + log.Logger.Error("failed to get torrent by id: " + err.Error()) + return + } + + if bt.UploaderID != userID && !util.CheckStringInSlice(role.ADMIN, roles) { + resp.AbortWithMsg(c, code.AuthErrorNoPermission, "permission denied") + log.Logger.Errorf("permission denied, user id: %v", userID) + return + } + + err = db.PatchTorrent(req.TorrentID, &req) + if err != nil { + resp.AbortWithMsg(c, code.DatabaseErrorRecordPatchFailed, err.Error()) + return + } + + resp.OK(c) + + log.Logger.Infof("update torrent info success, torrent id: %v", req.TorrentID) +} diff --git a/internal/service/torrent/filelist.go b/internal/service/torrent/filelist.go index 6cdd1b5..1ca9320 100644 --- a/internal/service/torrent/filelist.go +++ b/internal/service/torrent/filelist.go @@ -16,7 +16,7 @@ type FileListRequest struct { type FileListResponse []torrent.BitTorrentFileListItem -// FileList 获取种子文件列表 +// FileList 获取种子文件列表 (GET /filelist) func FileList(c *gin.Context) { // 绑定参数 var req FileListRequest diff --git a/internal/service/torrent/official.go b/internal/service/torrent/official.go new file mode 100644 index 0000000..10cbafc --- /dev/null +++ b/internal/service/torrent/official.go @@ -0,0 +1 @@ +package torrent diff --git a/internal/service/torrent/upload.go b/internal/service/torrent/upload.go index a5aba2b..19db2c5 100644 --- a/internal/service/torrent/upload.go +++ b/internal/service/torrent/upload.go @@ -21,7 +21,7 @@ type UploadRequest struct { AnidbID int32 `form:"anidb_id" binding:"required"` AudioCodec string `form:"audio_codec" binding:"required,oneof=FLAC AAC AC3 DTS DDP LPCM other"` Description string `form:"description" binding:"required"` - Essay *string `form:"essay" binding:"omitempty"` + Essay string `form:"essay" binding:"required"` Genre string `form:"genre" binding:"required,oneof=BDrip WEBrip DVDrip Remux Blu-ray WEB-DL DVD HDTV other"` //nolint:lll Img string `form:"img" binding:"required"` Language string `form:"language" binding:"required,oneof=Chinese English Japanese other"` @@ -131,7 +131,7 @@ func Upload(c *gin.Context) { Status: status, Title: req.Title, Subtitle: req.Subtitle, - Essay: *req.Essay, + Essay: req.Essay, Description: req.Description, Genre: req.Genre, AnidbID: req.AnidbID, diff --git a/internal/service/user/profile.go b/internal/service/user/profile.go index 79e1610..116894d 100644 --- a/internal/service/user/profile.go +++ b/internal/service/user/profile.go @@ -35,6 +35,7 @@ type ProfileOthersRequest struct { func ProfileMe(c *gin.Context) { userID, _ := resp.GetUserIDFromGinContext(c) + // 更新活跃时间 err := db.PatchUser(userID, &model.User{LastActive: time.Now()}) if err != nil { resp.AbortWithMsg(c, code.UnknownError, err.Error()) diff --git a/internal/service/user/profile_update.go b/internal/service/user/profile_update.go index 0f342cb..49f04a8 100644 --- a/internal/service/user/profile_update.go +++ b/internal/service/user/profile_update.go @@ -1,7 +1,6 @@ package user import ( - "github.com/TensoRaws/NuxBT-Backend/dal/model" "github.com/TensoRaws/NuxBT-Backend/internal/common/db" "github.com/TensoRaws/NuxBT-Backend/module/code" "github.com/TensoRaws/NuxBT-Backend/module/log" @@ -11,12 +10,12 @@ import ( ) type ProfileUpdateRequest struct { - Avatar *string `json:"avatar" binding:"omitempty"` - Background *string `json:"background" binding:"omitempty"` - Email *string `json:"email" binding:"omitempty,email"` - Private *bool `json:"private" binding:"omitempty"` - Signature *string `json:"signature" binding:"omitempty"` - Username *string `json:"username" binding:"omitempty"` + Avatar string `json:"avatar" binding:"required"` + Background string `json:"background" binding:"required"` + Email string `json:"email" binding:"required,email"` + Private *bool `json:"private" binding:"required"` + Signature string `json:"signature" binding:"required"` + Username string `json:"username" binding:"required"` } // ProfileUpdate 用户信息更新 (POST /profile/update) @@ -28,7 +27,7 @@ func ProfileUpdate(c *gin.Context) { return } - err := util.CheckUsername(*req.Username) + err := util.CheckUsername(req.Username) if err != nil { resp.AbortWithMsg(c, code.UserErrorInvalidUsername, err.Error()) return @@ -36,15 +35,14 @@ func ProfileUpdate(c *gin.Context) { userID, _ := resp.GetUserIDFromGinContext(c) + // 没传数字,直接序列化嗯造 + updateInfo, err := util.StructToMap(req) + if err != nil { + resp.AbortWithMsg(c, code.UnknownError, err.Error()) + return + } // 执行更新 - err = db.PatchUser(userID, &model.User{ - Avatar: *req.Avatar, - Background: *req.Background, - Email: *req.Email, - Private: *req.Private, - Signature: *req.Signature, - Username: *req.Username, - }) + err = db.PatchUser(userID, updateInfo) if err != nil { resp.AbortWithMsg(c, code.DatabaseErrorRecordPatchFailed, err.Error()) diff --git a/internal/service/user/reset.go b/internal/service/user/reset.go index 3b4c198..149a2be 100644 --- a/internal/service/user/reset.go +++ b/internal/service/user/reset.go @@ -33,9 +33,7 @@ func ResetPassword(c *gin.Context) { } // 修改密码 - err = db.PatchUser(userID, &model.User{ - Password: string(password), - }) + err = db.PatchUser(userID, &model.User{Password: string(password)}) if err != nil { resp.AbortWithMsg(c, code.DatabaseErrorRecordPatchFailed, "reset password fail") diff --git a/module/util/ds.go b/module/util/ds.go index 87b09f3..4be90a7 100644 --- a/module/util/ds.go +++ b/module/util/ds.go @@ -17,7 +17,7 @@ func StringToStruct(str string, s interface{}) error { return sonic.Unmarshal([]byte(str), s) } -// StructToMap 结构体转 map[string]interface{} +// StructToMap 结构体转 map[string]interface{},请勿在有数字情况下使用,请使用反射 func StructToMap(s interface{}) (map[string]interface{}, error) { // 使用 sonic 将结构体序列化为 JSON jsonBytes, err := sonic.Marshal(s)