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