152 lines · 3.5 KB · Go
RawDownload
// Copyright 2021 The Gitea Authors. All rights reserved.// SPDX-License-Identifier: MIT package system import (	"context"	"math"	"sync"	"time" 	"forgejo.org/models/db"	"forgejo.org/modules/log"	"forgejo.org/modules/setting/config"	"forgejo.org/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	Created      timeutil.TimeStamp `xorm:"created"`	Updated      timeutil.TimeStamp `xorm:"updated"`} // TableName sets the table name for the settings structfunc (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{}}