Feat: setting interface and boot strap

pull/21/head
zijiren233 2 years ago
parent 3085eb800b
commit ff6d2faa9f

@ -29,6 +29,7 @@ var ServerCmd = &cobra.Command{
bootstrap.InitLog,
bootstrap.InitGinMode,
bootstrap.InitDatabase,
bootstrap.InitSetting,
bootstrap.InitProvider,
bootstrap.InitOp,
bootstrap.InitRtmp,

@ -0,0 +1,11 @@
package bootstrap
import (
"context"
"github.com/synctv-org/synctv/internal/setting"
)
func InitSetting(ctx context.Context) error {
return setting.Init()
}

@ -245,3 +245,70 @@ func GetAdmins() []*model.User {
db.Where("role >= ?", model.RoleAdmin).Find(&users)
return users
}
func AddAdminByID(userID uint) error {
err := db.Model(&model.User{}).Where("id = ?", userID).Update("role", model.RoleAdmin).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("user not found")
}
return err
}
func RemoveAdminByID(userID uint) error {
err := db.Model(&model.User{}).Where("id = ?", userID).Update("role", model.RoleUser).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("user not found")
}
return err
}
func AddRoot(u *model.User) error {
if u.Role == model.RoleRoot {
return nil
}
u.Role = model.RoleRoot
return SaveUser(u)
}
func RemoveRoot(u *model.User) error {
if u.Role != model.RoleRoot {
return nil
}
u.Role = model.RoleUser
return SaveUser(u)
}
func AddRootByID(userID uint) error {
err := db.Model(&model.User{}).Where("id = ?", userID).Update("role", model.RoleRoot).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("user not found")
}
return err
}
func RemoveRootByID(userID uint) error {
err := db.Model(&model.User{}).Where("id = ?", userID).Update("role", model.RoleUser).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("user not found")
}
return err
}
func GetRoots() []*model.User {
var users []*model.User
db.Where("role = ?", model.RoleRoot).Find(&users)
return users
}
func SetRole(u *model.User, role model.Role) error {
u.Role = role
return SaveUser(u)
}
func SetRoleByID(userID uint, role model.Role) error {
err := db.Model(&model.User{}).Where("id = ?", userID).Update("role", role).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("user not found")
}
return err
}

