Feat: lazy init room

pull/21/head
zijiren233 2 years ago
parent e38eb7aa6a
commit e2a83a68c0

@ -32,7 +32,6 @@ var ServerCmd = &cobra.Command{
bootstrap.InitProvider, bootstrap.InitProvider,
bootstrap.InitOp, bootstrap.InitOp,
bootstrap.InitRtmp, bootstrap.InitRtmp,
bootstrap.InitRoom,
) )
if !flags.DisableUpdateCheck { if !flags.DisableUpdateCheck {
boot.Add(bootstrap.InitCheckUpdate) boot.Add(bootstrap.InitCheckUpdate)

@ -15,7 +15,6 @@ require (
github.com/google/uuid v1.3.1 github.com/google/uuid v1.3.1
github.com/gorilla/websocket v1.5.0 github.com/gorilla/websocket v1.5.0
github.com/json-iterator/go v1.1.12 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/mitchellh/go-homedir v1.1.0
github.com/natefinch/lumberjack v2.0.0+incompatible github.com/natefinch/lumberjack v2.0.0+incompatible
github.com/quic-go/quic-go v0.39.1 github.com/quic-go/quic-go v0.39.1
@ -66,6 +65,7 @@ require (
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/leodido/go-urn v1.2.4 // 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/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect

@ -286,8 +286,6 @@ gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= 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 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM=
modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak= 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 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=

@ -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
}

@ -32,7 +32,7 @@ func auth(ReqAppName, ReqChannelName string, IsPublisher bool) (*rtmps.Channel,
log.Errorf("rtmp: parse channel name to id error: %v", err) log.Errorf("rtmp: parse channel name to id error: %v", err)
return nil, err return nil, err
} }
r, err := op.GetRoomByID(uint(id)) r, err := op.LoadOrInitRoomByID(uint(id))
if err != nil { if err != nil {
log.Errorf("rtmp: get room by id error: %v", err) log.Errorf("rtmp: get room by id error: %v", err)
return nil, 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) log.Errorf("rtmp: parse channel name to id error: %v", err)
return nil, err return nil, err
} }
r, err := op.GetRoomByID(uint(id)) r, err := op.LoadOrInitRoomByID(uint(id))
if err != nil { if err != nil {
log.Errorf("rtmp: get room by id error: %v", err) log.Errorf("rtmp: get room by id error: %v", err)
return nil, err return nil, err

@ -45,3 +45,45 @@ func Close() {
return 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")
}

@ -13,7 +13,7 @@ type CreateRoomConfig func(r *model.Room)
func WithSetting(setting model.Setting) CreateRoomConfig { func WithSetting(setting model.Setting) CreateRoomConfig {
return func(r *model.Room) { return func(r *model.Room) {
r.Setting = setting r.Settings = setting
} }
} }
@ -157,29 +157,26 @@ func SetRoomHashedPassword(roomID uint, hashedPassword []byte) error {
return err return err
} }
func GetAllRooms() ([]*model.Room, error) { func GetAllRooms(scopes ...func(*gorm.DB) *gorm.DB) []*model.Room {
rooms := []*model.Room{} rooms := []*model.Room{}
err := db.Find(&rooms).Error db.Scopes(scopes...).Find(&rooms)
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) { return rooms
return rooms, nil
}
return rooms, err
} }
func GetAllRoomsAndCreator() ([]*model.Room, error) { func GetAllRoomsWithoutHidden(scopes ...func(*gorm.DB) *gorm.DB) []*model.Room {
rooms := []*model.Room{} rooms := []*model.Room{}
err := db.Preload("Creater").Find(&rooms).Error db.Preload("Setting").Where("setting.hidden = ?", false).Scopes(scopes...).Find(&rooms)
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) { return rooms
return rooms, nil
}
return rooms, err
} }
func GetAllRoomsByUserID(userID uint) ([]*model.Room, error) { func GetAllRoomsAndCreator(scopes ...func(*gorm.DB) *gorm.DB) []*model.Room {
rooms := []*model.Room{} rooms := []*model.Room{}
err := db.Where("creator_id = ?", userID).Find(&rooms).Error db.Preload("Creator").Scopes(scopes...).Find(&rooms)
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) { return rooms
return rooms, nil }
}
return rooms, err func GetAllRoomsByUserID(userID uint) []*model.Room {
rooms := []*model.Room{}
db.Where("creator_id = ?", userID).Find(&rooms)
return rooms
} }

