You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

setting.go 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package user
  4. import (
  5. "context"
  6. "fmt"
  7. "strings"
  8. "code.gitea.io/gitea/models/db"
  9. "code.gitea.io/gitea/modules/cache"
  10. setting_module "code.gitea.io/gitea/modules/setting"
  11. "code.gitea.io/gitea/modules/util"
  12. "xorm.io/builder"
  13. )
  14. // Setting is a key value store of user settings
  15. type Setting struct {
  16. ID int64 `xorm:"pk autoincr"`
  17. UserID int64 `xorm:"index unique(key_userid)"` // to load all of someone's settings
  18. SettingKey string `xorm:"varchar(255) index unique(key_userid)"` // ensure key is always lowercase
  19. SettingValue string `xorm:"text"`
  20. }
  21. // TableName sets the table name for the settings struct
  22. func (s *Setting) TableName() string {
  23. return "user_setting"
  24. }
  25. func init() {
  26. db.RegisterModel(new(Setting))
  27. }
  28. // ErrUserSettingIsNotExist represents an error that a setting is not exist with special key
  29. type ErrUserSettingIsNotExist struct {
  30. Key string
  31. }
  32. // Error implements error
  33. func (err ErrUserSettingIsNotExist) Error() string {
  34. return fmt.Sprintf("Setting[%s] is not exist", err.Key)
  35. }
  36. func (err ErrUserSettingIsNotExist) Unwrap() error {
  37. return util.ErrNotExist
  38. }
  39. // IsErrUserSettingIsNotExist return true if err is ErrSettingIsNotExist
  40. func IsErrUserSettingIsNotExist(err error) bool {
  41. _, ok := err.(ErrUserSettingIsNotExist)
  42. return ok
  43. }
  44. // genSettingCacheKey returns the cache key for some configuration
  45. func genSettingCacheKey(userID int64, key string) string {
  46. return fmt.Sprintf("user_%d.setting.%s", userID, key)
  47. }
  48. // GetSetting returns the setting value via the key
  49. func GetSetting(ctx context.Context, uid int64, key string) (string, error) {
  50. return cache.GetString(genSettingCacheKey(uid, key), func() (string, error) {
  51. res, err := GetSettingNoCache(ctx, uid, key)
  52. if err != nil {
  53. return "", err
  54. }
  55. return res.SettingValue, nil
  56. })
  57. }
  58. // GetSettingNoCache returns specific setting without using the cache
  59. func GetSettingNoCache(ctx context.Context, uid int64, key string) (*Setting, error) {
  60. v, err := GetSettings(ctx, uid, []string{key})
  61. if err != nil {
  62. return nil, err
  63. }
  64. if len(v) == 0 {
  65. return nil, ErrUserSettingIsNotExist{key}
  66. }
  67. return v[key], nil
  68. }
  69. // GetSettings returns specific settings from user
  70. func GetSettings(ctx context.Context, uid int64, keys []string) (map[string]*Setting, error) {
  71. settings := make([]*Setting, 0, len(keys))
  72. if err := db.GetEngine(ctx).
  73. Where("user_id=?", uid).
  74. And(builder.In("setting_key", keys)).
  75. Find(&settings); err != nil {
  76. return nil, err
  77. }
  78. settingsMap := make(map[string]*Setting)
  79. for _, s := range settings {
  80. settingsMap[s.SettingKey] = s
  81. }
  82. return settingsMap, nil
  83. }
  84. // GetUserAllSettings returns all settings from user
  85. func GetUserAllSettings(ctx context.Context, uid int64) (map[string]*Setting, error) {
  86. settings := make([]*Setting, 0, 5)
  87. if err := db.GetEngine(ctx).
  88. Where("user_id=?", uid).
  89. Find(&settings); err != nil {
  90. return nil, err
  91. }
  92. settingsMap := make(map[string]*Setting)
  93. for _, s := range settings {
  94. settingsMap[s.SettingKey] = s
  95. }
  96. return settingsMap, nil
  97. }
  98. func validateUserSettingKey(key string) error {
  99. if len(key) == 0 {
  100. return fmt.Errorf("setting key must be set")
  101. }
  102. if strings.ToLower(key) != key {
  103. return fmt.Errorf("setting key should be lowercase")
  104. }
  105. return nil
  106. }
  107. // GetUserSetting gets a specific setting for a user
  108. func GetUserSetting(ctx context.Context, userID int64, key string, def ...string) (string, error) {
  109. if err := validateUserSettingKey(key); err != nil {
  110. return "", err
  111. }
  112. setting := &Setting{UserID: userID, SettingKey: key}
  113. has, err := db.GetEngine(ctx).Get(setting)
  114. if err != nil {
  115. return "", err
  116. }
  117. if !has {
  118. if len(def) == 1 {
  119. return def[0], nil
  120. }
  121. return "", nil
  122. }
  123. return setting.SettingValue, nil
  124. }
  125. // DeleteUserSetting deletes a specific setting for a user
  126. func DeleteUserSetting(ctx context.Context, userID int64, key string) error {
  127. if err := validateUserSettingKey(key); err != nil {
  128. return err
  129. }
  130. cache.Remove(genSettingCacheKey(userID, key))
  131. _, err := db.GetEngine(ctx).Delete(&Setting{UserID: userID, SettingKey: key})
  132. return err
  133. }
  134. // SetUserSetting updates a users' setting for a specific key
  135. func SetUserSetting(ctx context.Context, userID int64, key, value string) error {
  136. if err := validateUserSettingKey(key); err != nil {
  137. return err
  138. }
  139. if err := upsertUserSettingValue(ctx, userID, key, value); err != nil {
  140. return err
  141. }
  142. cc := cache.GetCache()
  143. if cc != nil {
  144. return cc.Put(genSettingCacheKey(userID, key), value, setting_module.CacheService.TTLSeconds())
  145. }
  146. return nil
  147. }
  148. func upsertUserSettingValue(ctx context.Context, userID int64, key, value string) error {
  149. return db.WithTx(ctx, func(ctx context.Context) error {
  150. e := db.GetEngine(ctx)
  151. // here we use a general method to do a safe upsert for different databases (and most transaction levels)
  152. // 1. try to UPDATE the record and acquire the transaction write lock
  153. // if UPDATE returns non-zero rows are changed, OK, the setting is saved correctly
  154. // if UPDATE returns "0 rows changed", two possibilities: (a) record doesn't exist (b) value is not changed
  155. // 2. do a SELECT to check if the row exists or not (we already have the transaction lock)
  156. // 3. if the row doesn't exist, do an INSERT (we are still protected by the transaction lock, so it's safe)
  157. //
  158. // to optimize the SELECT in step 2, we can use an extra column like `revision=revision+1`
  159. // to make sure the UPDATE always returns a non-zero value for existing (unchanged) records.
  160. res, err := e.Exec("UPDATE user_setting SET setting_value=? WHERE setting_key=? AND user_id=?", value, key, userID)
  161. if err != nil {
  162. return err
  163. }
  164. rows, _ := res.RowsAffected()
  165. if rows > 0 {
  166. // the existing row is updated, so we can return
  167. return nil
  168. }
  169. // in case the value isn't changed, update would return 0 rows changed, so we need this check
  170. has, err := e.Exist(&Setting{UserID: userID, SettingKey: key})
  171. if err != nil {
  172. return err
  173. }
  174. if has {
  175. return nil
  176. }
  177. // if no existing row, insert a new row
  178. _, err = e.Insert(&Setting{UserID: userID, SettingKey: key, SettingValue: value})
  179. return err
  180. })
  181. }