@ -14,6 +14,7 @@ const (
RoleBanned Role = iota
RoleUser
RoleAdmin
RoleRoot
)
func (r Role) String() string {
@ -24,6 +25,8 @@ func (r Role) String() string {
return "user"
case RoleAdmin:
return "admin"
case RoleRoot:
return "root"
default:
return "unknown"
}
@ -50,6 +53,10 @@ func (u *User) BeforeCreate(tx *gorm.DB) error {
return nil
}
func (u *User) IsRoot() bool {
return u.Role == RoleRoot
}
func (u *User) IsAdmin() bool {
return u.Role == RoleAdmin
}

@ -2,8 +2,6 @@ package op
import (
"github.com/bluele/gcache"
"github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/model"
)
func Init(size int) error {
@ -11,27 +9,5 @@ func Init(size int) error {
LRU().
Build()
err := initSettings(ToSettings(BoolSettings)...)
if err != nil {
return err
}
return nil
}
func initSettings(i ...Setting) error {
for _, b := range i {
s := &model.Setting{
Name: b.Name(),
Value: b.Raw(),
Type: b.Type(),
Group: b.Group(),
}
err := db.FirstOrCreateSettingItemValue(s)
if err != nil {
return err
}
b.InitRaw(s.Value)
}
return nil
}

@ -1,150 +0,0 @@
package op
import (
"github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/model"
)
var (
BoolSettings map[string]BoolSetting
)
type Setting interface {
Name() string
InitRaw(string)
Raw() string
Type() model.SettingType
Group() model.SettingGroup
Interface() (any, error)
}
func GetSettingByGroup(group model.SettingGroup) []Setting {
return settingByGroup(group, ToSettings(BoolSettings)...)
}
func settingByGroup(group model.SettingGroup, settings ...Setting) []Setting {
s := make([]Setting, 0, len(settings))
for _, bs := range settings {
if bs.Group() == group {
s = append(s, bs)
}
}
return s
}
func ToSettings[s Setting](settings ...map[string]s) []Setting {
l := 0
for _, v := range settings {
l += len(v)
}
var ss []Setting = make([]Setting, 0, l)
for _, v := range settings {
for _, s := range v {
ss = append(ss, s)
}
}
return ss
}
type BoolSetting interface {
Setting
Set(bool) error
Get() (bool, error)
}
type Bool struct {
name string
value string
}
func NewBool(name, value string) *Bool {
return &Bool{
name: name,
value: value,
}
}
func (b *Bool) Name() string {
return b.name
}
func (b *Bool) InitRaw(s string) {
if b.value == s {
return
}
b.value = s
}
func (b *Bool) Set(value bool) error {
if value {
if b.value == "1" {
return nil
}
b.value = "1"
} else {
if b.value == "0" {
return nil
}
b.value = "0"
}
return db.UpdateSettingItemValue(b.name, b.value)
}
func (b *Bool) Get() (bool, error) {
return b.value == "1", nil
}
func (b *Bool) Raw() string {
return b.value
}
func (b *Bool) Type() model.SettingType {
return model.SettingTypeBool
}
func (b *Bool) Group() model.SettingGroup {
return model.SettingGroupRoom
}
func (b *Bool) Interface() (any, error) {
return b.Get()
}
type Int64Setting interface {
Set(int64) error
Get() (int64, error)
Raw() string
}
type Float64Setting interface {
Set(float64) error
Get() (float64, error)
Raw() string
}
type StringSetting interface {
Set(string) error
Get() (string, error)
Raw() string
}
func newRegBoolSetting(k, v string) BoolSetting {
b := NewBool(k, v)
if BoolSettings == nil {
BoolSettings = make(map[string]BoolSetting)
}
BoolSettings[k] = b
return b
}
func GetSettingType(name string) (model.SettingType, bool) {
s, ok := BoolSettings[name]
if !ok {
return "", false
}
return s.Type(), true
}
var (
DisableCreateRoom = newRegBoolSetting("disable_create_room", "0")
)

@ -22,8 +22,12 @@ func (u *User) NewMovie(movie model.MovieInfo) model.Movie {
}
}
func (u *User) IsRoot() bool {
return u.Role == model.RoleRoot
}
func (u *User) IsAdmin() bool {
return u.Role == model.RoleAdmin
return u.Role >= model.RoleAdmin
}
func (u *User) IsBanned() bool {
@ -31,7 +35,7 @@ func (u *User) IsBanned() bool {
}
func (u *User) HasPermission(roomID uint, permission model.Permission) bool {
if u.Role == model.RoleAdmin {
if u.Role >= model.RoleAdmin {
return true
}
ur, err := db.GetRoomUserRelation(roomID, u.ID)
@ -54,3 +58,11 @@ func (u *User) SetRoomPassword(roomID uint, password string) error {
}
return SetRoomPassword(roomID, password)
}
func (u *User) SetRole(role model.Role) error {
if err := db.SetRoleByID(u.ID, role); err != nil {
return err
}
u.Role = role
return nil
}

@ -0,0 +1,90 @@
package setting
import (
log "github.com/sirupsen/logrus"
"github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/model"
)
type BoolSetting interface {
Setting
Set(bool) error
Get() (bool, error)
}
type Bool struct {
name string
value string
group model.SettingGroup
}
func NewBool(name, value string, group model.SettingGroup) *Bool {
return &Bool{
name: name,
value: value,
group: group,
}
}
func (b *Bool) Name() string {
return b.name
}
func (b *Bool) InitRaw(s string) {
if b.value == s {
return
}
b.value = s
}
func (b *Bool) Set(value bool) error {
if value {
if b.value == "1" {
return nil
}
b.value = "1"
} else {
if b.value == "0" {
return nil
}
b.value = "0"
}
return db.UpdateSettingItemValue(b.name, b.value)
}
func (b *Bool) Get() (bool, error) {
return b.value == "1", nil
}
func (b *Bool) Raw() string {
return b.value
}
func (b *Bool) Type() model.SettingType {
return model.SettingTypeBool
}
func (b *Bool) Group() model.SettingGroup {
return b.group
}
func (b *Bool) Interface() (any, error) {
return b.Get()
}
func newBoolSetting(k, v string, g model.SettingGroup) BoolSetting {
if Settings == nil {
Settings = make(map[string]Setting)
}
if GroupsSetting == nil {
GroupsSetting = make(map[model.SettingGroup][]Setting)
}
_, loaded := Settings[k]
if loaded {
log.Fatalf("setting %s already exists", k)
}
b := NewBool(k, v, g)
Settings[k] = b
GroupsSetting[g] = append(GroupsSetting[g], b)
return b
}

@ -0,0 +1,126 @@
package setting
import (
"fmt"
log "github.com/sirupsen/logrus"
"github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/model"
)
var (
Settings map[string]Setting
GroupsSetting map[model.SettingGroup][]Setting
)
type Setting interface {
Name() string
InitRaw(string)
Raw() string
Type() model.SettingType
Group() model.SettingGroup
Interface() (any, error)
}
func SetValue(name string, value any) error {
s, ok := Settings[name]
if !ok {
return fmt.Errorf("setting %s not found", name)
}
return SetSettingValue(s, value)
}
func SetSettingValue(s Setting, value any) error {
switch s.Type() {
case model.SettingTypeBool:
i, ok := s.(BoolSetting)
if !ok {
log.Fatalf("setting %s is not bool", s.Name())
}
v, ok := value.(bool)
if !ok {
return fmt.Errorf("setting %s, value %v is not bool", s.Name(), value)
}
i.Set(v)
case model.SettingTypeInt64:
i, ok := s.(Int64Setting)
if !ok {
log.Fatalf("setting %s is not int64", s.Name())
}
v, ok := value.(int64)
if !ok {
return fmt.Errorf("setting %s, value %v is not int64", s.Name(), value)
}
i.Set(v)
case model.SettingTypeFloat64:
i, ok := s.(Float64Setting)
if !ok {
log.Fatalf("setting %s is not float64", s.Name())
}
v, ok := value.(float64)
if !ok {
return fmt.Errorf("setting %s, value %v is not float64", s.Name(), value)
}
i.Set(v)
case model.SettingTypeString:
i, ok := s.(StringSetting)
if !ok {
log.Fatalf("setting %s is not string", s.Name())
}
v, ok := value.(string)
if !ok {
return fmt.Errorf("setting %s, value %v is not string", s.Name(), value)
}
i.Set(v)
default:
log.Fatalf("unknown setting type: %s", s.Type())
}
return nil
}
func ToSettings[s Setting](settings map[string]s) []Setting {
var ss []Setting = make([]Setting, 0, len(settings))
for _, v := range settings {
ss = append(ss, v)
}
return ss
}
type Int64Setting interface {
Set(int64) error
Get() (int64, error)
Raw() string
}
type Float64Setting interface {
Set(float64) error
Get() (float64, error)
Raw() string
}
type StringSetting interface {
Set(string) error
Get() (string, error)
Raw() string
}
func Init() error {
return initSettings(ToSettings(Settings)...)
}
func initSettings(i ...Setting) error {
for _, b := range i {
s := &model.Setting{
Name: b.Name(),
Value: b.Raw(),
Type: b.Type(),
Group: b.Group(),
}
err := db.FirstOrCreateSettingItemValue(s)
if err != nil {
return err
}
b.InitRaw(s.Value)
}
return nil
}

@ -0,0 +1,7 @@
package setting
import "github.com/synctv-org/synctv/internal/model"
var (
DisableCreateRoom = newBoolSetting("disable_create_room", "0", model.SettingGroupRoom)
)

@ -1,12 +1,12 @@
package handlers
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
dbModel "github.com/synctv-org/synctv/internal/model"
"github.com/synctv-org/synctv/internal/op"
"github.com/synctv-org/synctv/internal/setting"
"github.com/synctv-org/synctv/server/model"
)
@ -14,26 +14,17 @@ func EditAdminSettings(ctx *gin.Context) {
// user := ctx.MustGet("user").(*op.User)
req := model.AdminSettingsReq{}
if err := req.Decode(ctx); err != nil {
ctx.AbortWithError(http.StatusBadRequest, err)
if err := model.Decode(ctx, &req); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
for k, v := range req {
t, ok := op.GetSettingType(k)
if !ok {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp(fmt.Sprintf("setting %s not found", k)))
err := setting.SetValue(k, v)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
switch t {
case dbModel.SettingTypeBool:
b, ok := v.(bool)
if !ok {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp(fmt.Sprintf("setting %s is not bool", k)))
return
}
op.BoolSettings[k].Set(b)
}
}
ctx.Status(http.StatusNoContent)
@ -41,13 +32,17 @@ func EditAdminSettings(ctx *gin.Context) {
func AdminSettings(ctx *gin.Context) {
// user := ctx.MustGet("user").(*op.User)
group := ctx.Query("group")
group := ctx.Param("group")
if group == "" {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("group is required"))
return
}
s := op.GetSettingByGroup(dbModel.SettingGroup(group))
s, ok := setting.GroupsSetting[dbModel.SettingGroup(group)]
if !ok {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("group not found"))
return
}
resp := make(gin.H, len(s))
for _, v := range s {
i, err := v.Interface()
@ -60,3 +55,47 @@ func AdminSettings(ctx *gin.Context) {
ctx.JSON(http.StatusOK, model.NewApiDataResp(resp))
}
func AddAdmin(ctx *gin.Context) {
user := ctx.MustGet("user").(*op.User)
if !user.IsRoot() {
ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("permission denied"))
return
}
req := model.IdReq{}
if err := model.Decode(ctx, &req); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
if err := user.SetRole(dbModel.RoleAdmin); err != nil {
ctx.AbortWithError(http.StatusInternalServerError, err)
return
}
ctx.Status(http.StatusNoContent)
}
func DeleteAdmin(ctx *gin.Context) {
user := ctx.MustGet("user").(*op.User)
if !user.IsRoot() {
ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("permission denied"))
return
}
req := model.IdReq{}
if err := model.Decode(ctx, &req); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
if err := user.SetRole(dbModel.RoleUser); err != nil {
ctx.AbortWithError(http.StatusInternalServerError, err)
return
}
ctx.Status(http.StatusNoContent)
}

@ -41,7 +41,7 @@ func Init(e *gin.Engine) {
admin := api.Group("/admin")
admin.Use(middlewares.AuthAdminMiddleware)
admin.GET("/settings", AdminSettings)
admin.GET("/settings/:group", AdminSettings)
admin.POST("/settings", EditAdminSettings)
}

@ -9,6 +9,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/op"
"github.com/synctv-org/synctv/internal/setting"
"github.com/synctv-org/synctv/server/middlewares"
"github.com/synctv-org/synctv/server/model"
"github.com/synctv-org/synctv/utils"
@ -31,7 +32,7 @@ func (e FormatErrNotSupportPosition) Error() string {
func CreateRoom(ctx *gin.Context) {
user := ctx.MustGet("user").(*op.User)
v, err := op.DisableCreateRoom.Get()
v, err := setting.DisableCreateRoom.Get()
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return

@ -1,10 +1,16 @@
package model
import (
"errors"
"github.com/gin-gonic/gin"
json "github.com/json-iterator/go"
)
var (
ErrInvalidID = errors.New("invalid id")
)
type AdminSettingsReq map[string]any
func (asr *AdminSettingsReq) Validate() error {

Loading…
Cancel
Save