@ -8,14 +8,18 @@ import (
type Room struct { type Room struct {
gorm.Model gorm.Model
Name string `gorm:"not null;uniqueIndex"` Name string `gorm:"not null;uniqueIndex"`
Setting Settings Setting `gorm:"foreignKey:RoomID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
CreatorID uint `gorm:"index"` CreatorID uint `gorm:"index"`
HashedPassword []byte HashedPassword []byte
GroupUserRelations []RoomUserRelation `gorm:"foreignKey:RoomID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` GroupUserRelations []RoomUserRelation `gorm:"foreignKey:RoomID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
Movies []Movie `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 { 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
} }

@ -1,5 +1,9 @@
package model package model
import "gorm.io/gorm"
type Setting struct { type Setting struct {
gorm.Model
RoomID uint `gorm:"uniqueIndex"`
Hidden bool Hidden bool
} }

@ -40,7 +40,7 @@ func (r *Room) LazyInit() (err error) {
r.hub = newHub(r.ID) r.hub = newHub(r.ID)
var ms []*model.Movie var ms []*model.Movie
ms, err = r.GetAllMoviesByRoomID() ms, err = r.GetAllMovies()
if err != nil { if err != nil {
log.Errorf("failed to get movies: %s", err.Error()) log.Errorf("failed to get movies: %s", err.Error())
return return
@ -331,7 +331,7 @@ func (r *Room) GetMoviesCount() (int, error) {
return GetMoviesCountByRoomID(r.ID) return GetMoviesCountByRoomID(r.ID)
} }
func (r *Room) GetAllMoviesByRoomID() ([]*model.Movie, error) { func (r *Room) GetAllMovies() ([]*model.Movie, error) {
ms, err := GetAllMoviesByRoomID(r.ID) ms, err := GetAllMoviesByRoomID(r.ID)
if err != nil { if err != nil {
return nil, err return nil, err

@ -3,7 +3,6 @@ package op
import ( import (
"errors" "errors"
"hash/crc32" "hash/crc32"
"sync/atomic"
"github.com/synctv-org/synctv/internal/db" "github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/model" "github.com/synctv-org/synctv/internal/model"
@ -17,35 +16,30 @@ func CreateRoom(name, password string, conf ...db.CreateRoomConfig) (*Room, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
return initRoom(r) return InitRoom(r)
} }
type RoomConf func(r *Room) func InitRoom(room *model.Room) (*Room, error) {
func WithVersion(version uint32) RoomConf {
return func(r *Room) {
atomic.StoreUint32(&r.version, version)
}
}
func initRoom(room *model.Room, conf ...RoomConf) (*Room, error) {
r := &Room{ r := &Room{
Room: *room, Room: *room,
version: crc32.ChecksumIEEE(room.HashedPassword), version: crc32.ChecksumIEEE(room.HashedPassword),
current: newCurrent(), current: newCurrent(),
} }
for _, c := range conf {
c(r)
}
r, loaded := roomCache.LoadOrStore(room.ID, r) r, loaded := roomCache.LoadOrStore(room.ID, r)
if loaded { if loaded {
return r, errors.New("room already exists") return r, errors.New("room already init")
} }
return r, nil return r, nil
} }
func LoadRoom(room *model.Room) (*Room, error) { func LoadOrInitRoom(room *model.Room) (r *Room, loaded bool) {
return initRoom(room) r = &Room{
Room: *room,
version: crc32.ChecksumIEEE(room.HashedPassword),
current: newCurrent(),
}
r, loaded = roomCache.LoadOrStore(room.ID, r)
return
} }
func DeleteRoom(room *Room) error { func DeleteRoom(room *Room) error {
@ -63,16 +57,25 @@ func DeleteRoomByID(id uint) error {
return db.DeleteRoomByID(r.ID) return db.DeleteRoomByID(r.ID)
} }
func GetRoomByID(id uint) (*Room, error) { func LoadRoomByID(id uint) (*Room, error) {
r2, ok := roomCache.Load(id) r2, ok := roomCache.Load(id)
if ok { if ok {
return r2, nil 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 { if err != nil {
return nil, err return nil, err
} }
return initRoom(r) r, _ = LoadOrInitRoom(room)
return r, nil
} }
func HasRoom(roomID uint) bool { func HasRoom(roomID uint) bool {
@ -96,14 +99,14 @@ func HasRoomByName(name string) bool {
} }
func SetRoomPassword(roomID uint, password string) error { func SetRoomPassword(roomID uint, password string) error {
r, err := GetRoomByID(roomID) r, err := LoadOrInitRoomByID(roomID)
if err != nil { if err != nil {
return err return err
} }
return r.SetPassword(password) return r.SetPassword(password)
} }
func GetAllRooms() []*Room { func GetAllRoomsInCache() []*Room {
rooms := make([]*Room, roomCache.Len()) rooms := make([]*Room, roomCache.Len())
roomCache.Range(func(key uint, value *Room) bool { roomCache.Range(func(key uint, value *Room) bool {
rooms = append(rooms, value) rooms = append(rooms, value)
@ -112,7 +115,7 @@ func GetAllRooms() []*Room {
return rooms return rooms
} }
func GetAllRoomsWithNoNeedPassword() []*Room { func GetAllRoomsInCacheWithNoNeedPassword() []*Room {
rooms := make([]*Room, roomCache.Len()) rooms := make([]*Room, roomCache.Len())
roomCache.Range(func(key uint, value *Room) bool { roomCache.Range(func(key uint, value *Room) bool {
if !value.NeedPassword() { if !value.NeedPassword() {
@ -123,10 +126,10 @@ func GetAllRoomsWithNoNeedPassword() []*Room {
return rooms return rooms
} }
func GetAllRoomsWithoutHidden() []*Room { func GetAllRoomsInCacheWithoutHidden() []*Room {
rooms := make([]*Room, 0, roomCache.Len()) rooms := make([]*Room, 0, roomCache.Len())
roomCache.Range(func(key uint, value *Room) bool { roomCache.Range(func(key uint, value *Room) bool {
if !value.Setting.Hidden { if !value.Settings.Hidden {
rooms = append(rooms, value) rooms = append(rooms, value)
} }
return true return true

@ -77,22 +77,20 @@ func CreateOrLoadUser(username string, p provider.OAuth2Provider, pid uint, conf
} }
func DeleteUserByID(userID uint) error { func DeleteUserByID(userID uint) error {
rs, err := db.GetAllRoomsByUserID(userID) err := db.DeleteUserByID(userID)
if err != nil {
return err
}
err = db.DeleteUserByID(userID)
if err != nil { if err != nil {
return err return err
} }
userCache.Remove(userID) userCache.Remove(userID)
for _, r := range rs { roomCache.Range(func(key uint, value *Room) bool {
r2, loaded := roomCache.LoadAndDelete(r.ID) if value.CreatorID == userID {
if loaded { roomCache.Delete(key)
r2.close() value.close()
} }
} return true
})
return nil return nil
} }

@ -3,7 +3,6 @@ package provider
import ( import (
"context" "context"
"net/http" "net/http"
"time"
json "github.com/json-iterator/go" json "github.com/json-iterator/go"
"golang.org/x/oauth2" "golang.org/x/oauth2"
@ -59,51 +58,8 @@ func (p *GithubProvider) GetUserInfo(ctx context.Context, config *oauth2.Config,
} }
type githubUserInfo struct { type githubUserInfo struct {
Login string `json:"login"` Login string `json:"login"`
ID uint `json:"id"` 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"`
} }
func init() { func init() {

@ -24,32 +24,32 @@ import (
"github.com/zijiren233/livelib/protocol/httpflv" "github.com/zijiren233/livelib/protocol/httpflv"
) )
func GetPageAndMax(ctx *gin.Context) (int64, int64, error) { func GetPageAndPageSize(ctx *gin.Context) (int, int, error) {
max, err := strconv.ParseInt(ctx.DefaultQuery("max", "10"), 10, 64) pageSize, err := strconv.Atoi(ctx.DefaultQuery("max", "10"))
if err != nil { if err != nil {
return 0, 0, errors.New("max must be a number") 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 { if err != nil {
return 0, 0, errors.New("page must be a number") 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) { func GetPageItems[T any](ctx *gin.Context, items []T) ([]T, error) {
page, max, err := GetPageAndMax(ctx) page, max, err := GetPageAndPageSize(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return utils.GetPageItems(items, max, page), nil return utils.GetPageItems(items, page, max), nil
} }
func MovieList(ctx *gin.Context) { func MovieList(ctx *gin.Context) {
room := ctx.MustGet("room").(*op.Room) room := ctx.MustGet("room").(*op.Room)
// user := ctx.MustGet("user").(*op.User) // user := ctx.MustGet("user").(*op.User)
page, max, err := GetPageAndMax(ctx) page, max, err := GetPageAndPageSize(ctx)
if err != nil { if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return return
@ -97,7 +97,7 @@ func Movies(ctx *gin.Context) {
room := ctx.MustGet("room").(*op.Room) room := ctx.MustGet("room").(*op.Room)
// user := ctx.MustGet("user").(*op.User) // user := ctx.MustGet("user").(*op.User)
page, max, err := GetPageAndMax(ctx) page, max, err := GetPageAndPageSize(ctx)
if err != nil { if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return return
@ -369,7 +369,8 @@ func ProxyMovie(ctx *gin.Context) {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return return
} }
room, err := op.GetRoomByID(uint(id))
room, err := op.LoadOrInitRoomByID(uint(id))
if err != nil { if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return return

@ -7,13 +7,14 @@ import (
"strconv" "strconv"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/maruel/natural"
"github.com/synctv-org/synctv/internal/db" "github.com/synctv-org/synctv/internal/db"
dbModel "github.com/synctv-org/synctv/internal/model" dbModel "github.com/synctv-org/synctv/internal/model"
"github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/op"
"github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/middlewares"
"github.com/synctv-org/synctv/server/model" "github.com/synctv-org/synctv/server/model"
"github.com/synctv-org/synctv/utils"
"github.com/zijiren233/gencontainer/vec" "github.com/zijiren233/gencontainer/vec"
"gorm.io/gorm"
) )
var ( var (
@ -42,11 +43,7 @@ func CreateRoom(ctx *gin.Context) {
return return
} }
room, err := op.LoadRoom(r) room, _ := op.LoadOrInitRoom(r)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
token, err := middlewares.NewAuthRoomToken(user, room) token, err := middlewares.NewAuthRoomToken(user, room)
if err != nil { if err != nil {
@ -61,83 +58,99 @@ func CreateRoom(ctx *gin.Context) {
} }
func RoomList(ctx *gin.Context) { func RoomList(ctx *gin.Context) {
r := op.GetAllRoomsWithoutHidden() page, pageSize, err := GetPageAndPageSize(ctx)
resp := vec.New[*model.RoomListResp](vec.WithCmpLess[*model.RoomListResp](func(v1, v2 *model.RoomListResp) bool { if err != nil {
return v1.PeopleNum < v2.PeopleNum ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
}), vec.WithCmpEqual[*model.RoomListResp](func(v1, v2 *model.RoomListResp) bool { return
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(),
})
} }
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": case "peopleNum":
resp.SortStable() r := op.GetAllRoomsInCacheWithoutHidden()
case "creator": rs := vec.New[*model.RoomListResp](vec.WithCmpLess[*model.RoomListResp](func(v1, v2 *model.RoomListResp) bool {
resp.SortStableFunc(func(v1, v2 *model.RoomListResp) bool { return v1.PeopleNum < v2.PeopleNum
return natural.Less(v1.Creator, v2.Creator) }), vec.WithCmpEqual[*model.RoomListResp](func(v1, v2 *model.RoomListResp) bool {
}, func(t1, t2 *model.RoomListResp) bool { return v1.PeopleNum == v2.PeopleNum
return t1.Creator == t2.Creator }))
}) 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": case "createdAt":
resp.SortStableFunc(func(v1, v2 *model.RoomListResp) bool { if desc {
return v1.CreatedAt < v2.CreatedAt scopes = append(scopes, db.OrderByCreatedAtDesc)
}, func(t1, t2 *model.RoomListResp) bool { } else {
return t1.CreatedAt == t2.CreatedAt scopes = append(scopes, db.OrderByCreatedAtAsc)
}) }
resp = genRoomsResp(resp, scopes...)
case "roomName": case "roomName":
resp.SortStableFunc(func(v1, v2 *model.RoomListResp) bool { if desc {
return natural.Less(v1.RoomName, v2.RoomName) scopes = append(scopes, db.OrderByDesc("name"))
}, func(t1, t2 *model.RoomListResp) bool { } else {
return t1.RoomName == t2.RoomName scopes = append(scopes, db.OrderByAsc("name"))
}) }
resp = genRoomsResp(resp, scopes...)
case "roomId": case "roomId":
resp.SortStableFunc(func(v1, v2 *model.RoomListResp) bool { if desc {
return v1.RoomId < v2.RoomId scopes = append(scopes, db.OrderByIDDesc)
}, func(t1, t2 *model.RoomListResp) bool { } else {
return t1.RoomId == t2.RoomId scopes = append(scopes, db.OrderByIDAsc)
}) }
case "needPassword": resp = genRoomsResp(resp, scopes...)
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()
default: default:
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("order must be asc or desc")) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("not support order"))
return
}
list, err := GetPageItems(ctx, resp.Slice())
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return return
} }
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{ ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"total": resp.Len(), "total": total,
"list": list, "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) { func CheckRoom(ctx *gin.Context) {
id, err := strconv.Atoi(ctx.Query("roomId")) id, err := strconv.Atoi(ctx.Query("roomId"))
if err != nil { if err != nil {
@ -145,14 +158,21 @@ func CheckRoom(ctx *gin.Context) {
return return
} }
r, err := op.GetRoomByID(uint(id)) r, err := db.GetRoomByID(uint(id))
if err != nil { if err != nil {
ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err)) ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err))
return return
} }
var peopleNum int64
room, err := op.LoadRoomByID(r.ID)
if err == nil {
peopleNum = room.ClientNum()
}
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{ ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"peopleNum": r.ClientNum(), "peopleNum": peopleNum,
"needPassword": r.NeedPassword(), "needPassword": r.NeedPassword(),
})) }))
} }
@ -234,7 +254,7 @@ func RoomSetting(ctx *gin.Context) {
// user := ctx.MustGet("user").(*op.User) // user := ctx.MustGet("user").(*op.User)
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{ ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"hidden": room.Setting.Hidden, "hidden": room.Settings.Hidden,
"needPassword": room.NeedPassword(), "needPassword": room.NeedPassword(),
})) }))
} }

