Feat: db update

pull/31/head
zijiren233 2 years ago
parent 4b228748a2
commit 74fa432ad7

@ -29,18 +29,11 @@ var SetCmd = &cobra.Command{
if !ok {
return errors.New("setting not found")
}
current := s.Raw()
err := s.SetRaw(args[1])
err := s.SetString(args[1])
if err != nil {
s.SetRaw(current)
fmt.Printf("set setting %s error: %v\n", args[0], err)
}
if v := s.Interface(); err != nil {
s.SetRaw(current)
fmt.Printf("set setting %s error: %v\n", args[0], err)
} else {
fmt.Printf("set setting success:\n%s: %v\n", args[0], v)
}
fmt.Printf("set setting success:\n%s: %v\n", args[0], s.Interface())
return nil
},
}

@ -3,9 +3,55 @@ package bootstrap
import (
"context"
"github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/model"
"github.com/synctv-org/synctv/internal/settings"
)
func InitSetting(ctx context.Context) error {
return settings.Init()
return initAndFixSettings(settings.Settings)
}
func initSettings(s map[string]settings.Setting) error {
for _, b := range s {
s := &model.Setting{
Name: b.Name(),
Value: b.DefaultString(),
Type: b.Type(),
Group: b.Group(),
}
err := db.FirstOrCreateSettingItemValue(s)
if err != nil {
return err
}
err = b.Init(s.Value)
if err != nil {
return err
}
}
return nil
}
func initAndFixSettings(s map[string]settings.Setting) error {
for _, b := range s {
s := &model.Setting{
Name: b.Name(),
Value: b.DefaultString(),
Type: b.Type(),
Group: b.Group(),
}
err := db.FirstOrCreateSettingItemValue(s)
if err != nil {
return err
}
err = b.Init(s.Value)
if err != nil {
// auto fix
err = b.SetString(b.DefaultString())
if err != nil {
return err
}
}
}
return nil
}

