Feat: pending user

pull/24/head
zijiren233 1 year ago
parent d19d8bd5c3
commit b596d9ea03

@ -283,9 +283,9 @@ func GetAllUserWithRoleUser(role model.Role, scopes ...func(*gorm.DB) *gorm.DB)
return users
}
func GetAllUserWithRoleUserCount(scopes ...func(*gorm.DB) *gorm.DB) int64 {
func GetAllUserCountWithRole(role model.Role, scopes ...func(*gorm.DB) *gorm.DB) int64 {
var count int64
db.Model(&model.User{}).Where("role = ?", model.RoleUser).Scopes(scopes...).Count(&count)
db.Model(&model.User{}).Where("role = ?", role).Scopes(scopes...).Count(&count)
return count
}

@ -8,37 +8,23 @@ import (
"gorm.io/gorm"
)
type Role uint8
type Role string
const (
RoleBanned Role = iota
RoleUser
RoleAdmin
RoleRoot
RoleBanned Role = "banned"
RolePending Role = "pending"
RoleUser Role = "user"
RoleAdmin Role = "admin"
RoleRoot Role = "root"
)
func (r Role) String() string {
switch r {
case RoleBanned:
return "banned"
case RoleUser:
return "user"
case RoleAdmin:
return "admin"
case RoleRoot:
return "root"
default:
return "unknown"
}
}
type User struct {
ID uint `gorm:"primarykey"`
CreatedAt time.Time
UpdatedAt time.Time
Providers []UserProvider `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
Username string `gorm:"not null;uniqueIndex"`
Role Role `gorm:"not null"`
Role Role `gorm:"not null;default:user"`
GroupUserRelations []RoomUserRelation `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
Rooms []Room `gorm:"foreignKey:CreatorID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
Movies []Movie `gorm:"foreignKey:CreatorID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL"`

@ -34,6 +34,10 @@ func (u *User) IsBanned() bool {
return u.Role == model.RoleBanned
}
func (u *User) IsPending() bool {
return u.Role == model.RolePending
}
func (u *User) HasPermission(roomID uint, permission model.Permission) bool {
if u.Role >= model.RoleAdmin {
return true

@ -8,4 +8,5 @@ var (
var (
DisableUserSignup = newBoolSetting("disable_user_signup", false, model.SettingGroupUser)
SignupNeedReview = newBoolSetting("signup_need_review", false, model.SettingGroupUser)
)

@ -1,11 +1,13 @@
package handlers
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
"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/internal/settings"
"github.com/synctv-org/synctv/server/model"
"gorm.io/gorm"
@ -104,7 +106,7 @@ func Users(ctx *gin.Context) {
}
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"total": db.GetAllUserWithRoleUserCount(scopes...),
"total": db.GetAllUserCountWithRole(dbModel.RoleUser, scopes...),
"list": genUserListResp(dbModel.RoleUser, append(scopes, db.Paginate(page, pageSize))...),
}))
}
@ -122,3 +124,102 @@ func genUserListResp(role dbModel.Role, scopes ...func(db *gorm.DB) *gorm.DB) []
}
return resp
}
func PendingUsers(ctx *gin.Context) {
// user := ctx.MustGet("user").(*op.User)
order := ctx.Query("order")
if order == "" {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("order is required"))
return
}
page, pageSize, err := GetPageAndPageSize(ctx)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
var desc = ctx.DefaultQuery("sort", "desc") == "desc"
scopes := []func(db *gorm.DB) *gorm.DB{}
if keyword := ctx.Query("keyword"); keyword != "" {
scopes = append(scopes, db.WhereUserNameLike(keyword))
}
switch order {
case "createdAt":
if desc {
scopes = append(scopes, db.OrderByCreatedAtDesc)
} else {
scopes = append(scopes, db.OrderByCreatedAtAsc)
}
case "name":
if desc {
scopes = append(scopes, db.OrderByDesc("username"))
} else {
scopes = append(scopes, db.OrderByAsc("username"))
}
case "id":
if desc {
scopes = append(scopes, db.OrderByIDDesc)
} else {
scopes = append(scopes, db.OrderByIDAsc)
}
default:
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("not support order"))
return
}
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"total": db.GetAllUserCountWithRole(dbModel.RolePending, scopes...),
"list": genUserListResp(dbModel.RolePending, append(scopes, db.Paginate(page, pageSize))...),
}))
}
func ApprovePendingUser(Authorization string, userID uint) error {
user, err := op.GetUserById(userID)
if err != nil {
return err
}
if !user.IsPending() {
return errors.New("user is not pending")
}
if err := user.SetRole(dbModel.RoleUser); err != nil {
return err
}
return nil
}
func BanUser(ctx *gin.Context) {
user := ctx.MustGet("user").(*op.User)
req := model.UserIDReq{}
if err := model.Decode(ctx, &req); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
u, err := op.GetUserById(req.ID)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
if u.ID == user.ID {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("cannot ban yourself"))
return
}
if u.IsRoot() {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("cannot ban root user"))
return
}
err = u.SetRole(dbModel.RoleBanned)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
ctx.Status(http.StatusNoContent)
}

@ -79,6 +79,9 @@ func AuthRoom(Authorization string) (*op.User, *op.Room, error) {
if u.IsBanned() {
return nil, nil, errors.New("user banned")
}
if u.IsPending() {
return nil, nil, errors.New("user is pending, need admin to approve")
}
r, err := op.LoadOrInitRoomByID(claims.RoomId)
if err != nil {
@ -108,6 +111,9 @@ func AuthUser(Authorization string) (*op.User, error) {
if u.IsBanned() {
return nil, errors.New("user banned")
}
if u.IsPending() {
return nil, errors.New("user is pending, need admin to approve")
}
return u, nil
}
@ -138,6 +144,9 @@ func NewAuthUserToken(user *op.User) (string, error) {
if user.IsBanned() {
return "", errors.New("user banned")
}
if user.IsPending() {
return "", errors.New("user is pending, need admin to approve")
}
t, err := time.ParseDuration(conf.Conf.Jwt.Expire)
if err != nil {
return "", err
@ -156,6 +165,9 @@ func NewAuthRoomToken(user *op.User, room *op.Room) (string, error) {
if user.IsBanned() {
return "", errors.New("user banned")
}
if user.IsPending() {
return "", errors.New("user is pending, need admin to approve")
}
t, err := time.ParseDuration(conf.Conf.Jwt.Expire)
if err != nil {
return "", err

@ -76,3 +76,18 @@ func (s *SetUsernameReq) Validate() error {
func (s *SetUsernameReq) Decode(ctx *gin.Context) error {
return json.NewDecoder(ctx.Request.Body).Decode(s)
}
type UserIDReq struct {
ID uint `json:"id"`
}
func (u *UserIDReq) Decode(ctx *gin.Context) error {
return json.NewDecoder(ctx.Request.Body).Decode(u)
}
func (u *UserIDReq) Validate() error {
if u.ID == 0 {
return errors.New("id is required")
}
return nil
}

@ -5,6 +5,8 @@ import (
"time"
"github.com/gin-gonic/gin"
"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/internal/provider"
"github.com/synctv-org/synctv/internal/provider/providers"
@ -93,7 +95,17 @@ func OAuth2Callback(ctx *gin.Context) {
if disable {
user, err = op.GetUserByProvider(p, ui.ProviderUserID)
} else {
user, err = op.CreateOrLoadUser(ui.Username, p, ui.ProviderUserID)
var review bool
review, err = settings.SignupNeedReview.Get()
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
if review {
user, err = op.CreateOrLoadUser(ui.Username, p, ui.ProviderUserID, db.WithRole(dbModel.RolePending))
} else {
user, err = op.CreateOrLoadUser(ui.Username, p, ui.ProviderUserID)
}
}
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))

Loading…
Cancel
Save