Feat: use rooms middleware

pull/21/head
zijiren233 1 year ago
parent e40075268e
commit 6d7b7417dd

@ -0,0 +1,97 @@
package room
import (
"errors"
"time"
"github.com/zijiren233/gencontainer/rwmap"
rtmps "github.com/zijiren233/livelib/server"
)
const (
roomMaxInactivityTime = time.Hour * 12
)
var (
ErrRoomNotFound = errors.New("room not found")
ErrUserNotFound = errors.New("user not found")
ErrRoomAlreadyExist = errors.New("room already exist")
)
type Rooms struct {
rooms rwmap.RWMap[string, *Room]
}
func NewRooms() *Rooms {
return &Rooms{}
}
func (rs *Rooms) List() (rooms []*Room) {
rooms = make([]*Room, 0, rs.rooms.Len())
rs.rooms.Range(func(id string, r *Room) bool {
rooms = append(rooms, r)
return true
})
return
}
func (rs *Rooms) ListNonHidden() (rooms []*Room) {
rooms = make([]*Room, 0, rs.rooms.Len())
rs.rooms.Range(func(id string, r *Room) bool {
if !r.Hidden() {
rooms = append(rooms, r)
}
return true
})
return
}
func (rs *Rooms) ListHidden() (rooms []*Room) {
rooms = make([]*Room, 0, rs.rooms.Len())
rs.rooms.Range(func(id string, r *Room) bool {
if r.Hidden() {
rooms = append(rooms, r)
}
return true
})
return
}
func (rs *Rooms) HasRoom(id string) bool {
_, ok := rs.rooms.Load(id)
return ok
}
func (rs *Rooms) GetRoom(id string) (*Room, error) {
if id == "" {
return nil, ErrRoomIDEmpty
}
r, ok := rs.rooms.Load(id)
if !ok {
return nil, ErrRoomNotFound
}
return r, nil
}
func (rs *Rooms) CreateRoom(id string, password string, s *rtmps.Server, conf ...RoomConf) (*Room, error) {
r, err := NewRoom(id, password, s, conf...)
if err != nil {
return nil, err
}
r, loaded := rs.rooms.LoadOrStore(r.ID(), r)
if loaded {
return nil, ErrRoomAlreadyExist
}
return r, nil
}
func (rs *Rooms) DelRoom(id string) error {
if id == "" {
return ErrRoomIDEmpty
}
r, ok := rs.rooms.LoadAndDelete(id)
if !ok {
return ErrRoomNotFound
}
return r.Close()
}