@ -76,7 +76,7 @@ func AuthRoom(Authorization string) (*op.User, *op.Room, error) {
return nil, nil, err return nil, nil, err
} }
r, err := op.GetRoomByID(claims.RoomId) r, err := op.LoadOrInitRoomByID(claims.RoomId)
if err != nil { if err != nil {
return nil, nil, err 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) { 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 { if err != nil {
return nil, err return nil, err
} }

@ -5,6 +5,7 @@ import (
"time" "time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/op"
"github.com/synctv-org/synctv/internal/provider" "github.com/synctv-org/synctv/internal/provider"
"github.com/synctv-org/synctv/server/middlewares" "github.com/synctv-org/synctv/server/middlewares"
@ -90,6 +91,8 @@ func OAuth2Callback(ctx *gin.Context) {
return return
} }
logrus.Info("asdasd")
RenderToken(ctx, "/web/", token) RenderToken(ctx, "/web/", token)
} }

@ -34,16 +34,16 @@ func RandBytes(n int) []byte {
return b return b
} }
func GetPageItems[T any](items []T, max, page int64) []T { func GetPageItems[T any](items []T, page, pageSize int) []T {
if max <= 0 || page <= 0 { if pageSize <= 0 || page <= 0 {
return nil return nil
} }
start := (page - 1) * max start := (page - 1) * pageSize
l := int64(len(items)) l := len(items)
if start > l { if start > l {
start = l start = l
} }
end := page * max end := page * pageSize
if end > l { if end > l {
end = l end = l
} }

@ -9,9 +9,9 @@ import (
func TestGetPageItems(t *testing.T) { func TestGetPageItems(t *testing.T) {
type args struct { type args struct {
items []int items []int
max int64 page int
page int64 pageSize int
} }
tests := []struct { tests := []struct {
name string name string
@ -21,34 +21,34 @@ func TestGetPageItems(t *testing.T) {
{ {
name: "Test Case 1", name: "Test Case 1",
args: args{ args: args{
items: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, items: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
max: 5, pageSize: 5,
page: 1, page: 1,
}, },
want: []int{1, 2, 3, 4, 5}, want: []int{1, 2, 3, 4, 5},
}, },
{ {
name: "Test Case 2", name: "Test Case 2",
args: args{ args: args{
items: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, items: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
max: 5, pageSize: 5,
page: 2, page: 2,
}, },
want: []int{6, 7, 8, 9, 10}, want: []int{6, 7, 8, 9, 10},
}, },
{ {
name: "Test Case 3", name: "Test Case 3",
args: args{ args: args{
items: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, items: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
max: 5, pageSize: 5,
page: 3, page: 3,
}, },
want: []int{}, want: []int{},
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { 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) t.Errorf("GetPageItems() = %v, want %v", got, tt.want)
} }
}) })

Loading…
Cancel
Save