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