mirror of https://github.com/go-gitea/gitea.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
152 lines
3.6 KiB
152 lines
3.6 KiB
// Copyright 2021 The Gitea Authors. All rights reserved. |
|
// SPDX-License-Identifier: MIT |
|
|
|
package system |
|
|
|
import ( |
|
"context" |
|
"math" |
|
"sync" |
|
"time" |
|
|
|
"code.gitea.io/gitea/models/db" |
|
"code.gitea.io/gitea/modules/log" |
|
"code.gitea.io/gitea/modules/setting/config" |
|
"code.gitea.io/gitea/modules/timeutil" |
|
|
|
"xorm.io/builder" |
|
) |
|
|
|
type Setting struct { |
|
ID int64 `xorm:"pk autoincr"` |
|
SettingKey string `xorm:"varchar(255) unique"` // key should be lowercase |
|
SettingValue string `xorm:"text"` |
|
Version int `xorm:"version"` |
|
Created timeutil.TimeStamp `xorm:"created"` |
|
Updated timeutil.TimeStamp `xorm:"updated"` |
|
} |
|
|
|
// TableName sets the table name for the settings struct |
|
func (s *Setting) TableName() string { |
|
return "system_setting" |
|
} |
|
|
|
func init() { |
|
db.RegisterModel(new(Setting)) |
|
} |
|
|
|
const keyRevision = "revision" |
|
|
|
func GetRevision(ctx context.Context) int { |
|
revision, exist, err := db.Get[Setting](ctx, builder.Eq{"setting_key": keyRevision}) |
|
if err != nil { |
|
return 0 |
|
} else if !exist { |
|
err = db.Insert(ctx, &Setting{SettingKey: keyRevision, Version: 1}) |
|
if err != nil { |
|
return 0 |
|
} |
|
return 1 |
|
} |
|
if revision.Version <= 0 || revision.Version >= math.MaxInt-1 { |
|
_, err = db.Exec(ctx, "UPDATE system_setting SET version=1 WHERE setting_key=?", keyRevision) |
|
if err != nil { |
|
return 0 |
|
} |
|
return 1 |
|
} |
|
return revision.Version |
|
} |
|
|
|
func GetAllSettings(ctx context.Context) (revision int, res map[string]string, err error) { |
|
_ = GetRevision(ctx) // prepare the "revision" key ahead |
|
var settings []*Setting |
|
if err := db.GetEngine(ctx). |
|
Find(&settings); err != nil { |
|
return 0, nil, err |
|
} |
|
res = make(map[string]string) |
|
for _, s := range settings { |
|
if s.SettingKey == keyRevision { |
|
revision = s.Version |
|
} |
|
res[s.SettingKey] = s.SettingValue |
|
} |
|
return revision, res, nil |
|
} |
|
|
|
func SetSettings(ctx context.Context, settings map[string]string) error { |
|
_ = GetRevision(ctx) // prepare the "revision" key ahead |
|
return db.WithTx(ctx, func(ctx context.Context) error { |
|
e := db.GetEngine(ctx) |
|
_, err := db.Exec(ctx, "UPDATE system_setting SET version=version+1 WHERE setting_key=?", keyRevision) |
|
if err != nil { |
|
return err |
|
} |
|
for k, v := range settings { |
|
res, err := e.Exec("UPDATE system_setting SET version=version+1, setting_value=? WHERE setting_key=?", v, k) |
|
if err != nil { |
|
return err |
|
} |
|
rows, _ := res.RowsAffected() |
|
if rows == 0 { // if no existing row, insert a new row |
|
if _, err = e.Insert(&Setting{SettingKey: k, SettingValue: v}); err != nil { |
|
return err |
|
} |
|
} |
|
} |
|
return nil |
|
}) |
|
} |
|
|
|
type dbConfigCachedGetter struct { |
|
mu sync.RWMutex |
|
|
|
cacheTime time.Time |
|
revision int |
|
settings map[string]string |
|
} |
|
|
|
var _ config.DynKeyGetter = (*dbConfigCachedGetter)(nil) |
|
|
|
func (d *dbConfigCachedGetter) GetValue(ctx context.Context, key string) (v string, has bool) { |
|
d.mu.RLock() |
|
defer d.mu.RUnlock() |
|
v, has = d.settings[key] |
|
return v, has |
|
} |
|
|
|
func (d *dbConfigCachedGetter) GetRevision(ctx context.Context) int { |
|
d.mu.RLock() |
|
cachedDuration := time.Since(d.cacheTime) |
|
cachedRevision := d.revision |
|
d.mu.RUnlock() |
|
|
|
if cachedDuration < time.Second { |
|
return cachedRevision |
|
} |
|
|
|
d.mu.Lock() |
|
defer d.mu.Unlock() |
|
if GetRevision(ctx) != d.revision { |
|
rev, set, err := GetAllSettings(ctx) |
|
if err != nil { |
|
log.Error("Unable to get all settings: %v", err) |
|
} else { |
|
d.revision = rev |
|
d.settings = set |
|
} |
|
} |
|
d.cacheTime = time.Now() |
|
return d.revision |
|
} |
|
|
|
func (d *dbConfigCachedGetter) InvalidateCache() { |
|
d.mu.Lock() |
|
d.cacheTime = time.Time{} |
|
d.mu.Unlock() |
|
} |
|
|
|
func NewDatabaseDynKeyGetter() config.DynKeyGetter { |
|
return &dbConfigCachedGetter{} |
|
}
|
|
|