From e2a83a68c050b1d9157e8ae687525aa31901d710 Mon Sep 17 00:00:00 2001 From: zijiren233 Date: Sat, 21 Oct 2023 14:28:36 +0800 Subject: [PATCH] Feat: lazy init room --- cmd/server.go | 1 - go.mod | 2 +- go.sum | 2 - internal/bootstrap/room.go | 24 ------ internal/bootstrap/rtmp.go | 4 +- internal/db/db.go | 42 +++++++++ internal/db/room.go | 35 ++++---- internal/model/room.go | 12 ++- internal/model/setting.go | 4 + internal/op/room.go | 4 +- internal/op/rooms.go | 53 ++++++------ internal/op/users.go | 18 ++-- internal/provider/github.go | 48 +---------- server/handlers/movie.go | 19 +++-- server/handlers/room.go | 164 ++++++++++++++++++++---------------- server/middlewares/auth.go | 4 +- server/oauth2/auth.go | 3 + utils/utils.go | 10 +-- utils/utils_test.go | 26 +++--- 19 files changed, 238 insertions(+), 237 deletions(-) delete mode 100644 internal/bootstrap/room.go diff --git a/cmd/server.go b/cmd/server.go index 6e36678..e3e4db9 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -32,7 +32,6 @@ var ServerCmd = &cobra.Command{ bootstrap.InitProvider, bootstrap.InitOp, bootstrap.InitRtmp, - bootstrap.InitRoom, ) if !flags.DisableUpdateCheck { boot.Add(bootstrap.InitCheckUpdate) diff --git a/go.mod b/go.mod index 6df391b..6420321 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,6 @@ require ( github.com/google/uuid v1.3.1 github.com/gorilla/websocket v1.5.0 github.com/json-iterator/go v1.1.12 - github.com/maruel/natural v1.1.0 github.com/mitchellh/go-homedir v1.1.0 github.com/natefinch/lumberjack v2.0.0+incompatible github.com/quic-go/quic-go v0.39.1 @@ -66,6 +65,7 @@ require ( github.com/jinzhu/now v1.1.5 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/leodido/go-urn v1.2.4 // indirect + github.com/maruel/natural v1.1.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect diff --git a/go.sum b/go.sum index 9f04793..69fd45f 100644 --- a/go.sum +++ b/go.sum @@ -286,8 +286,6 @@ gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls= gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM= modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak= -modernc.org/libc v1.25.0 h1:nbTrmtr6NSdZe59ut3MvXS4ZQQPfxhicocdlKbkmZkg= -modernc.org/libc v1.25.0/go.mod h1:DaG/4Q3LRRdqpiLyP0C2m1B8ZMGkQ+cCgOIjEtQlYhQ= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= diff --git a/internal/bootstrap/room.go b/internal/bootstrap/room.go deleted file mode 100644 index 410a4b7..0000000 --- a/internal/bootstrap/room.go +++ /dev/null @@ -1,24 +0,0 @@ -package bootstrap - -import ( - "context" - - log "github.com/sirupsen/logrus" - "github.com/synctv-org/synctv/internal/db" - "github.com/synctv-org/synctv/internal/op" -) - -func InitRoom(ctx context.Context) error { - r, err := db.GetAllRooms() - if err != nil { - return err - } - for _, room := range r { - _, err := op.LoadRoom(room) - if err != nil { - log.Errorf("load room error: %v", err) - return err - } - } - return nil -} diff --git a/internal/bootstrap/rtmp.go b/internal/bootstrap/rtmp.go index 5f8d1c6..4d25c29 100644 --- a/internal/bootstrap/rtmp.go +++ b/internal/bootstrap/rtmp.go @@ -32,7 +32,7 @@ func auth(ReqAppName, ReqChannelName string, IsPublisher bool) (*rtmps.Channel, log.Errorf("rtmp: parse channel name to id error: %v", err) return nil, err } - r, err := op.GetRoomByID(uint(id)) + r, err := op.LoadOrInitRoomByID(uint(id)) if err != nil { log.Errorf("rtmp: get room by id error: %v", err) return nil, err @@ -49,7 +49,7 @@ func auth(ReqAppName, ReqChannelName string, IsPublisher bool) (*rtmps.Channel, log.Errorf("rtmp: parse channel name to id error: %v", err) return nil, err } - r, err := op.GetRoomByID(uint(id)) + r, err := op.LoadOrInitRoomByID(uint(id)) if err != nil { log.Errorf("rtmp: get room by id error: %v", err) return nil, err diff --git a/internal/db/db.go b/internal/db/db.go index 19c628b..9cfac32 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -45,3 +45,45 @@ func Close() { return } } + +func Paginate(page, pageSize int) func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + if page <= 0 { + page = 1 + } + if pageSize <= 0 { + pageSize = 10 + } + + offset := (page - 1) * pageSize + return db.Offset(offset).Limit(pageSize) + } +} + +func OrderByAsc(column string) func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + return db.Order(column + " asc") + } +} + +func OrderByDesc(column string) func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + return db.Order(column + " desc") + } +} + +func OrderByCreatedAtAsc(db *gorm.DB) *gorm.DB { + return db.Order("created_at asc") +} + +func OrderByCreatedAtDesc(db *gorm.DB) *gorm.DB { + return db.Order("created_at desc") +} + +func OrderByIDAsc(db *gorm.DB) *gorm.DB { + return db.Order("id asc") +} + +func OrderByIDDesc(db *gorm.DB) *gorm.DB { + return db.Order("id desc") +} diff --git a/internal/db/room.go b/internal/db/room.go index c505f8e..88f2f9b 100644 --- a/internal/db/room.go +++ b/internal/db/room.go @@ -13,7 +13,7 @@ type CreateRoomConfig func(r *model.Room) func WithSetting(setting model.Setting) CreateRoomConfig { return func(r *model.Room) { - r.Setting = setting + r.Settings = setting } } @@ -157,29 +157,26 @@ func SetRoomHashedPassword(roomID uint, hashedPassword []byte) error { return err } -func GetAllRooms() ([]*model.Room, error) { +func GetAllRooms(scopes ...func(*gorm.DB) *gorm.DB) []*model.Room { rooms := []*model.Room{} - err := db.Find(&rooms).Error - if err != nil && errors.Is(err, gorm.ErrRecordNotFound) { - return rooms, nil - } - return rooms, err + db.Scopes(scopes...).Find(&rooms) + return rooms } -func GetAllRoomsAndCreator() ([]*model.Room, error) { +func GetAllRoomsWithoutHidden(scopes ...func(*gorm.DB) *gorm.DB) []*model.Room { rooms := []*model.Room{} - err := db.Preload("Creater").Find(&rooms).Error - if err != nil && errors.Is(err, gorm.ErrRecordNotFound) { - return rooms, nil - } - return rooms, err + db.Preload("Setting").Where("setting.hidden = ?", false).Scopes(scopes...).Find(&rooms) + return rooms } -func GetAllRoomsByUserID(userID uint) ([]*model.Room, error) { +func GetAllRoomsAndCreator(scopes ...func(*gorm.DB) *gorm.DB) []*model.Room { rooms := []*model.Room{} - err := db.Where("creator_id = ?", userID).Find(&rooms).Error - if err != nil && errors.Is(err, gorm.ErrRecordNotFound) { - return rooms, nil - } - return rooms, err + db.Preload("Creator").Scopes(scopes...).Find(&rooms) + return rooms +} + +func GetAllRoomsByUserID(userID uint) []*model.Room { + rooms := []*model.Room{} + db.Where("creator_id = ?", userID).Find(&rooms) + return rooms } diff --git a/internal/model/room.go b/internal/model/room.go index 775b4b8..58b7d79 100644 --- a/internal/model/room.go +++ b/internal/model/room.go @@ -8,14 +8,18 @@ import ( type Room struct { gorm.Model - Name string `gorm:"not null;uniqueIndex"` - Setting - CreatorID uint `gorm:"index"` + Name string `gorm:"not null;uniqueIndex"` + Settings Setting `gorm:"foreignKey:RoomID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` + CreatorID uint `gorm:"index"` HashedPassword []byte GroupUserRelations []RoomUserRelation `gorm:"foreignKey:RoomID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` Movies []Movie `gorm:"foreignKey:RoomID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` } +func (r *Room) NeedPassword() bool { + return len(r.HashedPassword) != 0 +} + func (r *Room) CheckPassword(password string) bool { - return len(r.HashedPassword) == 0 || bcrypt.CompareHashAndPassword(r.HashedPassword, stream.StringToBytes(password)) == nil + return !r.NeedPassword() || bcrypt.CompareHashAndPassword(r.HashedPassword, stream.StringToBytes(password)) == nil } diff --git a/internal/model/setting.go b/internal/model/setting.go index 18cbfbd..fe2ee1b 100644 --- a/internal/model/setting.go +++ b/internal/model/setting.go @@ -1,5 +1,9 @@ package model +import "gorm.io/gorm" + type Setting struct { + gorm.Model + RoomID uint `gorm:"uniqueIndex"` Hidden bool } diff --git a/internal/op/room.go b/internal/op/room.go index 0fd9054..e85be2b 100644 --- a/internal/op/room.go +++ b/internal/op/room.go @@ -40,7 +40,7 @@ func (r *Room) LazyInit() (err error) { r.hub = newHub(r.ID) var ms []*model.Movie - ms, err = r.GetAllMoviesByRoomID() + ms, err = r.GetAllMovies() if err != nil { log.Errorf("failed to get movies: %s", err.Error()) return @@ -331,7 +331,7 @@ func (r *Room) GetMoviesCount() (int, error) { return GetMoviesCountByRoomID(r.ID) } -func (r *Room) GetAllMoviesByRoomID() ([]*model.Movie, error) { +func (r *Room) GetAllMovies() ([]*model.Movie, error) { ms, err := GetAllMoviesByRoomID(r.ID) if err != nil { return nil, err diff --git a/internal/op/rooms.go b/internal/op/rooms.go index 3dd77ef..533b9fd 100644 --- a/internal/op/rooms.go +++ b/internal/op/rooms.go @@ -3,7 +3,6 @@ package op import ( "errors" "hash/crc32" - "sync/atomic" "github.com/synctv-org/synctv/internal/db" "github.com/synctv-org/synctv/internal/model" @@ -17,35 +16,30 @@ func CreateRoom(name, password string, conf ...db.CreateRoomConfig) (*Room, erro if err != nil { return nil, err } - return initRoom(r) + return InitRoom(r) } -type RoomConf func(r *Room) - -func WithVersion(version uint32) RoomConf { - return func(r *Room) { - atomic.StoreUint32(&r.version, version) - } -} - -func initRoom(room *model.Room, conf ...RoomConf) (*Room, error) { +func InitRoom(room *model.Room) (*Room, error) { r := &Room{ Room: *room, version: crc32.ChecksumIEEE(room.HashedPassword), current: newCurrent(), } - for _, c := range conf { - c(r) - } r, loaded := roomCache.LoadOrStore(room.ID, r) if loaded { - return r, errors.New("room already exists") + return r, errors.New("room already init") } return r, nil } -func LoadRoom(room *model.Room) (*Room, error) { - return initRoom(room) +func LoadOrInitRoom(room *model.Room) (r *Room, loaded bool) { + r = &Room{ + Room: *room, + version: crc32.ChecksumIEEE(room.HashedPassword), + current: newCurrent(), + } + r, loaded = roomCache.LoadOrStore(room.ID, r) + return } func DeleteRoom(room *Room) error { @@ -63,16 +57,25 @@ func DeleteRoomByID(id uint) error { return db.DeleteRoomByID(r.ID) } -func GetRoomByID(id uint) (*Room, error) { +func LoadRoomByID(id uint) (*Room, error) { r2, ok := roomCache.Load(id) if ok { return r2, nil } - r, err := db.GetRoomByID(id) + return nil, errors.New("room not found") +} + +func LoadOrInitRoomByID(id uint) (*Room, error) { + r, ok := roomCache.Load(id) + if ok { + return r, nil + } + room, err := db.GetRoomByID(id) if err != nil { return nil, err } - return initRoom(r) + r, _ = LoadOrInitRoom(room) + return r, nil } func HasRoom(roomID uint) bool { @@ -96,14 +99,14 @@ func HasRoomByName(name string) bool { } func SetRoomPassword(roomID uint, password string) error { - r, err := GetRoomByID(roomID) + r, err := LoadOrInitRoomByID(roomID) if err != nil { return err } return r.SetPassword(password) } -func GetAllRooms() []*Room { +func GetAllRoomsInCache() []*Room { rooms := make([]*Room, roomCache.Len()) roomCache.Range(func(key uint, value *Room) bool { rooms = append(rooms, value) @@ -112,7 +115,7 @@ func GetAllRooms() []*Room { return rooms } -func GetAllRoomsWithNoNeedPassword() []*Room { +func GetAllRoomsInCacheWithNoNeedPassword() []*Room { rooms := make([]*Room, roomCache.Len()) roomCache.Range(func(key uint, value *Room) bool { if !value.NeedPassword() { @@ -123,10 +126,10 @@ func GetAllRoomsWithNoNeedPassword() []*Room { return rooms } -func GetAllRoomsWithoutHidden() []*Room { +func GetAllRoomsInCacheWithoutHidden() []*Room { rooms := make([]*Room, 0, roomCache.Len()) roomCache.Range(func(key uint, value *Room) bool { - if !value.Setting.Hidden { + if !value.Settings.Hidden { rooms = append(rooms, value) } return true diff --git a/internal/op/users.go b/internal/op/users.go index 95cfba3..2b8002c 100644 --- a/internal/op/users.go +++ b/internal/op/users.go @@ -77,22 +77,20 @@ func CreateOrLoadUser(username string, p provider.OAuth2Provider, pid uint, conf } func DeleteUserByID(userID uint) error { - rs, err := db.GetAllRoomsByUserID(userID) - if err != nil { - return err - } - err = db.DeleteUserByID(userID) + err := db.DeleteUserByID(userID) if err != nil { return err } userCache.Remove(userID) - for _, r := range rs { - r2, loaded := roomCache.LoadAndDelete(r.ID) - if loaded { - r2.close() + roomCache.Range(func(key uint, value *Room) bool { + if value.CreatorID == userID { + roomCache.Delete(key) + value.close() } - } + return true + }) + return nil } diff --git a/internal/provider/github.go b/internal/provider/github.go index 9132c3d..4040c19 100644 --- a/internal/provider/github.go +++ b/internal/provider/github.go @@ -3,7 +3,6 @@ package provider import ( "context" "net/http" - "time" json "github.com/json-iterator/go" "golang.org/x/oauth2" @@ -59,51 +58,8 @@ func (p *GithubProvider) GetUserInfo(ctx context.Context, config *oauth2.Config, } type githubUserInfo struct { - Login string `json:"login"` - ID uint `json:"id"` - NodeID string `json:"node_id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - Name string `json:"name"` - Company interface{} `json:"company"` - Blog string `json:"blog"` - Location string `json:"location"` - Email interface{} `json:"email"` - Hireable interface{} `json:"hireable"` - Bio string `json:"bio"` - TwitterUsername interface{} `json:"twitter_username"` - PublicRepos int `json:"public_repos"` - PublicGists int `json:"public_gists"` - Followers int `json:"followers"` - Following int `json:"following"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PrivateGists int `json:"private_gists"` - TotalPrivateRepos int `json:"total_private_repos"` - OwnedPrivateRepos int `json:"owned_private_repos"` - DiskUsage int `json:"disk_usage"` - Collaborators int `json:"collaborators"` - TwoFactorAuthentication bool `json:"two_factor_authentication"` - Plan Plan `json:"plan"` -} -type Plan struct { - Name string `json:"name"` - Space int `json:"space"` - Collaborators int `json:"collaborators"` - PrivateRepos int `json:"private_repos"` + Login string `json:"login"` + ID uint `json:"id"` } func init() { diff --git a/server/handlers/movie.go b/server/handlers/movie.go index edf0ffe..25e5a98 100644 --- a/server/handlers/movie.go +++ b/server/handlers/movie.go @@ -24,32 +24,32 @@ import ( "github.com/zijiren233/livelib/protocol/httpflv" ) -func GetPageAndMax(ctx *gin.Context) (int64, int64, error) { - max, err := strconv.ParseInt(ctx.DefaultQuery("max", "10"), 10, 64) +func GetPageAndPageSize(ctx *gin.Context) (int, int, error) { + pageSize, err := strconv.Atoi(ctx.DefaultQuery("max", "10")) if err != nil { return 0, 0, errors.New("max must be a number") } - page, err := strconv.ParseInt(ctx.DefaultQuery("page", "1"), 10, 64) + page, err := strconv.Atoi(ctx.DefaultQuery("page", "1")) if err != nil { return 0, 0, errors.New("page must be a number") } - return page, max, nil + return page, pageSize, nil } func GetPageItems[T any](ctx *gin.Context, items []T) ([]T, error) { - page, max, err := GetPageAndMax(ctx) + page, max, err := GetPageAndPageSize(ctx) if err != nil { return nil, err } - return utils.GetPageItems(items, max, page), nil + return utils.GetPageItems(items, page, max), nil } func MovieList(ctx *gin.Context) { room := ctx.MustGet("room").(*op.Room) // user := ctx.MustGet("user").(*op.User) - page, max, err := GetPageAndMax(ctx) + page, max, err := GetPageAndPageSize(ctx) if err != nil { ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return @@ -97,7 +97,7 @@ func Movies(ctx *gin.Context) { room := ctx.MustGet("room").(*op.Room) // user := ctx.MustGet("user").(*op.User) - page, max, err := GetPageAndMax(ctx) + page, max, err := GetPageAndPageSize(ctx) if err != nil { ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return @@ -369,7 +369,8 @@ func ProxyMovie(ctx *gin.Context) { ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return } - room, err := op.GetRoomByID(uint(id)) + + room, err := op.LoadOrInitRoomByID(uint(id)) if err != nil { ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return diff --git a/server/handlers/room.go b/server/handlers/room.go index bfc6e6b..d68d542 100644 --- a/server/handlers/room.go +++ b/server/handlers/room.go @@ -7,13 +7,14 @@ import ( "strconv" "github.com/gin-gonic/gin" - "github.com/maruel/natural" "github.com/synctv-org/synctv/internal/db" dbModel "github.com/synctv-org/synctv/internal/model" "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/model" + "github.com/synctv-org/synctv/utils" "github.com/zijiren233/gencontainer/vec" + "gorm.io/gorm" ) var ( @@ -42,11 +43,7 @@ func CreateRoom(ctx *gin.Context) { return } - room, err := op.LoadRoom(r) - if err != nil { - ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) - return - } + room, _ := op.LoadOrInitRoom(r) token, err := middlewares.NewAuthRoomToken(user, room) if err != nil { @@ -61,83 +58,99 @@ func CreateRoom(ctx *gin.Context) { } func RoomList(ctx *gin.Context) { - r := op.GetAllRoomsWithoutHidden() - resp := vec.New[*model.RoomListResp](vec.WithCmpLess[*model.RoomListResp](func(v1, v2 *model.RoomListResp) bool { - return v1.PeopleNum < v2.PeopleNum - }), vec.WithCmpEqual[*model.RoomListResp](func(v1, v2 *model.RoomListResp) bool { - return v1.PeopleNum == v2.PeopleNum - })) - for _, v := range r { - resp.Push(&model.RoomListResp{ - RoomId: v.ID, - RoomName: v.Name, - PeopleNum: v.ClientNum(), - NeedPassword: v.NeedPassword(), - Creator: op.GetUserName(v.Room.CreatorID), - CreatedAt: v.Room.CreatedAt.UnixMilli(), - }) + page, pageSize, err := GetPageAndPageSize(ctx) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) + return } + resp := make([]*model.RoomListResp, 0, pageSize) - switch ctx.DefaultQuery("sort", "peopleNum") { + var desc = ctx.DefaultQuery("sort", "desc") == "desc" + + scopes := []func(db *gorm.DB) *gorm.DB{ + db.Paginate(page, pageSize), + } + + total := 0 + + switch ctx.DefaultQuery("order", "peopleNum") { case "peopleNum": - resp.SortStable() - case "creator": - resp.SortStableFunc(func(v1, v2 *model.RoomListResp) bool { - return natural.Less(v1.Creator, v2.Creator) - }, func(t1, t2 *model.RoomListResp) bool { - return t1.Creator == t2.Creator - }) + r := op.GetAllRoomsInCacheWithoutHidden() + rs := vec.New[*model.RoomListResp](vec.WithCmpLess[*model.RoomListResp](func(v1, v2 *model.RoomListResp) bool { + return v1.PeopleNum < v2.PeopleNum + }), vec.WithCmpEqual[*model.RoomListResp](func(v1, v2 *model.RoomListResp) bool { + return v1.PeopleNum == v2.PeopleNum + })) + for _, v := range r { + rs.Push(&model.RoomListResp{ + RoomId: v.ID, + RoomName: v.Name, + PeopleNum: v.ClientNum(), + NeedPassword: v.NeedPassword(), + Creator: op.GetUserName(v.Room.CreatorID), + CreatedAt: v.Room.CreatedAt.UnixMilli(), + }) + } + rs.SortStable() + if desc { + rs.Reverse() + } + total = rs.Len() + resp = utils.GetPageItems(rs.Slice(), page, pageSize) case "createdAt": - resp.SortStableFunc(func(v1, v2 *model.RoomListResp) bool { - return v1.CreatedAt < v2.CreatedAt - }, func(t1, t2 *model.RoomListResp) bool { - return t1.CreatedAt == t2.CreatedAt - }) + if desc { + scopes = append(scopes, db.OrderByCreatedAtDesc) + } else { + scopes = append(scopes, db.OrderByCreatedAtAsc) + } + resp = genRoomsResp(resp, scopes...) case "roomName": - resp.SortStableFunc(func(v1, v2 *model.RoomListResp) bool { - return natural.Less(v1.RoomName, v2.RoomName) - }, func(t1, t2 *model.RoomListResp) bool { - return t1.RoomName == t2.RoomName - }) + if desc { + scopes = append(scopes, db.OrderByDesc("name")) + } else { + scopes = append(scopes, db.OrderByAsc("name")) + } + resp = genRoomsResp(resp, scopes...) case "roomId": - resp.SortStableFunc(func(v1, v2 *model.RoomListResp) bool { - return v1.RoomId < v2.RoomId - }, func(t1, t2 *model.RoomListResp) bool { - return t1.RoomId == t2.RoomId - }) - case "needPassword": - resp.SortStableFunc(func(v1, v2 *model.RoomListResp) bool { - return v1.NeedPassword && !v2.NeedPassword - }, func(t1, t2 *model.RoomListResp) bool { - return t1.NeedPassword == t2.NeedPassword - }) - default: - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("sort must be peoplenum or roomid")) - return - } - - switch ctx.DefaultQuery("order", "desc") { - case "asc": - // do nothing - case "desc": - resp.Reverse() + if desc { + scopes = append(scopes, db.OrderByIDDesc) + } else { + scopes = append(scopes, db.OrderByIDAsc) + } + resp = genRoomsResp(resp, scopes...) default: - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("order must be asc or desc")) - return - } - - list, err := GetPageItems(ctx, resp.Slice()) - if err != nil { - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("not support order")) return } ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{ - "total": resp.Len(), - "list": list, + "total": total, + "list": resp, })) } +func genRoomsResp(resp []*model.RoomListResp, scopes ...func(db *gorm.DB) *gorm.DB) []*model.RoomListResp { + var clientNum int64 + for _, r := range db.GetAllRooms(scopes...) { + room, err := op.LoadRoomByID(r.ID) + if err != nil { + clientNum = 0 + } else { + clientNum = room.ClientNum() + } + + resp = append(resp, &model.RoomListResp{ + RoomId: r.ID, + RoomName: r.Name, + PeopleNum: clientNum, + NeedPassword: len(r.HashedPassword) != 0, + Creator: op.GetUserName(r.CreatorID), + CreatedAt: r.CreatedAt.UnixMilli(), + }) + } + return resp +} + func CheckRoom(ctx *gin.Context) { id, err := strconv.Atoi(ctx.Query("roomId")) if err != nil { @@ -145,14 +158,21 @@ func CheckRoom(ctx *gin.Context) { return } - r, err := op.GetRoomByID(uint(id)) + r, err := db.GetRoomByID(uint(id)) if err != nil { ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err)) return } + var peopleNum int64 + + room, err := op.LoadRoomByID(r.ID) + if err == nil { + peopleNum = room.ClientNum() + } + ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{ - "peopleNum": r.ClientNum(), + "peopleNum": peopleNum, "needPassword": r.NeedPassword(), })) } @@ -234,7 +254,7 @@ func RoomSetting(ctx *gin.Context) { // user := ctx.MustGet("user").(*op.User) ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{ - "hidden": room.Setting.Hidden, + "hidden": room.Settings.Hidden, "needPassword": room.NeedPassword(), })) } diff --git a/server/middlewares/auth.go b/server/middlewares/auth.go index 1a40317..c8c47cf 100644 --- a/server/middlewares/auth.go +++ b/server/middlewares/auth.go @@ -76,7 +76,7 @@ func AuthRoom(Authorization string) (*op.User, *op.Room, error) { return nil, nil, err } - r, err := op.GetRoomByID(claims.RoomId) + r, err := op.LoadOrInitRoomByID(claims.RoomId) if err != nil { return nil, nil, err } @@ -88,7 +88,7 @@ func AuthRoom(Authorization string) (*op.User, *op.Room, error) { } func AuthRoomWithPassword(u *op.User, roomId uint, password string) (*op.Room, error) { - r, err := op.GetRoomByID(roomId) + r, err := op.LoadOrInitRoomByID(roomId) if err != nil { return nil, err } diff --git a/server/oauth2/auth.go b/server/oauth2/auth.go index 154efc8..075589c 100644 --- a/server/oauth2/auth.go +++ b/server/oauth2/auth.go @@ -5,6 +5,7 @@ import ( "time" "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/provider" "github.com/synctv-org/synctv/server/middlewares" @@ -90,6 +91,8 @@ func OAuth2Callback(ctx *gin.Context) { return } + logrus.Info("asdasd") + RenderToken(ctx, "/web/", token) } diff --git a/utils/utils.go b/utils/utils.go index 3fcc188..6b852c0 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -34,16 +34,16 @@ func RandBytes(n int) []byte { return b } -func GetPageItems[T any](items []T, max, page int64) []T { - if max <= 0 || page <= 0 { +func GetPageItems[T any](items []T, page, pageSize int) []T { + if pageSize <= 0 || page <= 0 { return nil } - start := (page - 1) * max - l := int64(len(items)) + start := (page - 1) * pageSize + l := len(items) if start > l { start = l } - end := page * max + end := page * pageSize if end > l { end = l } diff --git a/utils/utils_test.go b/utils/utils_test.go index 75c4c6f..0a9c87d 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -9,9 +9,9 @@ import ( func TestGetPageItems(t *testing.T) { type args struct { - items []int - max int64 - page int64 + items []int + page int + pageSize int } tests := []struct { name string @@ -21,34 +21,34 @@ func TestGetPageItems(t *testing.T) { { name: "Test Case 1", args: args{ - items: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, - max: 5, - page: 1, + items: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + pageSize: 5, + page: 1, }, want: []int{1, 2, 3, 4, 5}, }, { name: "Test Case 2", args: args{ - items: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, - max: 5, - page: 2, + items: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + pageSize: 5, + page: 2, }, want: []int{6, 7, 8, 9, 10}, }, { name: "Test Case 3", args: args{ - items: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, - max: 5, - page: 3, + items: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + pageSize: 5, + page: 3, }, want: []int{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := utils.GetPageItems(tt.args.items, tt.args.max, tt.args.page); !reflect.DeepEqual(got, tt.want) { + if got := utils.GetPageItems(tt.args.items, tt.args.page, tt.args.pageSize); !reflect.DeepEqual(got, tt.want) { t.Errorf("GetPageItems() = %v, want %v", got, tt.want) } })