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