feat: Implement caching for settings retrieval and update logic (#11886)

* feat: Implement caching for settings retrieval and update logic

- Introduced a caching mechanism for settings using go-cache to improve performance.
- Updated the Create, Update, and UpdateOrCreate methods to cache values after database operations.
- Modified the Get and GetValueByKey methods to utilize the cache for faster access.
- Adjusted middleware to use the new GetValueByKey method for retrieving settings, enhancing code consistency.

* feat: Enhance setting cache management with dynamic TTL

- Introduced a function to determine cache TTL based on setting keys, allowing critical settings to have shorter cache durations.
- Updated cache logic in Create, Update, Get, and GetValueByKey methods to utilize the new TTL management, improving performance and consistency in settings retrieval.

* refactor: Simplify setting cache TTL management

- Removed the dynamic TTL function and standardized the cache TTL to a fixed duration for all settings.
- Updated cache logic in Create, Update, Get, and GetValueByKey methods to use the new fixed TTL, enhancing code clarity and maintainability.
- Added a new function to restart the core service after resetting settings, improving the reset command's functionality.

* refactor: Remove core restart functionality from reset commands

- Eliminated the restartCoreAfterReset function to simplify the reset command logic.
- Updated reset commands to return nil after setting changes, enhancing clarity and reducing unnecessary complexity.
This commit is contained in:
KOMATA
2026-02-25 16:40:20 +08:00
committed by GitHub
parent 924e59d948
commit 5209275f28
10 changed files with 102 additions and 35 deletions
+44 -6
View File
@@ -2,14 +2,22 @@ package repo
import (
"errors"
"time"
"github.com/1Panel-dev/1Panel/core/app/model"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/init/migration/helper"
"github.com/patrickmn/go-cache"
"gorm.io/gorm"
)
type SettingRepo struct{}
var (
settingCache = cache.New(5*time.Minute, 10*time.Minute)
settingTTL = 5 * time.Minute
)
type ISettingRepo interface {
List(opts ...global.DBOption) ([]model.Setting, error)
Get(opts ...global.DBOption) (model.Setting, error)
@@ -39,7 +47,11 @@ func (u *SettingRepo) Create(key, value string) error {
Key: key,
Value: value,
}
return global.DB.Create(setting).Error
if err := global.DB.Create(setting).Error; err != nil {
return err
}
settingCache.Set(key, value, settingTTL)
return nil
}
func (u *SettingRepo) Get(opts ...global.DBOption) (model.Setting, error) {
@@ -48,20 +60,33 @@ func (u *SettingRepo) Get(opts ...global.DBOption) (model.Setting, error) {
for _, opt := range opts {
db = opt(db)
}
err := db.First(&settings).Error
if err == nil && settings.Key != "" {
settingCache.Set(settings.Key, settings.Value, settingTTL)
}
return settings, err
}
func (u *SettingRepo) GetValueByKey(key string) (string, error) {
if val, found := settingCache.Get(key); found {
return val.(string), nil
}
var setting model.Setting
if err := global.DB.Model(&model.Setting{}).Where("key = ?", key).First(&setting).Error; err != nil {
return "", err
}
settingCache.Set(key, setting.Value, settingTTL)
return setting.Value, nil
}
func (u *SettingRepo) Update(key, value string) error {
return global.DB.Model(&model.Setting{}).Where("key = ?", key).Updates(map[string]interface{}{"value": value}).Error
if err := global.DB.Model(&model.Setting{}).Where("key = ?", key).Updates(map[string]interface{}{"value": value}).Error; err != nil {
return err
}
settingCache.Set(key, value, settingTTL)
return nil
}
func (u *SettingRepo) UpdateOrCreate(key, value string) error {
@@ -69,15 +94,28 @@ func (u *SettingRepo) UpdateOrCreate(key, value string) error {
result := global.DB.Where("key = ?", key).First(&setting)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return global.DB.Create(&model.Setting{Key: key, Value: value}).Error
if err := global.DB.Create(&model.Setting{Key: key, Value: value}).Error; err != nil {
return err
}
settingCache.Set(key, value, settingTTL)
return nil
}
return result.Error
}
return global.DB.Model(&setting).UpdateColumn("value", value).Error
if err := global.DB.Model(&setting).UpdateColumn("value", value).Error; err != nil {
return err
}
settingCache.Set(key, value, settingTTL)
return nil
}
func (u *SettingRepo) DefaultMenu() error {
return global.DB.Model(&model.Setting{}).
menus := helper.LoadMenus()
if err := global.DB.Model(&model.Setting{}).
Where("key = ?", "HideMenu").
Update("value", helper.LoadMenus()).Error
Update("value", menus).Error; err != nil {
return err
}
settingCache.Set("HideMenu", menus, settingTTL)
return nil
}
+12 -3
View File
@@ -62,7 +62,10 @@ var resetSSLCmd = &cobra.Command{
return err
}
return setSettingByKey(db, "SSL", constant.StatusDisable)
if err := setSettingByKey(db, "SSL", constant.StatusDisable); err != nil {
return err
}
return nil
},
}
var resetEntranceCmd = &cobra.Command{
@@ -94,7 +97,10 @@ var resetBindIpsCmd = &cobra.Command{
return err
}
return setSettingByKey(db, "AllowIPs", "")
if err := setSettingByKey(db, "AllowIPs", ""); err != nil {
return err
}
return nil
},
}
var resetDomainCmd = &cobra.Command{
@@ -110,7 +116,10 @@ var resetDomainCmd = &cobra.Command{
return err
}
return setSettingByKey(db, "BindDomain", "")
if err := setSettingByKey(db, "BindDomain", ""); err != nil {
return err
}
return nil
},
}
+4 -4
View File
@@ -71,16 +71,16 @@ func checkSession(c *gin.Context) bool {
return false
}
settingRepo := repo.NewISettingRepo()
setting, err := settingRepo.Get(repo.WithByKey("SessionTimeout"))
sessionTimeout, err := settingRepo.GetValueByKey("SessionTimeout")
if err != nil {
return false
}
lifeTime, _ := strconv.Atoi(setting.Value)
httpsSetting, err := settingRepo.Get(repo.WithByKey("SSL"))
lifeTime, _ := strconv.Atoi(sessionTimeout)
ssl, err := settingRepo.GetValueByKey("SSL")
if err != nil {
return false
}
_ = global.SESSION.Set(c, psession, httpsSetting.Value == constant.StatusEnable, lifeTime)
_ = global.SESSION.Set(c, psession, ssl == constant.StatusEnable, lifeTime)
return true
}
+3 -3
View File
@@ -16,12 +16,12 @@ func BindDomain() gin.HandlerFunc {
return
}
settingRepo := repo.NewISettingRepo()
status, err := settingRepo.Get(repo.WithByKey("BindDomain"))
bindDomain, err := settingRepo.GetValueByKey("BindDomain")
if err != nil {
helper.InternalServer(c, err)
return
}
if len(status.Value) == 0 {
if len(bindDomain) == 0 {
c.Next()
return
}
@@ -31,7 +31,7 @@ func BindDomain() gin.HandlerFunc {
domains = parts[0]
}
if domains != status.Value {
if domains != bindDomain {
code := LoadErrCode()
helper.ErrWithHtml(c, code, "err_domain")
return
+2 -2
View File
@@ -8,12 +8,12 @@ import (
func LoadErrCode() int {
settingRepo := repo.NewISettingRepo()
codeVal, err := settingRepo.Get(repo.WithByKey("NoAuthSetting"))
codeVal, err := settingRepo.GetValueByKey("NoAuthSetting")
if err != nil {
return 500
}
switch codeVal.Value {
switch codeVal {
case "400":
return http.StatusBadRequest
case "401":
+3 -3
View File
@@ -24,17 +24,17 @@ func WhiteAllow() gin.HandlerFunc {
}
settingRepo := repo.NewISettingRepo()
status, err := settingRepo.Get(repo.WithByKey("AllowIPs"))
allowIPs, err := settingRepo.GetValueByKey("AllowIPs")
if err != nil {
helper.InternalServer(c, err)
return
}
if len(status.Value) == 0 {
if len(allowIPs) == 0 {
c.Next()
return
}
for _, ip := range strings.Split(status.Value, ",") {
for _, ip := range strings.Split(allowIPs, ",") {
if len(ip) == 0 {
continue
}
+3 -3
View File
@@ -9,13 +9,13 @@ import (
func GlobalLoading() gin.HandlerFunc {
return func(c *gin.Context) {
settingRepo := repo.NewISettingRepo()
status, err := settingRepo.Get(repo.WithByKey("SystemStatus"))
status, err := settingRepo.GetValueByKey("SystemStatus")
if err != nil {
helper.InternalServer(c, err)
return
}
if status.Value != "Free" {
helper.ErrorWithDetail(c, 407, status.Value, err)
if status != "Free" {
helper.ErrorWithDetail(c, 407, status, err)
return
}
c.Next()
+25 -5
View File
@@ -4,6 +4,7 @@ import (
"net/http"
"strconv"
"strings"
"sync"
"time"
"github.com/1Panel-dev/1Panel/core/app/api/v2/helper"
@@ -13,6 +14,11 @@ import (
"github.com/gin-gonic/gin"
)
var (
expiredLoc *time.Location
expiredLocOnce sync.Once
)
func PasswordExpired() gin.HandlerFunc {
return func(c *gin.Context) {
if strings.HasPrefix(c.Request.URL.Path, "/api/v2/core/auth") ||
@@ -22,24 +28,23 @@ func PasswordExpired() gin.HandlerFunc {
return
}
settingRepo := repo.NewISettingRepo()
setting, err := settingRepo.Get(repo.WithByKey("ExpirationDays"))
expirationDays, err := settingRepo.GetValueByKey("ExpirationDays")
if err != nil {
helper.ErrorWithDetail(c, http.StatusInternalServerError, "ErrPasswordExpired", err)
return
}
expiredDays, _ := strconv.Atoi(setting.Value)
expiredDays, _ := strconv.Atoi(expirationDays)
if expiredDays == 0 {
c.Next()
return
}
extime, err := settingRepo.Get(repo.WithByKey("ExpirationTime"))
expirationTime, err := settingRepo.GetValueByKey("ExpirationTime")
if err != nil {
helper.ErrorWithDetail(c, http.StatusInternalServerError, "ErrPasswordExpired", err)
return
}
loc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
expiredTime, err := time.ParseInLocation(constant.DateTimeLayout, extime.Value, loc)
expiredTime, err := time.ParseInLocation(constant.DateTimeLayout, expirationTime, loadExpiredLocation())
if err != nil {
helper.ErrorWithDetail(c, 313, "ErrPasswordExpired", err)
return
@@ -51,3 +56,18 @@ func PasswordExpired() gin.HandlerFunc {
c.Next()
}
}
func loadExpiredLocation() *time.Location {
expiredLocOnce.Do(func() {
loc, err := time.LoadLocation(common.LoadTimeZoneByCmd())
if err != nil {
expiredLoc = time.Local
return
}
expiredLoc = loc
})
if expiredLoc == nil {
return time.Local
}
return expiredLoc
}
+2 -2
View File
@@ -10,8 +10,8 @@ func SetPasswordPublicKey() gin.HandlerFunc {
return func(c *gin.Context) {
cookieKey, _ := c.Cookie("panel_public_key")
settingRepo := repo.NewISettingRepo()
key, _ := settingRepo.Get(repo.WithByKey("PASSWORD_PUBLIC_KEY"))
base64Key := base64.StdEncoding.EncodeToString([]byte(key.Value))
key, _ := settingRepo.GetValueByKey("PASSWORD_PUBLIC_KEY")
base64Key := base64.StdEncoding.EncodeToString([]byte(key))
if base64Key == cookieKey {
c.Next()
return
+4 -4
View File
@@ -31,18 +31,18 @@ func SessionAuth() gin.HandlerFunc {
return
}
settingRepo := repo.NewISettingRepo()
setting, err := settingRepo.Get(repo.WithByKey("SessionTimeout"))
sessionTimeout, err := settingRepo.GetValueByKey("SessionTimeout")
if err != nil {
global.LOG.Errorf("create operation record failed, err: %v", err)
return
}
lifeTime, _ := strconv.Atoi(setting.Value)
httpsSetting, err := settingRepo.Get(repo.WithByKey("SSL"))
lifeTime, _ := strconv.Atoi(sessionTimeout)
ssl, err := settingRepo.GetValueByKey("SSL")
if err != nil {
global.LOG.Errorf("create operation record failed, err: %v", err)
return
}
_ = global.SESSION.Set(c, psession, httpsSetting.Value == constant.StatusEnable, lifeTime)
_ = global.SESSION.Set(c, psession, ssl == constant.StatusEnable, lifeTime)
c.Next()
}
}