mirror of
				https://github.com/go-gitea/gitea
				synced 2025-11-03 04:48:25 +00:00 
			
		
		
		
	The function `GetByBean` has an obvious defect that when the fields are
empty values, it will be ignored. Then users will get a wrong result
which is possibly used to make a security problem.
To avoid the possibility, this PR removed function `GetByBean` and all
references.
And some new generic functions have been introduced to be used.
The recommand usage like below.
```go
// if query an object according id
obj, err := db.GetByID[Object](ctx, id)
// query with other conditions
obj, err := db.Get[Object](ctx, builder.Eq{"a": a, "b":b})
```
		
	
		
			
				
	
	
		
			153 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			153 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// 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{}
 | 
						|
}
 |