code.gitea.io/gitea@v1.22.3/models/system/setting.go (about) 1 // Copyright 2021 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package system 5 6 import ( 7 "context" 8 "math" 9 "sync" 10 "time" 11 12 "code.gitea.io/gitea/models/db" 13 "code.gitea.io/gitea/modules/log" 14 "code.gitea.io/gitea/modules/setting/config" 15 "code.gitea.io/gitea/modules/timeutil" 16 17 "xorm.io/builder" 18 ) 19 20 type Setting struct { 21 ID int64 `xorm:"pk autoincr"` 22 SettingKey string `xorm:"varchar(255) unique"` // key should be lowercase 23 SettingValue string `xorm:"text"` 24 Version int `xorm:"version"` 25 Created timeutil.TimeStamp `xorm:"created"` 26 Updated timeutil.TimeStamp `xorm:"updated"` 27 } 28 29 // TableName sets the table name for the settings struct 30 func (s *Setting) TableName() string { 31 return "system_setting" 32 } 33 34 func init() { 35 db.RegisterModel(new(Setting)) 36 } 37 38 const keyRevision = "revision" 39 40 func GetRevision(ctx context.Context) int { 41 revision, exist, err := db.Get[Setting](ctx, builder.Eq{"setting_key": keyRevision}) 42 if err != nil { 43 return 0 44 } else if !exist { 45 err = db.Insert(ctx, &Setting{SettingKey: keyRevision, Version: 1}) 46 if err != nil { 47 return 0 48 } 49 return 1 50 } 51 if revision.Version <= 0 || revision.Version >= math.MaxInt-1 { 52 _, err = db.Exec(ctx, "UPDATE system_setting SET version=1 WHERE setting_key=?", keyRevision) 53 if err != nil { 54 return 0 55 } 56 return 1 57 } 58 return revision.Version 59 } 60 61 func GetAllSettings(ctx context.Context) (revision int, res map[string]string, err error) { 62 _ = GetRevision(ctx) // prepare the "revision" key ahead 63 var settings []*Setting 64 if err := db.GetEngine(ctx). 65 Find(&settings); err != nil { 66 return 0, nil, err 67 } 68 res = make(map[string]string) 69 for _, s := range settings { 70 if s.SettingKey == keyRevision { 71 revision = s.Version 72 } 73 res[s.SettingKey] = s.SettingValue 74 } 75 return revision, res, nil 76 } 77 78 func SetSettings(ctx context.Context, settings map[string]string) error { 79 _ = GetRevision(ctx) // prepare the "revision" key ahead 80 return db.WithTx(ctx, func(ctx context.Context) error { 81 e := db.GetEngine(ctx) 82 _, err := db.Exec(ctx, "UPDATE system_setting SET version=version+1 WHERE setting_key=?", keyRevision) 83 if err != nil { 84 return err 85 } 86 for k, v := range settings { 87 res, err := e.Exec("UPDATE system_setting SET version=version+1, setting_value=? WHERE setting_key=?", v, k) 88 if err != nil { 89 return err 90 } 91 rows, _ := res.RowsAffected() 92 if rows == 0 { // if no existing row, insert a new row 93 if _, err = e.Insert(&Setting{SettingKey: k, SettingValue: v}); err != nil { 94 return err 95 } 96 } 97 } 98 return nil 99 }) 100 } 101 102 type dbConfigCachedGetter struct { 103 mu sync.RWMutex 104 105 cacheTime time.Time 106 revision int 107 settings map[string]string 108 } 109 110 var _ config.DynKeyGetter = (*dbConfigCachedGetter)(nil) 111 112 func (d *dbConfigCachedGetter) GetValue(ctx context.Context, key string) (v string, has bool) { 113 d.mu.RLock() 114 defer d.mu.RUnlock() 115 v, has = d.settings[key] 116 return v, has 117 } 118 119 func (d *dbConfigCachedGetter) GetRevision(ctx context.Context) int { 120 d.mu.RLock() 121 cachedDuration := time.Since(d.cacheTime) 122 cachedRevision := d.revision 123 d.mu.RUnlock() 124 125 if cachedDuration < time.Second { 126 return cachedRevision 127 } 128 129 d.mu.Lock() 130 defer d.mu.Unlock() 131 if GetRevision(ctx) != d.revision { 132 rev, set, err := GetAllSettings(ctx) 133 if err != nil { 134 log.Error("Unable to get all settings: %v", err) 135 } else { 136 d.revision = rev 137 d.settings = set 138 } 139 } 140 d.cacheTime = time.Now() 141 return d.revision 142 } 143 144 func (d *dbConfigCachedGetter) InvalidateCache() { 145 d.mu.Lock() 146 d.cacheTime = time.Time{} 147 d.mu.Unlock() 148 } 149 150 func NewDatabaseDynKeyGetter() config.DynKeyGetter { 151 return &dbConfigCachedGetter{} 152 }