@ -32,7 +32,10 @@ func AutoMigrate(dst ...any) error {
default:
log.Fatalf("unknown database type: %s", conf.Conf.Database.Type)
}
return err
if err != nil {
return err
}
return upgradeDatabase()
}
func DB() *gorm.DB {

@ -0,0 +1,56 @@
package db
import (
log "github.com/sirupsen/logrus"
"github.com/synctv-org/synctv/internal/model"
)
type dbVersion struct {
NextVersion string
Upgrade func() error
}
var dbVersions = map[string]dbVersion{
"0.0.1": {
NextVersion: "",
Upgrade: nil,
},
}
func upgradeDatabase() error {
setting := model.Setting{
Name: "database_version",
Type: model.SettingTypeString,
Group: model.SettingGroupDatabase,
Value: "0.0.1",
}
err := FirstOrCreateSettingItemValue(&setting)
if err != nil {
return err
}
currentVersion := setting.Value
version, ok := dbVersions[currentVersion]
if !ok {
return nil
}
currentVersion = version.NextVersion
for currentVersion != "" {
version, ok := dbVersions[currentVersion]
if !ok {
break
}
log.Infof("Upgrading database to version %s", currentVersion)
if version.Upgrade != nil {
err = version.Upgrade()
if err != nil {
return err
}
}
err = UpdateSettingItemValue("database_version", currentVersion)
if err != nil {
return err
}
currentVersion = version.NextVersion
}
return nil
}

@ -16,10 +16,11 @@ func (s SettingGroup) String() string {
}
const (
SettingGroupRoom SettingGroup = "room"
SettingGroupUser SettingGroup = "user"
SettingGroupProxy SettingGroup = "proxy"
SettingGroupRtmp SettingGroup = "rtmp"
SettingGroupRoom SettingGroup = "room"
SettingGroupUser SettingGroup = "user"
SettingGroupProxy SettingGroup = "proxy"
SettingGroupRtmp SettingGroup = "rtmp"
SettingGroupDatabase SettingGroup = "database"
)
type Setting struct {

@ -5,7 +5,6 @@ import (
"strconv"
"sync/atomic"
"github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/model"
)
@ -24,9 +23,18 @@ type Bool struct {
setting
defaultValue bool
value uint32
beforeSet func(BoolSetting, bool) error
}
func NewBool(name string, value bool, group model.SettingGroup) *Bool {
type BoolSettingOption func(*Bool)
func WithBeforeSetBool(beforeSet func(BoolSetting, bool) error) BoolSettingOption {
return func(s *Bool) {
s.beforeSet = beforeSet
}
}
func newBool(name string, value bool, group model.SettingGroup, options ...BoolSettingOption) *Bool {
b := &Bool{
setting: setting{
name: name,
@ -35,6 +43,9 @@ func NewBool(name string, value bool, group model.SettingGroup) *Bool {
},
defaultValue: value,
}
for _, option := range options {
option(b)
}
b.set(value)
return b
}
@ -72,11 +83,11 @@ func (b *Bool) Default() bool {
return b.defaultValue
}
func (b *Bool) DefaultRaw() string {
func (b *Bool) DefaultString() string {
return b.Stringify(b.defaultValue)
}
func (b *Bool) Raw() string {
func (b *Bool) String() string {
return b.Stringify(b.Get())
}
@ -84,33 +95,45 @@ func (b *Bool) DefaultInterface() any {
return b.Default()
}
func (b *Bool) SetRaw(value string) error {
err := b.Init(value)
func (b *Bool) SetString(value string) error {
v, err := b.Parse(value)
if err != nil {
return err
}
return db.UpdateSettingItemValue(b.Name(), b.Raw())
if b.beforeSet != nil {
err = b.beforeSet(b, v)
if err != nil {
return err
}
}
b.set(v)
return nil
}
func (b *Bool) Set(value bool) error {
err := db.UpdateSettingItemValue(b.Name(), b.Stringify(value))
if err != nil {
return err
func (b *Bool) Set(value bool) (err error) {
if b.beforeSet != nil {
err = b.beforeSet(b, value)
if err != nil {
return err
}
}
b.set(value)
return nil
return
}
func (b *Bool) Interface() any {
return b.Get()
}
func newBoolSetting(k string, v bool, g model.SettingGroup) BoolSetting {
func NewBoolSetting(k string, v bool, g model.SettingGroup, options ...BoolSettingOption) BoolSetting {
_, loaded := Settings[k]
if loaded {
panic(fmt.Sprintf("setting %s already exists", k))
}
b := NewBool(k, v, g)
b := newBool(k, v, g, options...)
Settings[k] = b
GroupSettings[g] = append(GroupSettings[g], b)
return b

@ -6,7 +6,6 @@ import (
"strconv"
"sync/atomic"
"github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/model"
)
@ -25,9 +24,25 @@ type Float64 struct {
setting
defaultValue float64
value uint64
validator func(float64) error
beforeSet func(Float64Setting, float64) error
}
func NewFloat64(name string, value float64, group model.SettingGroup) *Float64 {
type Float64SettingOption func(*Float64)
func WithValidatorFloat64(validator func(float64) error) Float64SettingOption {
return func(s *Float64) {
s.validator = validator
}
}
func WithBeforeSetFloat64(beforeSet func(Float64Setting, float64) error) Float64SettingOption {
return func(s *Float64) {
s.beforeSet = beforeSet
}
}
func newFloat64(name string, value float64, group model.SettingGroup, options ...Float64SettingOption) *Float64 {
f := &Float64{
setting: setting{
name: name,
@ -36,12 +51,22 @@ func NewFloat64(name string, value float64, group model.SettingGroup) *Float64 {
},
defaultValue: value,
}
for _, option := range options {
option(f)
}
f.set(value)
return f
}
func (f *Float64) Parse(value string) (float64, error) {
return strconv.ParseFloat(value, 64)
v, err := strconv.ParseFloat(value, 64)
if err != nil {
return 0, err
}
if f.validator != nil {
return v, f.validator(v)
}
return v, nil
}
func (f *Float64) Stringify(value float64) string {
@ -57,7 +82,7 @@ func (f *Float64) Init(value string) error {
return nil
}
func (f *Float64) Raw() string {
func (f *Float64) String() string {
return f.Stringify(f.Get())
}
@ -65,7 +90,7 @@ func (f *Float64) Default() float64 {
return f.defaultValue
}
func (f *Float64) DefaultRaw() string {
func (f *Float64) DefaultString() string {
return f.Stringify(f.defaultValue)
}
@ -73,25 +98,44 @@ func (f *Float64) DefaultInterface() any {
return f.Default()
}
func (f *Float64) SetRaw(value string) error {
err := f.Init(value)
func (f *Float64) SetString(value string) error {
v, err := f.Parse(value)
if err != nil {
return err
}
return db.UpdateSettingItemValue(f.Name(), f.Raw())
if f.beforeSet != nil {
err = f.beforeSet(f, v)
if err != nil {
return err
}
}
f.set(v)
return nil
}
func (f *Float64) set(value float64) {
atomic.StoreUint64(&f.value, math.Float64bits(value))
}
func (f *Float64) Set(value float64) error {
err := db.UpdateSettingItemValue(f.Name(), f.Stringify(value))
if err != nil {
return err
func (f *Float64) Set(value float64) (err error) {
if f.validator != nil {
err = f.validator(value)
if err != nil {
return err
}
}
if f.beforeSet != nil {
err = f.beforeSet(f, value)
if err != nil {
return err
}
}
f.set(value)
return nil
return
}
func (f *Float64) Get() float64 {
@ -102,12 +146,12 @@ func (f *Float64) Interface() any {
return f.Get()
}
func newFloat64Setting(k string, v float64, g model.SettingGroup) *Float64 {
func NewFloat64Setting(k string, v float64, g model.SettingGroup, options ...Float64SettingOption) *Float64 {
_, loaded := Settings[k]
if loaded {
panic(fmt.Sprintf("setting %s already exists", k))
}
f := NewFloat64(k, v, g)
f := newFloat64(k, v, g, options...)
Settings[k] = f
GroupSettings[g] = append(GroupSettings[g], f)
return f

@ -5,7 +5,6 @@ import (
"strconv"
"sync/atomic"
"github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/model"
)
@ -24,9 +23,25 @@ type Int64 struct {
setting
defaultValue int64
value int64
validator func(int64) error
beforeSet func(Int64Setting, int64) error
}
func NewInt64(name string, value int64, group model.SettingGroup) *Int64 {
type Int64SettingOption func(*Int64)
func WithValidatorInt64(validator func(int64) error) Int64SettingOption {
return func(s *Int64) {
s.validator = validator
}
}
func WithBeforeSetInt64(beforeSet func(Int64Setting, int64) error) Int64SettingOption {
return func(s *Int64) {
s.beforeSet = beforeSet
}
}
func newInt64(name string, value int64, group model.SettingGroup, options ...Int64SettingOption) *Int64 {
i := &Int64{
setting: setting{
name: name,
@ -36,11 +51,21 @@ func NewInt64(name string, value int64, group model.SettingGroup) *Int64 {
defaultValue: value,
value: value,
}
for _, option := range options {
option(i)
}
return i
}
func (i *Int64) Parse(value string) (int64, error) {
return strconv.ParseInt(value, 10, 64)
v, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return 0, err
}
if i.validator != nil {
return v, i.validator(v)
}
return v, nil
}
func (i *Int64) Stringify(value int64) string {
@ -60,37 +85,56 @@ func (i *Int64) Default() int64 {
return i.defaultValue
}
func (i *Int64) DefaultRaw() string {
return strconv.FormatInt(i.defaultValue, 10)
func (i *Int64) DefaultString() string {
return i.Stringify(i.defaultValue)
}
func (i *Int64) DefaultInterface() any {
return i.defaultValue
return i.Default()
}
func (i *Int64) Raw() string {
func (i *Int64) String() string {
return i.Stringify(i.Get())
}
func (i *Int64) SetRaw(value string) error {
err := i.Init(value)
func (i *Int64) SetString(value string) error {
v, err := i.Parse(value)
if err != nil {
return err
}
return db.UpdateSettingItemValue(i.Name(), i.Raw())
if i.beforeSet != nil {
err = i.beforeSet(i, v)
if err != nil {
return err
}
}
i.set(v)
return nil
}
func (i *Int64) set(value int64) {
atomic.StoreInt64(&i.value, value)
}
func (i *Int64) Set(value int64) error {
err := db.UpdateSettingItemValue(i.Name(), i.Stringify(value))
if err != nil {
return err
func (i *Int64) Set(value int64) (err error) {
if i.validator != nil {
err = i.validator(value)
if err != nil {
return err
}
}
if i.beforeSet != nil {
err = i.beforeSet(i, value)
if err != nil {
return err
}
}
i.set(value)
return nil
return
}
func (i *Int64) Get() int64 {
@ -101,12 +145,12 @@ func (i *Int64) Interface() any {
return i.Get()
}
func newInt64Setting(k string, v int64, g model.SettingGroup) *Int64 {
func NewInt64Setting(k string, v int64, g model.SettingGroup, options ...Int64SettingOption) *Int64 {
_, loaded := Settings[k]
if loaded {
panic(fmt.Sprintf("setting %s already exists", k))
}
i := NewInt64(k, v, g)
i := newInt64(k, v, g, options...)
Settings[k] = i
GroupSettings[g] = append(GroupSettings[g], i)
return i

@ -4,8 +4,6 @@ import (
"fmt"
json "github.com/json-iterator/go"
log "github.com/sirupsen/logrus"
"github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/model"
)
@ -19,9 +17,9 @@ type Setting interface {
Type() model.SettingType
Group() model.SettingGroup
Init(string) error
Raw() string
SetRaw(string) error
DefaultRaw() string
String() string
SetString(string) error
DefaultString() string
DefaultInterface() any
Interface() any
}
@ -31,79 +29,7 @@ func SetValue(name string, value any) error {
if !ok {
return fmt.Errorf("setting %s not found", name)
}
return SetSettingValue(s, value)
}
func SetSettingValue(s Setting, value any) error {
switch s := s.(type) {
case BoolSetting:
return s.Set(json.Wrap(value).ToBool())
case Int64Setting:
return s.Set(json.Wrap(value).ToInt64())
case Float64Setting:
return s.Set(json.Wrap(value).ToFloat64())
case StringSetting:
return s.Set(json.Wrap(value).ToString())
default:
log.Fatalf("unknown setting %s type: %s", s.Name(), 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
}
func Init() error {
return initAndFixSettings(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
}
err = b.Init(s.Value)
if err != nil {
return err
}
}
return nil
}
func initAndFixSettings(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
}
err = b.Init(s.Value)
if err != nil {
// auto fix
err = b.SetRaw(b.DefaultRaw())
if err != nil {
return err
}
}
}
return nil
return s.SetString(json.Wrap(value).ToString())
}
type setting struct {

@ -4,7 +4,6 @@ import (
"fmt"
"sync"
"github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/model"
)
@ -24,9 +23,25 @@ type String struct {
defaultValue string
lock sync.RWMutex
value string
validator func(string) error
beforeSet func(StringSetting, string) error
}
func NewString(name string, value string, group model.SettingGroup) *String {
type StringSettingOption func(*String)
func WithValidatorString(validator func(string) error) StringSettingOption {
return func(s *String) {
s.validator = validator
}
}
func WithBeforeSetString(beforeSet func(StringSetting, string) error) StringSettingOption {
return func(s *String) {
s.beforeSet = beforeSet
}
}
func newString(name string, value string, group model.SettingGroup, options ...StringSettingOption) *String {
s := &String{
setting: setting{
name: name,
@ -36,10 +51,16 @@ func NewString(name string, value string, group model.SettingGroup) *String {
defaultValue: value,
value: value,
}
for _, option := range options {
option(s)
}
return s
}
func (s *String) Parse(value string) (string, error) {
if s.validator != nil {
return value, s.validator(value)
}
return value, nil
}
@ -60,24 +81,33 @@ func (s *String) Default() string {
return s.defaultValue
}
func (s *String) DefaultRaw() string {
return s.defaultValue
func (s *String) DefaultString() string {
return s.Stringify(s.defaultValue)
}
func (s *String) DefaultInterface() any {
return s.Default()
}
func (s *String) Raw() string {
func (s *String) String() string {
return s.Stringify(s.Get())
}
func (s *String) SetRaw(value string) error {
err := s.Init(value)
func (s *String) SetString(value string) error {
v, err := s.Parse(value)
if err != nil {
return err
}
return db.UpdateSettingItemValue(s.Name(), s.Raw())
if s.beforeSet != nil {
err = s.beforeSet(s, v)
if err != nil {
return err
}
}
s.set(v)
return nil
}
func (s *String) set(value string) {
@ -86,13 +116,23 @@ func (s *String) set(value string) {
s.value = value
}
func (s *String) Set(value string) error {
err := db.UpdateSettingItemValue(s.Name(), s.Stringify(value))
if err != nil {
return err
func (s *String) Set(value string) (err error) {
if s.validator != nil {
err = s.validator(value)
if err != nil {
return err
}
}
if s.beforeSet != nil {
err = s.beforeSet(s, value)
if err != nil {
return err
}
}
s.set(value)
return nil
return
}
func (s *String) Get() string {
@ -105,12 +145,12 @@ func (s *String) Interface() any {
return s.Get()
}
func newStringSetting(k string, v string, g model.SettingGroup) *String {
func NewStringSetting(k string, v string, g model.SettingGroup, options ...StringSettingOption) *String {
_, loaded := Settings[k]
if loaded {
panic(fmt.Sprintf("setting %s already exists", k))
}
s := NewString(k, v, g)
s := newString(k, v, g, options...)
Settings[k] = s
GroupSettings[g] = append(GroupSettings[g], s)
return s

@ -1,35 +1,59 @@
package settings
import (
"errors"
"time"
"github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/model"
)
func BeforeSetBoolFunc(s BoolSetting, v bool) error {
return db.UpdateSettingItemValue(s.Name(), s.Stringify(v))
}
func BeforeSetStringFunc(s StringSetting, v string) error {
return db.UpdateSettingItemValue(s.Name(), s.Stringify(v))
}
func BeforeSetInt64Func(s Int64Setting, v int64) error {
return db.UpdateSettingItemValue(s.Name(), s.Stringify(v))
}
func BeforeSetFloat64Func(s Float64Setting, v float64) error {
return db.UpdateSettingItemValue(s.Name(), s.Stringify(v))
}
var (
DisableCreateRoom = newBoolSetting("disable_create_room", false, model.SettingGroupRoom)
RoomMustNeedPwd = newBoolSetting("room_must_need_pwd", false, model.SettingGroupRoom)
CreateRoomNeedReview = newBoolSetting("create_room_need_review", false, model.SettingGroupRoom)
RoomTTL = newInt64Setting("room_ttl", int64(time.Hour*48), model.SettingGroupRoom)
UserMaxRoomCount = newInt64Setting("user_max_room_count", 3, model.SettingGroupRoom)
DisableCreateRoom = NewBoolSetting("disable_create_room", false, model.SettingGroupRoom, WithBeforeSetBool(BeforeSetBoolFunc))
RoomMustNeedPwd = NewBoolSetting("room_must_need_pwd", false, model.SettingGroupRoom, WithBeforeSetBool(BeforeSetBoolFunc))
CreateRoomNeedReview = NewBoolSetting("create_room_need_review", false, model.SettingGroupRoom, WithBeforeSetBool(BeforeSetBoolFunc))
RoomTTL = NewInt64Setting("room_ttl", int64(time.Hour*48), model.SettingGroupRoom, WithBeforeSetInt64(BeforeSetInt64Func))
UserMaxRoomCount = NewInt64Setting("user_max_room_count", 3, model.SettingGroupRoom, WithBeforeSetInt64(BeforeSetInt64Func))
)
var (
DisableUserSignup = newBoolSetting("disable_user_signup", false, model.SettingGroupUser)
SignupNeedReview = newBoolSetting("signup_need_review", false, model.SettingGroupUser)
DisableUserSignup = NewBoolSetting("disable_user_signup", false, model.SettingGroupUser, WithBeforeSetBool(BeforeSetBoolFunc))
SignupNeedReview = NewBoolSetting("signup_need_review", false, model.SettingGroupUser, WithBeforeSetBool(BeforeSetBoolFunc))
)
var (
MovieProxy = newBoolSetting("movie_proxy", true, model.SettingGroupProxy)
LiveProxy = newBoolSetting("live_proxy", true, model.SettingGroupProxy)
AllowProxyToLocal = newBoolSetting("allow_proxy_to_local", false, model.SettingGroupProxy)
MovieProxy = NewBoolSetting("movie_proxy", true, model.SettingGroupProxy, WithBeforeSetBool(BeforeSetBoolFunc))
LiveProxy = NewBoolSetting("live_proxy", true, model.SettingGroupProxy, WithBeforeSetBool(BeforeSetBoolFunc))
AllowProxyToLocal = NewBoolSetting("allow_proxy_to_local", false, model.SettingGroupProxy, WithBeforeSetBool(BeforeSetBoolFunc))
)
var (
// can watch live streams through the RTMP protocol (without authentication, insecure).
RtmpPlayer = newBoolSetting("rtmp_player", false, model.SettingGroupRtmp)
RtmpPlayer = NewBoolSetting("rtmp_player", false, model.SettingGroupRtmp, WithBeforeSetBool(BeforeSetBoolFunc))
// default use http header host
CustomPublishHost = newStringSetting("custom_publish_host", "", model.SettingGroupRtmp)
CustomPublishHost = NewStringSetting("custom_publish_host", "", model.SettingGroupRtmp, WithBeforeSetString(BeforeSetStringFunc))
// disguise the .ts file as a .png file
TsDisguisedAsPng = newBoolSetting("ts_disguised_as_png", true, model.SettingGroupRtmp)
TsDisguisedAsPng = NewBoolSetting("ts_disguised_as_png", true, model.SettingGroupRtmp, WithBeforeSetBool(BeforeSetBoolFunc))
)
var (
DatabaseVersion = NewStringSetting("database_version", "0.0.1", model.SettingGroupDatabase, WithBeforeSetString(func(ss StringSetting, s string) error {
return errors.New("not support change database version")
}))
)

Loading…
Cancel
Save