From 99311f398dbcfceaebb53eeb43aa0d416a3179d1 Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Fri, 6 Feb 2026 12:32:30 +0800 Subject: [PATCH 1/3] 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. --- core/app/repo/setting.go | 50 +++++++++++++++++++++++++---- core/init/router/proxy.go | 8 ++--- core/middleware/bind_domain.go | 6 ++-- core/middleware/helper.go | 4 +-- core/middleware/ip_limit.go | 6 ++-- core/middleware/loading.go | 6 ++-- core/middleware/password_expired.go | 30 ++++++++++++++--- core/middleware/password_rsa.go | 4 +-- core/middleware/session.go | 8 ++--- 9 files changed, 90 insertions(+), 32 deletions(-) diff --git a/core/app/repo/setting.go b/core/app/repo/setting.go index 8d41c5f2cfce..d09657990003 100644 --- a/core/app/repo/setting.go +++ b/core/app/repo/setting.go @@ -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) + settingCacheTTL = 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, settingCacheTTL) + 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, settingCacheTTL) + } 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, settingCacheTTL) 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, settingCacheTTL) + 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, settingCacheTTL) + 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, settingCacheTTL) + 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, settingCacheTTL) + return nil } diff --git a/core/init/router/proxy.go b/core/init/router/proxy.go index 783c1917dcfe..49fdd188311b 100644 --- a/core/init/router/proxy.go +++ b/core/init/router/proxy.go @@ -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 } diff --git a/core/middleware/bind_domain.go b/core/middleware/bind_domain.go index ad85708f9062..b70ea29bc47b 100644 --- a/core/middleware/bind_domain.go +++ b/core/middleware/bind_domain.go @@ -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 diff --git a/core/middleware/helper.go b/core/middleware/helper.go index b115b34127b1..7ab34a8f8c9e 100644 --- a/core/middleware/helper.go +++ b/core/middleware/helper.go @@ -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": diff --git a/core/middleware/ip_limit.go b/core/middleware/ip_limit.go index d2f900a821af..5689157c6a5b 100644 --- a/core/middleware/ip_limit.go +++ b/core/middleware/ip_limit.go @@ -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 } diff --git a/core/middleware/loading.go b/core/middleware/loading.go index afa14a648f40..975c1cc497a3 100644 --- a/core/middleware/loading.go +++ b/core/middleware/loading.go @@ -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() diff --git a/core/middleware/password_expired.go b/core/middleware/password_expired.go index ebd8e0bcddcb..5808be10713f 100644 --- a/core/middleware/password_expired.go +++ b/core/middleware/password_expired.go @@ -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 +} diff --git a/core/middleware/password_rsa.go b/core/middleware/password_rsa.go index 6548562107bf..9f27dbfbafe7 100644 --- a/core/middleware/password_rsa.go +++ b/core/middleware/password_rsa.go @@ -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 diff --git a/core/middleware/session.go b/core/middleware/session.go index 979993f5c2e1..a0c587edf298 100644 --- a/core/middleware/session.go +++ b/core/middleware/session.go @@ -24,18 +24,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() } } From 024b05af8ad1c4547c6bb8bf0ed828063ae140c9 Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Fri, 6 Feb 2026 13:12:43 +0800 Subject: [PATCH 2/3] 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. --- core/app/repo/setting.go | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/core/app/repo/setting.go b/core/app/repo/setting.go index d09657990003..63ed41b699e0 100644 --- a/core/app/repo/setting.go +++ b/core/app/repo/setting.go @@ -14,8 +14,9 @@ import ( type SettingRepo struct{} var ( - settingCache = cache.New(5*time.Minute, 10*time.Minute) - settingCacheTTL = 5 * time.Minute + settingCache = cache.New(5*time.Minute, 10*time.Minute) + settingDefaultTTL = 5 * time.Minute + settingCriticalTTL = 5 * time.Second ) type ISettingRepo interface { @@ -32,6 +33,14 @@ func NewISettingRepo() ISettingRepo { return &SettingRepo{} } +func loadSettingTTL(key string) time.Duration { + switch key { + case "AllowIPs", "BindDomain", "SSL": + return settingCriticalTTL + } + return settingDefaultTTL +} + func (u *SettingRepo) List(opts ...global.DBOption) ([]model.Setting, error) { var settings []model.Setting db := global.DB.Model(&model.Setting{}) @@ -50,7 +59,7 @@ func (u *SettingRepo) Create(key, value string) error { if err := global.DB.Create(setting).Error; err != nil { return err } - settingCache.Set(key, value, settingCacheTTL) + settingCache.Set(key, value, loadSettingTTL(key)) return nil } @@ -63,7 +72,7 @@ func (u *SettingRepo) Get(opts ...global.DBOption) (model.Setting, error) { err := db.First(&settings).Error if err == nil && settings.Key != "" { - settingCache.Set(settings.Key, settings.Value, settingCacheTTL) + settingCache.Set(settings.Key, settings.Value, loadSettingTTL(settings.Key)) } return settings, err } @@ -77,7 +86,7 @@ func (u *SettingRepo) GetValueByKey(key string) (string, error) { if err := global.DB.Model(&model.Setting{}).Where("key = ?", key).First(&setting).Error; err != nil { return "", err } - settingCache.Set(key, setting.Value, settingCacheTTL) + settingCache.Set(key, setting.Value, loadSettingTTL(key)) return setting.Value, nil } @@ -85,7 +94,7 @@ func (u *SettingRepo) Update(key, value string) 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, settingCacheTTL) + settingCache.Set(key, value, loadSettingTTL(key)) return nil } @@ -97,7 +106,7 @@ func (u *SettingRepo) UpdateOrCreate(key, value string) error { if err := global.DB.Create(&model.Setting{Key: key, Value: value}).Error; err != nil { return err } - settingCache.Set(key, value, settingCacheTTL) + settingCache.Set(key, value, loadSettingTTL(key)) return nil } return result.Error @@ -105,7 +114,7 @@ func (u *SettingRepo) UpdateOrCreate(key, value string) error { if err := global.DB.Model(&setting).UpdateColumn("value", value).Error; err != nil { return err } - settingCache.Set(key, value, settingCacheTTL) + settingCache.Set(key, value, loadSettingTTL(key)) return nil } @@ -116,6 +125,6 @@ func (u *SettingRepo) DefaultMenu() error { Update("value", menus).Error; err != nil { return err } - settingCache.Set("HideMenu", menus, settingCacheTTL) + settingCache.Set("HideMenu", menus, settingDefaultTTL) return nil } From 9365ea02906444f287f22a1b44cff6247b9526a3 Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Sat, 7 Feb 2026 09:36:50 +0800 Subject: [PATCH 3/3] 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. --- core/app/repo/setting.go | 27 +++++++++------------------ core/cmd/server/cmd/reset.go | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/core/app/repo/setting.go b/core/app/repo/setting.go index 63ed41b699e0..5f74819ef981 100644 --- a/core/app/repo/setting.go +++ b/core/app/repo/setting.go @@ -14,9 +14,8 @@ import ( type SettingRepo struct{} var ( - settingCache = cache.New(5*time.Minute, 10*time.Minute) - settingDefaultTTL = 5 * time.Minute - settingCriticalTTL = 5 * time.Second + settingCache = cache.New(5*time.Minute, 10*time.Minute) + settingTTL = 5 * time.Minute ) type ISettingRepo interface { @@ -33,14 +32,6 @@ func NewISettingRepo() ISettingRepo { return &SettingRepo{} } -func loadSettingTTL(key string) time.Duration { - switch key { - case "AllowIPs", "BindDomain", "SSL": - return settingCriticalTTL - } - return settingDefaultTTL -} - func (u *SettingRepo) List(opts ...global.DBOption) ([]model.Setting, error) { var settings []model.Setting db := global.DB.Model(&model.Setting{}) @@ -59,7 +50,7 @@ func (u *SettingRepo) Create(key, value string) error { if err := global.DB.Create(setting).Error; err != nil { return err } - settingCache.Set(key, value, loadSettingTTL(key)) + settingCache.Set(key, value, settingTTL) return nil } @@ -72,7 +63,7 @@ func (u *SettingRepo) Get(opts ...global.DBOption) (model.Setting, error) { err := db.First(&settings).Error if err == nil && settings.Key != "" { - settingCache.Set(settings.Key, settings.Value, loadSettingTTL(settings.Key)) + settingCache.Set(settings.Key, settings.Value, settingTTL) } return settings, err } @@ -86,7 +77,7 @@ func (u *SettingRepo) GetValueByKey(key string) (string, error) { if err := global.DB.Model(&model.Setting{}).Where("key = ?", key).First(&setting).Error; err != nil { return "", err } - settingCache.Set(key, setting.Value, loadSettingTTL(key)) + settingCache.Set(key, setting.Value, settingTTL) return setting.Value, nil } @@ -94,7 +85,7 @@ func (u *SettingRepo) Update(key, value string) 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, loadSettingTTL(key)) + settingCache.Set(key, value, settingTTL) return nil } @@ -106,7 +97,7 @@ func (u *SettingRepo) UpdateOrCreate(key, value string) error { if err := global.DB.Create(&model.Setting{Key: key, Value: value}).Error; err != nil { return err } - settingCache.Set(key, value, loadSettingTTL(key)) + settingCache.Set(key, value, settingTTL) return nil } return result.Error @@ -114,7 +105,7 @@ func (u *SettingRepo) UpdateOrCreate(key, value string) error { if err := global.DB.Model(&setting).UpdateColumn("value", value).Error; err != nil { return err } - settingCache.Set(key, value, loadSettingTTL(key)) + settingCache.Set(key, value, settingTTL) return nil } @@ -125,6 +116,6 @@ func (u *SettingRepo) DefaultMenu() error { Update("value", menus).Error; err != nil { return err } - settingCache.Set("HideMenu", menus, settingDefaultTTL) + settingCache.Set("HideMenu", menus, settingTTL) return nil } diff --git a/core/cmd/server/cmd/reset.go b/core/cmd/server/cmd/reset.go index de29fb14485e..1070ab9c665e 100644 --- a/core/cmd/server/cmd/reset.go +++ b/core/cmd/server/cmd/reset.go @@ -2,9 +2,11 @@ package cmd import ( "fmt" + "strings" "github.com/1Panel-dev/1Panel/core/constant" "github.com/1Panel-dev/1Panel/core/i18n" + cmdUtils "github.com/1Panel-dev/1Panel/core/utils/cmd" "github.com/1Panel-dev/1Panel/core/utils/passkey" "github.com/spf13/cobra" ) @@ -62,7 +64,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 restartCoreAfterReset() }, } var resetEntranceCmd = &cobra.Command{ @@ -94,7 +99,10 @@ var resetBindIpsCmd = &cobra.Command{ return err } - return setSettingByKey(db, "AllowIPs", "") + if err := setSettingByKey(db, "AllowIPs", ""); err != nil { + return err + } + return restartCoreAfterReset() }, } var resetDomainCmd = &cobra.Command{ @@ -110,7 +118,10 @@ var resetDomainCmd = &cobra.Command{ return err } - return setSettingByKey(db, "BindDomain", "") + if err := setSettingByKey(db, "BindDomain", ""); err != nil { + return err + } + return restartCoreAfterReset() }, } @@ -133,6 +144,22 @@ var resetPasskeyCmd = &cobra.Command{ }, } +func restartCoreAfterReset() error { + stdout, err := cmdUtils.RunDefaultWithStdoutBashC("1pctl restart core") + if len(stdout) != 0 { + fmt.Print(stdout) + } + if err == nil { + return nil + } + + stdout = strings.TrimSpace(stdout) + if len(stdout) != 0 { + return fmt.Errorf("reset succeeded but restart core failed: %s", stdout) + } + return fmt.Errorf("reset succeeded but restart core failed: %v", err) +} + func loadResetHelper() { fmt.Println(i18n.GetMsgByKeyForCmd("ResetCommands")) fmt.Println("\nUsage:\n 1panel reset [command]\n\nAvailable Commands:")