@ -44,7 +44,8 @@ func GetPageItems[T any](ctx *gin.Context, items []T) ([]T, error) {
}
func MovieList(ctx *gin.Context) {
user, err := AuthRoom(ctx.GetHeader("Authorization"))
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
@ -66,7 +67,8 @@ func MovieList(ctx *gin.Context) {
}
func CurrentMovie(ctx *gin.Context) {
user, err := AuthRoom(ctx.GetHeader("Authorization"))
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
@ -78,7 +80,8 @@ func CurrentMovie(ctx *gin.Context) {
}
func Movies(ctx *gin.Context) {
user, err := AuthRoom(ctx.GetHeader("Authorization"))
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
@ -101,7 +104,8 @@ func Movies(ctx *gin.Context) {
type PushMovieReq = room.BaseMovieInfo
func PushMovie(ctx *gin.Context) {
user, err := AuthRoom(ctx.GetHeader("Authorization"))
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
@ -235,7 +239,8 @@ func PushMovie(ctx *gin.Context) {
}
func NewPublishKey(ctx *gin.Context) {
user, err := AuthRoom(ctx.GetHeader("Authorization"))
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatus(http.StatusUnauthorized)
return
@ -295,7 +300,8 @@ type EditMovieReq struct {
}
func EditMovie(ctx *gin.Context) {
user, err := AuthRoom(ctx.GetHeader("Authorization"))
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
@ -338,7 +344,8 @@ type IdsReq struct {
}
func DelMovie(ctx *gin.Context) {
user, err := AuthRoom(ctx.GetHeader("Authorization"))
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
@ -369,7 +376,8 @@ func DelMovie(ctx *gin.Context) {
}
func ClearMovies(ctx *gin.Context) {
user, err := AuthRoom(ctx.GetHeader("Authorization"))
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
@ -399,7 +407,8 @@ type SwapMovieReq struct {
}
func SwapMovie(ctx *gin.Context) {
user, err := AuthRoom(ctx.GetHeader("Authorization"))
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
@ -434,7 +443,8 @@ type IdReq struct {
}
func ChangeCurrentMovie(ctx *gin.Context) {
user, err := AuthRoom(ctx.GetHeader("Authorization"))
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
@ -502,12 +512,13 @@ func NewRtmpAuthorization(channelName string) (string, error) {
const UserAgent = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.40`
func ProxyMovie(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
roomid := ctx.Query("roomid")
if roomid == "" {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorStringResp("roomid is empty"))
return
}
room, err := Rooms.GetRoom(roomid)
room, err := rooms.GetRoom(roomid)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
}
@ -563,11 +574,12 @@ func (e FormatErrNotSupportFileType) Error() string {
}
func JoinLive(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
if !conf.Conf.Proxy.LiveProxy && !conf.Conf.Rtmp.Enable {
ctx.AbortWithStatusJSON(http.StatusForbidden, NewApiErrorStringResp("live proxy and rtmp source is not enabled"))
return
}
user, err := AuthRoom(ctx.GetHeader("Authorization"))
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return

@ -42,7 +42,7 @@ type AuthClaims struct {
jwt.RegisteredClaims
}
func AuthRoom(Authorization string) (*room.User, error) {
func AuthRoom(Authorization string, rooms *room.Rooms) (*room.User, error) {
t, err := jwt.ParseWithClaims(strings.TrimPrefix(Authorization, `Bearer `), &AuthClaims{}, func(token *jwt.Token) (any, error) {
return stream.StringToBytes(conf.Conf.Jwt.Secret), nil
})
@ -51,7 +51,7 @@ func AuthRoom(Authorization string) (*room.User, error) {
}
claims := t.Claims.(*AuthClaims)
r, err := Rooms.GetRoom(claims.RoomID)
r, err := rooms.GetRoom(claims.RoomID)
if err != nil {
return nil, err
}
@ -62,7 +62,7 @@ func AuthRoom(Authorization string) (*room.User, error) {
user, err := r.GetUser(claims.Username)
if err != nil {
return nil, ErrUserNotFound
return nil, err
}
if !user.CheckVersion(claims.UserVersion) {
@ -72,8 +72,8 @@ func AuthRoom(Authorization string) (*room.User, error) {
return user, nil
}
func authWithPassword(roomid, password, username, userPassword string) (*room.User, error) {
r, err := Rooms.GetRoom(roomid)
func authWithPassword(rooms *room.Rooms, roomid, password, username, userPassword string) (*room.User, error) {
r, err := rooms.GetRoom(roomid)
if err != nil {
return nil, err
}
@ -82,7 +82,7 @@ func authWithPassword(roomid, password, username, userPassword string) (*room.Us
}
user, err := r.GetUser(username)
if err != nil {
return nil, ErrUserNotFound
return nil, err
}
if !user.CheckPassword(userPassword) {
return nil, ErrAuthFailed
@ -90,8 +90,8 @@ func authWithPassword(roomid, password, username, userPassword string) (*room.Us
return user, nil
}
func authOrNewWithPassword(roomid, password, username, userPassword string, conf ...room.UserConf) (*room.User, error) {
r, err := Rooms.GetRoom(roomid)
func authOrNewWithPassword(rooms *room.Rooms, roomid, password, username, userPassword string, conf ...room.UserConf) (*room.User, error) {
r, err := rooms.GetRoom(roomid)
if err != nil {
return nil, err
}
@ -132,6 +132,7 @@ type CreateRoomReq struct {
func NewCreateRoomHandler(s *rtmps.Server) gin.HandlerFunc {
return func(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
req := new(CreateRoomReq)
if err := json.NewDecoder(ctx.Request.Body).Decode(req); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
@ -144,7 +145,7 @@ func NewCreateRoomHandler(s *rtmps.Server) gin.HandlerFunc {
return
}
r, err := Rooms.CreateRoom(req.RoomID, req.Password, s,
r, err := rooms.CreateRoom(req.RoomID, req.Password, s,
room.WithHidden(req.Hidden),
room.WithRootUser(user),
)
@ -207,7 +208,8 @@ type RoomListResp struct {
}
func RoomList(ctx *gin.Context) {
r := Rooms.ListNonHidden()
rooms := ctx.Value("rooms").(*room.Rooms)
r := rooms.ListNonHidden()
resp := vec.New[*RoomListResp](vec.WithCmpLess[*RoomListResp](func(v1, v2 *RoomListResp) bool {
return v1.PeopleNum < v2.PeopleNum
}), vec.WithCmpEqual[*RoomListResp](func(v1, v2 *RoomListResp) bool {
@ -278,7 +280,8 @@ func RoomList(ctx *gin.Context) {
}
func CheckRoom(ctx *gin.Context) {
r, err := Rooms.GetRoom(ctx.Query("roomId"))
rooms := ctx.Value("rooms").(*room.Rooms)
r, err := rooms.GetRoom(ctx.Query("roomId"))
if err != nil {
ctx.AbortWithStatusJSON(http.StatusNotFound, NewApiErrorResp(err))
return
@ -291,7 +294,8 @@ func CheckRoom(ctx *gin.Context) {
}
func CheckUser(ctx *gin.Context) {
r, err := Rooms.GetRoom(ctx.Query("roomId"))
rooms := ctx.Value("rooms").(*room.Rooms)
r, err := rooms.GetRoom(ctx.Query("roomId"))
if err != nil {
ctx.AbortWithStatusJSON(http.StatusNotFound, NewApiErrorResp(err))
return
@ -318,6 +322,7 @@ type LoginRoomReq struct {
}
func LoginRoom(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
req := new(LoginRoomReq)
if err := json.NewDecoder(ctx.Request.Body).Decode(req); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, NewApiErrorResp(err))
@ -334,13 +339,13 @@ func LoginRoom(ctx *gin.Context) {
user *room.User
)
if autoNew {
user, err = authOrNewWithPassword(req.RoomID, req.Password, req.Username, req.UserPassword)
user, err = authOrNewWithPassword(rooms, req.RoomID, req.Password, req.Username, req.UserPassword)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
}
} else {
user, err = authWithPassword(req.RoomID, req.Password, req.Username, req.UserPassword)
user, err = authWithPassword(rooms, req.RoomID, req.Password, req.Username, req.UserPassword)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
@ -359,7 +364,8 @@ func LoginRoom(ctx *gin.Context) {
}
func DeleteRoom(ctx *gin.Context) {
user, err := AuthRoom(ctx.GetHeader("Authorization"))
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
@ -370,7 +376,7 @@ func DeleteRoom(ctx *gin.Context) {
return
}
err = Rooms.DelRoom(user.Room().ID())
err = rooms.DelRoom(user.Room().ID())
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorResp(err))
return
@ -384,7 +390,8 @@ type SetPasswordReq struct {
}
func SetPassword(ctx *gin.Context) {
user, err := AuthRoom(ctx.GetHeader("Authorization"))
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
@ -419,7 +426,8 @@ type UsernameReq struct {
}
func AddAdmin(ctx *gin.Context) {
user, err := AuthRoom(ctx.GetHeader("Authorization"))
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
@ -448,7 +456,8 @@ func AddAdmin(ctx *gin.Context) {
}
func DelAdmin(ctx *gin.Context) {
user, err := AuthRoom(ctx.GetHeader("Authorization"))
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
@ -477,7 +486,8 @@ func DelAdmin(ctx *gin.Context) {
}
func CloseRoom(ctx *gin.Context) {
user, err := AuthRoom(ctx.GetHeader("Authorization"))
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
@ -488,7 +498,7 @@ func CloseRoom(ctx *gin.Context) {
return
}
err = Rooms.DelRoom(user.Room().ID())
err = rooms.DelRoom(user.Room().ID())
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, NewApiErrorResp(err))
return

@ -5,10 +5,12 @@ import (
"github.com/gin-gonic/gin"
json "github.com/json-iterator/go"
"github.com/synctv-org/synctv/room"
)
func Me(ctx *gin.Context) {
user, err := AuthRoom(ctx.GetHeader("Authorization"))
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return
@ -23,7 +25,8 @@ func Me(ctx *gin.Context) {
}
func SetUserPassword(ctx *gin.Context) {
user, err := AuthRoom(ctx.GetHeader("Authorization"))
rooms := ctx.Value("rooms").(*room.Rooms)
user, err := AuthRoom(ctx.GetHeader("Authorization"), rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return

@ -8,13 +8,12 @@ import (
log "github.com/sirupsen/logrus"
"github.com/synctv-org/synctv/internal/conf"
"github.com/synctv-org/synctv/public"
"github.com/synctv-org/synctv/room"
"github.com/synctv-org/synctv/utils"
rtmps "github.com/zijiren233/livelib/server"
)
func Init(e *gin.Engine, s *rtmps.Server) {
initOnce()
func Init(e *gin.Engine, s *rtmps.Server, r *room.Rooms) {
{
s.SetParseChannelFunc(func(ReqAppName, ReqChannelName string, IsPublisher bool) (TrueAppName string, TrueChannel string, err error) {
if IsPublisher {
@ -23,7 +22,7 @@ func Init(e *gin.Engine, s *rtmps.Server) {
log.Errorf("rtmp: publish auth to %s error: %v", ReqAppName, err)
return "", "", err
}
if !Rooms.HasRoom(ReqAppName) {
if !r.HasRoom(ReqAppName) {
log.Infof("rtmp: publish to %s/%s error: %s", ReqAppName, channelName, fmt.Sprintf("room %s not exist", ReqAppName))
return "", "", fmt.Errorf("room %s not exist", ReqAppName)
}

@ -1,110 +0,0 @@
package handlers
import (
"errors"
"sync"
"time"
"github.com/synctv-org/synctv/room"
"github.com/zijiren233/gencontainer/rwmap"
rtmps "github.com/zijiren233/livelib/server"
)
const (
roomMaxInactivityTime = time.Hour * 12
)
var (
Rooms *rooms
once = sync.Once{}
initOnce = func() {
once.Do(func() {
Rooms = newRooms()
})
}
)
var (
ErrRoomIDEmpty = errors.New("roomid is empty")
ErrRoomNotFound = errors.New("room not found")
ErrUserNotFound = errors.New("user not found")
ErrRoomAlreadyExist = errors.New("room already exist")
)
type rooms struct {
rooms rwmap.RWMap[string, *room.Room]
}
func newRooms() *rooms {
return &rooms{}
}
func (rs *rooms) List() (rooms []*room.Room) {
rooms = make([]*room.Room, 0, rs.rooms.Len())
rs.rooms.Range(func(id string, r *room.Room) bool {
rooms = append(rooms, r)
return true
})
return
}
func (rs *rooms) ListNonHidden() (rooms []*room.Room) {
rooms = make([]*room.Room, 0, rs.rooms.Len())
rs.rooms.Range(func(id string, r *room.Room) bool {
if !r.Hidden() {
rooms = append(rooms, r)
}
return true
})
return
}
func (rs *rooms) ListHidden() (rooms []*room.Room) {
rooms = make([]*room.Room, 0, rs.rooms.Len())
rs.rooms.Range(func(id string, r *room.Room) bool {
if r.Hidden() {
rooms = append(rooms, r)
}
return true
})
return
}
func (rs *rooms) HasRoom(id string) bool {
_, ok := rs.rooms.Load(id)
return ok
}
func (rs *rooms) GetRoom(id string) (*room.Room, error) {
if id == "" {
return nil, ErrRoomIDEmpty
}
r, ok := rs.rooms.Load(id)
if !ok {
return nil, ErrRoomNotFound
}
return r, nil
}
func (rs *rooms) CreateRoom(id string, password string, s *rtmps.Server, conf ...room.RoomConf) (*room.Room, error) {
r, err := room.NewRoom(id, password, s, conf...)
if err != nil {
return nil, err
}
r, loaded := rs.rooms.LoadOrStore(r.ID(), r)
if loaded {
return nil, ErrRoomAlreadyExist
}
return r, nil
}
func (rs *rooms) DelRoom(id string) error {
if id == "" {
return ErrRoomIDEmpty
}
r, ok := rs.rooms.LoadAndDelete(id)
if !ok {
return ErrRoomNotFound
}
return r.Close()
}

@ -20,8 +20,9 @@ const maxInterval = 10
func NewWebSocketHandler(wss *utils.WebSocket) gin.HandlerFunc {
return func(ctx *gin.Context) {
rooms := ctx.Value("rooms").(*room.Rooms)
token := ctx.GetHeader("Sec-WebSocket-Protocol")
user, err := AuthRoom(token)
user, err := AuthRoom(token, rooms)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, NewApiErrorResp(err))
return

@ -4,13 +4,15 @@ import (
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"github.com/synctv-org/synctv/internal/conf"
"github.com/synctv-org/synctv/room"
)
func Init(e *gin.Engine) {
func Init(e *gin.Engine, r *room.Rooms) {
w := log.StandardLogger().Writer()
e.
Use(gin.LoggerWithWriter(w), gin.RecoveryWithWriter(w)).
Use(NewCors())
Use(NewCors()).
Use(NewRooms(r))
if conf.Conf.Server.Quic && conf.Conf.Server.CertPath != "" && conf.Conf.Server.KeyPath != "" {
e.Use(NewQuic())
}

@ -0,0 +1,13 @@
package middlewares
import (
"github.com/gin-gonic/gin"
"github.com/synctv-org/synctv/room"
)
func NewRooms(r *room.Rooms) gin.HandlerFunc {
return func(ctx *gin.Context) {
ctx.Set("rooms", r)
ctx.Next()
}
}

@ -3,14 +3,16 @@ package server
import (
"github.com/gin-gonic/gin"
"github.com/synctv-org/synctv/internal/conf"
"github.com/synctv-org/synctv/room"
"github.com/synctv-org/synctv/server/handlers"
"github.com/synctv-org/synctv/server/middlewares"
rtmps "github.com/zijiren233/livelib/server"
)
func Init(e *gin.Engine, s *rtmps.Server) {
middlewares.Init(e)
handlers.Init(e, s)
r := room.NewRooms()
middlewares.Init(e, r)
handlers.Init(e, s, r)
}
func NewAndInit() (e *gin.Engine, s *rtmps.Server) {

Loading…
Cancel
Save