You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
synctv/room/user.go

220 lines
4.7 KiB
Go

package room
import (
"errors"
"math/rand"
"sync/atomic"
"time"
"github.com/gorilla/websocket"
"github.com/zijiren233/gencontainer/dllist"
"github.com/zijiren233/stream"
"golang.org/x/crypto/bcrypt"
)
type User struct {
room *Room
name string
password []byte
version uint64
admin bool
lastAct int64
}
var (
ErrUsernameEmpty = errors.New("user name is empty")
ErrUserPasswordEmpty = errors.New("user password is empty")
)
type UserConf func(*User)
func WithUserVersion(version uint64) UserConf {
return func(u *User) {
u.version = version
}
}
func WithUserAdmin(admin bool) UserConf {
return func(u *User) {
u.admin = admin
}
}
func NewUser(id string, password string, room *Room, conf ...UserConf) (*User, error) {
if id == "" {
return nil, ErrUsernameEmpty
}
if password == "" {
return nil, ErrUserPasswordEmpty
}
hashedPassword, err := bcrypt.GenerateFromPassword(stream.StringToBytes(password), bcrypt.DefaultCost)
if err != nil {
return nil, err
}
u := &User{
room: room,
name: id,
password: hashedPassword,
lastAct: time.Now().UnixMicro(),
}
for _, c := range conf {
c(u)
}
if u.version == 0 {
u.version = rand.New(rand.NewSource(time.Now().UnixNano())).Uint64()
}
return u, nil
}
func (u *User) LastAct() int64 {
return atomic.LoadInt64(&u.lastAct)
}
func (u *User) LastActTime() time.Time {
return time.UnixMicro(u.LastAct())
}
func (u *User) UpdateLastAct() int64 {
return atomic.SwapInt64(&u.lastAct, time.Now().UnixMicro())
}
func (u *User) Version() uint64 {
return atomic.LoadUint64(&u.version)
}
func (u *User) CheckVersion(version uint64) bool {
return u.Version() == version
}
func (u *User) SetVersion(version uint64) {
atomic.StoreUint64(&u.version, version)
}
func (u *User) updateVersion() uint64 {
return atomic.AddUint64(&u.version, 1)
}
func (u *User) CheckPassword(password string) bool {
err := bcrypt.CompareHashAndPassword(u.password, stream.StringToBytes(password))
return err == nil
}
func (u *User) SetPassword(password string) error {
hashedPassword, err := bcrypt.GenerateFromPassword(stream.StringToBytes(password), bcrypt.DefaultCost)
if err != nil {
return err
}
u.password = hashedPassword
u.updateVersion()
return nil
}
func (u *User) CloseHub() {
c, loaded := u.room.hub.clients.LoadAndDelete(u.name)
if loaded {
c.Close()
}
}
func (u *User) IsRoot() bool {
return u.room.rootUser == u
}
func (u *User) Name() string {
return u.name
}
func (u *User) Room() *Room {
return u.room
}
func (u *User) IsAdmin() bool {
return u.admin
}
func (u *User) SetAdmin(admin bool) {
u.admin = admin
}
func (u *User) NewMovie(url string, name string, type_ string, live bool, proxy bool, rtmpSource bool, headers map[string][]string, conf ...MovieConf) (*Movie, error) {
return u.NewMovieWithBaseMovie(BaseMovie{
Url: url,
Name: name,
Live: live,
Proxy: proxy,
RtmpSource: rtmpSource,
Type: type_,
Headers: headers,
}, conf...)
}
func (u *User) NewMovieWithBaseMovie(baseMovie BaseMovie, conf ...MovieConf) (*Movie, error) {
conf = append(conf, WithCreator(u))
return NewMovieWithBaseMovie(atomic.AddUint64(&u.room.mid, 1), baseMovie, conf...)
}
func (u *User) Movie(id uint64) (*MovieInfo, error) {
u.room.movies.lock.RLock()
defer u.room.movies.lock.RUnlock()
m, err := u.room.movies.GetMovie(id)
if err != nil {
return nil, err
}
movie := &MovieInfo{
Id: m.Id(),
Url: m.Url,
Name: m.Name,
Live: m.Live,
Proxy: m.Proxy,
RtmpSource: m.RtmpSource,
Type: m.Type,
Headers: m.Headers,
PullKey: m.PullKey,
CreateAt: m.CreateAt,
LastEditAt: m.LastEditAt,
Creator: m.Creator().Name(),
}
if movie.Proxy && u.name != movie.Creator {
m.Headers = nil
}
return movie, nil
}
func (u *User) MovieList() []*MovieInfo {
u.room.movies.lock.RLock()
defer u.room.movies.lock.RUnlock()
movies := make([]*MovieInfo, 0, u.room.movies.l.Len())
u.room.movies.range_(func(e *dllist.Element[*Movie]) bool {
m := &MovieInfo{
Id: e.Value.Id(),
Url: e.Value.Url,
Name: e.Value.Name,
Live: e.Value.Live,
Proxy: e.Value.Proxy,
RtmpSource: e.Value.RtmpSource,
Type: e.Value.Type,
Headers: e.Value.Headers,
PullKey: e.Value.PullKey,
CreateAt: e.Value.CreateAt,
LastEditAt: e.Value.LastEditAt,
Creator: e.Value.Creator().Name(),
}
if e.Value.Proxy && u.name != m.Creator {
m.Headers = nil
}
movies = append(movies, m)
return true
})
return movies
}
func (u *User) RegClient(conn *websocket.Conn) (*Client, error) {
return u.room.RegClient(u, conn)
}
func (u *User) Broadcast(msg Message, conf ...BroadcastConf) error {
return u.room.Broadcast(msg, append(conf, WithSender(u.name))...)
}