mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 19:38:23 +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{}
 | |
| }
 |