diff --git a/agent/app/service/app.go b/agent/app/service/app.go
index e96a5a51f..409679e38 100644
--- a/agent/app/service/app.go
+++ b/agent/app/service/app.go
@@ -126,7 +126,7 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (*response.
lang := strings.ToLower(common.GetLang(ctx))
for _, ap := range apps {
if req.Type == "php" {
- if !global.CONF.Base.IsOffLine && (ap.RequiredPanelVersion == 0 || !common.CompareAppVersion(common.GetSystemVersion(info.SystemVersion), fmt.Sprintf("%f", ap.RequiredPanelVersion))) {
+ if !global.CONF.Base.IsOffline && (ap.RequiredPanelVersion == 0 || !common.CompareAppVersion(common.GetSystemVersion(info.SystemVersion), fmt.Sprintf("%f", ap.RequiredPanelVersion))) {
continue
}
}
diff --git a/agent/app/service/mcp_server.go b/agent/app/service/mcp_server.go
index 615d3bd14..47e0c1717 100644
--- a/agent/app/service/mcp_server.go
+++ b/agent/app/service/mcp_server.go
@@ -681,7 +681,7 @@ func GetWebsiteID() uint {
}
func pullImage(imageType string) {
- if global.CONF.Base.IsOffLine {
+ if global.CONF.Base.IsOffline {
return
}
if imageType == "npx" {
diff --git a/agent/global/config.go b/agent/global/config.go
index 057c48443..9f877fe1f 100644
--- a/agent/global/config.go
+++ b/agent/global/config.go
@@ -15,7 +15,7 @@ type Base struct {
Mode string `mapstructure:"mode"` // xpack [ Enable / Disable ]
IsDemo bool `mapstructure:"is_demo"`
InstallDir string `mapstructure:"install_dir"`
- IsOffLine bool `mapstructure:"is_offline"`
+ IsOffline bool `mapstructure:"is_offline"`
}
type SystemDir struct {
diff --git a/agent/init/business/business.go b/agent/init/business/business.go
index 2e9e8576b..d6d922aa2 100644
--- a/agent/init/business/business.go
+++ b/agent/init/business/business.go
@@ -20,7 +20,7 @@ func Init() {
}
func syncApp() {
- if global.CONF.Base.IsOffLine {
+ if global.CONF.Base.IsOffline {
return
}
setting, err := service.NewISettingService().GetSettingInfo()
diff --git a/core/app/api/v2/auth.go b/core/app/api/v2/auth.go
index 943f0dce4..7f4e9de99 100644
--- a/core/app/api/v2/auth.go
+++ b/core/app/api/v2/auth.go
@@ -241,7 +241,7 @@ func (b *BaseApi) GetLoginSetting(c *gin.Context) {
IsDemo: global.CONF.Base.IsDemo,
IsIntl: global.CONF.Base.Edition == "intl",
IsFxplay: global.CONF.Base.IsFxplay,
- IsOffLine: global.CONF.Base.IsOffLine,
+ IsOffline: global.CONF.Base.IsOffline || settingInfo.IsOffline == constant.StatusEnable,
IsEnterprise: global.CONF.Base.IsEnterprise,
Language: settingInfo.Language,
MenuTabs: settingInfo.MenuTabs,
diff --git a/core/app/dto/auth.go b/core/app/dto/auth.go
index 84772df16..b081f5172 100644
--- a/core/app/dto/auth.go
+++ b/core/app/dto/auth.go
@@ -51,3 +51,26 @@ type SystemSetting struct {
type PasskeyID struct {
ID string `json:"id" validate:"required"`
}
+
+type CurrentUserInfo struct {
+ Name string `json:"name"`
+ SessionTimeout int `json:"sessionTimeout"`
+ MFAStatus string `json:"mfaStatus"`
+ MFAInterval string `json:"mfaInterval"`
+ ExpirationDays int `json:"expirationDays"`
+ ExpirationTime string `json:"expirationTime"`
+ ComplexitySetting string `json:"complexitySetting"`
+
+ ApiInterfaceStatus string `json:"apiInterfaceStatus"`
+ ApiKey string `json:"apiKey"`
+ IpWhiteList string `json:"ipWhiteList"`
+ ApiKeyValidityTime string `json:"apiKeyValidityTime"`
+}
+type CurrentUserUpdate struct {
+ Name string `json:"name" validate:"required"`
+ Password string `json:"password"`
+ OldPassword string `json:"oldPassword"`
+ SessionTimeout int `json:"sessionTimeout" validate:"required,min=300,max=864000"`
+ ExpirationDays int `json:"expirationDays" validate:"min=0,max=60"`
+ ExpirationTime string `json:"expirationTime"`
+}
diff --git a/core/app/dto/setting.go b/core/app/dto/setting.go
index e2084f161..087913a44 100644
--- a/core/app/dto/setting.go
+++ b/core/app/dto/setting.go
@@ -18,6 +18,7 @@ type SettingInfo struct {
MenuTabs string `json:"menuTabs"`
Language string `json:"language"`
DocSource string `json:"docSource"`
+ IsOffline string `json:"isOffline"`
ServerPort string `json:"serverPort"`
SSL string `json:"ssl"`
@@ -73,35 +74,12 @@ type SettingBaseInfo struct {
DashboardSimpleNodeVisible string `json:"dashboardSimpleNodeVisible"`
}
-type CurrentUserInfo struct {
- Name string `json:"name"`
- SessionTimeout int `json:"sessionTimeout"`
- MFAStatus string `json:"mfaStatus"`
- MFAInterval string `json:"mfaInterval"`
- ExpirationDays int `json:"expirationDays"`
- ExpirationTime string `json:"expirationTime"`
- ComplexitySetting string `json:"complexitySetting"`
-
- ApiInterfaceStatus string `json:"apiInterfaceStatus"`
- ApiKey string `json:"apiKey"`
- IpWhiteList string `json:"ipWhiteList"`
- ApiKeyValidityTime string `json:"apiKeyValidityTime"`
-}
-type CurrentUserUpdate struct {
- Name string `json:"name" validate:"required"`
- Password string `json:"password"`
- OldPassword string `json:"oldPassword"`
- SessionTimeout int `json:"sessionTimeout" validate:"required,min=300,max=864000"`
- ExpirationDays int `json:"expirationDays" validate:"min=0,max=60"`
- ExpirationTime string `json:"expirationTime"`
-}
-
type SettingKey struct {
Key string `json:"key" validate:"required,oneof=ScriptSync"`
}
type SettingUpdate struct {
- Key string `json:"key" validate:"required"`
+ Key string `json:"key" validate:"required,base_setting_key"`
Value string `json:"value"`
}
@@ -291,7 +269,7 @@ type AppstoreConfig struct {
type LoginSetting struct {
IsDemo bool `json:"isDemo"`
IsIntl bool `json:"isIntl"`
- IsOffLine bool `json:"isOffLine"`
+ IsOffline bool `json:"isOffline"`
IsFxplay bool `json:"isFxplay"`
IsEnterprise bool `json:"isEnterprise"`
Language string `json:"language"`
diff --git a/core/app/service/script_library.go b/core/app/service/script_library.go
index 3fb3914d0..239bea660 100644
--- a/core/app/service/script_library.go
+++ b/core/app/service/script_library.go
@@ -172,7 +172,7 @@ func StartSync() {
}
service := NewIScriptService()
scriptSync, _ := repo.NewISettingRepo().GetValueByKey("ScriptSync")
- if !global.CONF.Base.IsOffLine && scriptSync == constant.StatusEnable {
+ if !global.CONF.Base.IsOffline && scriptSync == constant.StatusEnable {
minuteRand, err := rand.Int(rand.Reader, big.NewInt(60))
if err != nil {
global.LOG.Errorf("generate random minute failed: %v", err)
@@ -202,7 +202,7 @@ func LoadScriptInfo(id uint) (model.ScriptLibrary, error) {
}
func (u *ScriptService) Sync(req dto.OperateByTaskID) error {
- if global.CONF.Base.IsOffLine {
+ if global.CONF.Base.IsOffline {
return nil
}
syncTask, err := task.NewTaskWithOps(i18n.GetMsgByKey("RemoteScriptLibrary"), task.TaskSync, task.TaskScopeScript, req.TaskID, 0)
diff --git a/core/app/service/setting.go b/core/app/service/setting.go
index 8cc7577f8..040f52459 100644
--- a/core/app/service/setting.go
+++ b/core/app/service/setting.go
@@ -106,6 +106,9 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
} else {
info.ProxyPasswd, _ = encrypt.StringDecrypt(info.ProxyPasswd)
}
+ if !global.CONF.Base.IsEnterprise {
+ info.IsOffline = constant.StatusDisable
+ }
return &info, err
}
@@ -167,6 +170,10 @@ func (u *SettingService) Update(c *gin.Context, key, value string) error {
return nil
}
switch key {
+ case "IsOffline":
+ if !global.CONF.Base.IsEnterprise {
+ return buserr.New("ErrNotSupportInEnterpriseEdition")
+ }
case "AppStoreLastModified":
exist, _ := settingRepo.Get(repo.WithByKey("AppStoreLastModified"))
if exist.ID == 0 {
diff --git a/core/app/service/upgrade.go b/core/app/service/upgrade.go
index 8f44006cb..5bfa914e6 100644
--- a/core/app/service/upgrade.go
+++ b/core/app/service/upgrade.go
@@ -87,7 +87,7 @@ func NewIUpgradeService() IUpgradeService {
}
func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) {
- if global.CONF.Base.IsOffLine {
+ if global.CONF.Base.IsOffline {
return &dto.UpgradeInfo{}, nil
}
var upgrade dto.UpgradeInfo
diff --git a/core/global/config.go b/core/global/config.go
index f6c2903d6..c1a0e34af 100644
--- a/core/global/config.go
+++ b/core/global/config.go
@@ -12,7 +12,7 @@ type Base struct {
Password string `mapstructure:"password"`
Language string `mapstructure:"language"`
IsDemo bool `mapstructure:"is_demo"`
- IsOffLine bool `mapstructure:"is_offline"`
+ IsOffline bool `mapstructure:"is_offline"`
IsFxplay bool `mapstructure:"is_fxplay"`
Edition string `mapstructure:"edition"`
IsEnterprise bool `mapstructure:"is_enterprise"`
diff --git a/core/init/migration/migrate.go b/core/init/migration/migrate.go
index 53f2bb5eb..b46c99708 100644
--- a/core/init/migration/migrate.go
+++ b/core/init/migration/migrate.go
@@ -40,6 +40,7 @@ func Init() {
migrations.AddAppStoreInstallAllowPortSetting,
migrations.AddUserManagementMenu,
migrations.AddOperationLogUser,
+ migrations.AddIsOfflineSetting,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
diff --git a/core/init/migration/migrations/init.go b/core/init/migration/migrations/init.go
index e0cbe8e89..e2b323fdf 100644
--- a/core/init/migration/migrations/init.go
+++ b/core/init/migration/migrations/init.go
@@ -1048,3 +1048,13 @@ var AddOperationLogUser = &gormigrate.Migration{
return tx.AutoMigrate(&model.OperationLog{})
},
}
+
+var AddIsOfflineSetting = &gormigrate.Migration{
+ ID: "20260429-add-is-offline-setting",
+ Migrate: func(tx *gorm.DB) error {
+ if err := tx.Create(&model.Setting{Key: "IsOffline", Value: constant.StatusDisable}).Error; err != nil {
+ return err
+ }
+ return nil
+ },
+}
diff --git a/core/init/run/run.go b/core/init/run/run.go
index 56cb18cda..687785e35 100644
--- a/core/init/run/run.go
+++ b/core/init/run/run.go
@@ -10,7 +10,7 @@ import (
func Init() {
scriptSync, _ := repo.NewISettingRepo().GetValueByKey("ScriptSync")
- if !global.CONF.Base.IsOffLine && scriptSync == constant.StatusEnable {
+ if !global.CONF.Base.IsOffline && scriptSync == constant.StatusEnable {
if err := service.NewIScriptService().Sync(dto.OperateByTaskID{}); err != nil {
global.LOG.Errorf("sync scripts from remote failed, err: %v", err)
}
diff --git a/core/init/validator/validator.go b/core/init/validator/validator.go
index 06cb69432..a9c5883e7 100644
--- a/core/init/validator/validator.go
+++ b/core/init/validator/validator.go
@@ -20,9 +20,36 @@ func Init() {
if err := validator.RegisterValidation("password", checkPasswordPattern); err != nil {
panic(err)
}
+ if err := validator.RegisterValidation("base_setting_key", checkBaseSettingKey); err != nil {
+ panic(err)
+ }
global.VALID = validator
}
+var baseSettingKeys = map[string]struct{}{
+ "PanelName": {},
+ "Theme": {},
+ "MenuTabs": {},
+ "Language": {},
+ "DeveloperMode": {},
+ "UpgradeBackupCopies": {},
+ "SecurityEntrance": {},
+ "BindDomain": {},
+ "AllowIPs": {},
+ "PasskeyTrustedProxies": {},
+ "ComplexityVerification": {},
+ "NoAuthSetting": {},
+ "DashboardMemoVisible": {},
+ "DashboardSimpleNodeVisible": {},
+ "MFAStatus": {},
+ "Edition": {},
+ "DocSource": {},
+ "IsOffline": {},
+ "AppStoreLastModified": {},
+ "ScriptSync": {},
+ "HideMenu": {},
+}
+
func checkNamePattern(fl validator.FieldLevel) bool {
value := fl.Field().String()
result, err := regexp.MatchString("^[a-zA-Z\u4e00-\u9fa5]{1}[a-zA-Z0-9_\u4e00-\u9fa5]{0,30}$", value)
@@ -63,3 +90,8 @@ func checkPasswordPattern(fl validator.FieldLevel) bool {
return false
}
+
+func checkBaseSettingKey(fl validator.FieldLevel) bool {
+ _, ok := baseSettingKeys[fl.Field().String()]
+ return ok
+}
diff --git a/core/init/viper/viper.go b/core/init/viper/viper.go
index 1762dad7a..9baf3b1e0 100644
--- a/core/init/viper/viper.go
+++ b/core/init/viper/viper.go
@@ -87,7 +87,7 @@ func Init() {
global.CONF.Base.InstallDir = baseDir
global.CONF.Base.IsDemo = v.GetBool("base.is_demo")
global.CONF.Base.IsFxplay = v.GetBool("base.is_fxplay")
- global.CONF.Base.IsOffLine = v.GetBool("base.is_offline")
+ global.CONF.Base.IsOffline = v.GetBool("base.is_offline")
if edition == "intl" {
global.CONF.Base.Edition = "intl"
} else {
diff --git a/frontend/src/api/interface/auth.ts b/frontend/src/api/interface/auth.ts
index 05da35846..63a87c2d5 100644
--- a/frontend/src/api/interface/auth.ts
+++ b/frontend/src/api/interface/auth.ts
@@ -39,7 +39,7 @@ export namespace Login {
menuTabs: string;
panelName: string;
theme: string;
- isOffLine: boolean;
+ isOffline: boolean;
isEnterprise: boolean;
needCaptcha: boolean;
passkeySetting: boolean;
diff --git a/frontend/src/api/interface/setting.ts b/frontend/src/api/interface/setting.ts
index 27b3fce81..868664b5b 100644
--- a/frontend/src/api/interface/setting.ts
+++ b/frontend/src/api/interface/setting.ts
@@ -39,6 +39,7 @@ export namespace Setting {
menuTabs: string;
language: string;
docSource: string;
+ isOffline: string;
serverPort: number;
ipv6: string;
diff --git a/frontend/src/components/system-upgrade/index.vue b/frontend/src/components/system-upgrade/index.vue
index ff0a90a80..f3d462b28 100644
--- a/frontend/src/components/system-upgrade/index.vue
+++ b/frontend/src/components/system-upgrade/index.vue
@@ -28,6 +28,7 @@
{{ $t('license.pro') }}
+
@@ -44,7 +45,7 @@
@@ -73,7 +74,7 @@ import { GlobalStore } from '@/store';
import { storeToRefs } from 'pinia';
const globalStore = GlobalStore();
-const { docsUrl, isOffLine, isFxplay } = storeToRefs(globalStore);
+const { docsUrl, isOffline, isFxplay } = storeToRefs(globalStore);
const upgradeRef = ref();
const releasesRef = ref();
const isMasterPro = computed(() => {
@@ -107,6 +108,10 @@ const getVersionLog = () => {
};
const toLxware = () => {
+ if (isOffline.value) {
+ to1Panel();
+ return;
+ }
if (!globalStore.isIntl) {
window.open('https://www.lxware.cn/1panel' + '', '_blank', 'noopener,noreferrer');
} else {
diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts
index 77d3b55dd..bb5011a86 100644
--- a/frontend/src/lang/modules/en.ts
+++ b/frontend/src/lang/modules/en.ts
@@ -2230,6 +2230,7 @@ const message = {
auto: 'Follow System',
language: 'Language',
runtimeEnv: 'Runtime environment',
+ offlineEnv: 'Offline environment',
docSource: 'Documentation Source',
withByRegion: 'Match Region Setting (Default)',
withByLang: 'Match System Language',
diff --git a/frontend/src/lang/modules/es-es.ts b/frontend/src/lang/modules/es-es.ts
index 581f31040..88698e744 100644
--- a/frontend/src/lang/modules/es-es.ts
+++ b/frontend/src/lang/modules/es-es.ts
@@ -2270,6 +2270,7 @@ const message = {
auto: 'Seguir sistema',
language: 'Idioma',
runtimeEnv: 'Entorno de ejecución',
+ offlineEnv: 'Entorno sin conexión',
docSource: 'Fuente de documentación',
withByRegion: 'Seguir región de operación (Predeterminado)',
withByLang: 'Seguir idioma del sistema',
diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts
index bbf69a732..2d6f686f6 100644
--- a/frontend/src/lang/modules/ja.ts
+++ b/frontend/src/lang/modules/ja.ts
@@ -2235,6 +2235,7 @@ const message = {
auto: 'システムをフォローします',
language: '言語',
runtimeEnv: '実行環境',
+ offlineEnv: 'オフライン環境',
docSource: 'ドキュメントの参照先',
withByRegion: '運用リージョンに従う(デフォルト)',
withByLang: 'システム言語に従う',
diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts
index 106b2c5ca..94ead14d9 100644
--- a/frontend/src/lang/modules/ko.ts
+++ b/frontend/src/lang/modules/ko.ts
@@ -2195,6 +2195,7 @@ const message = {
auto: '시스템 따라가기',
language: '언어',
runtimeEnv: '실행 환경',
+ offlineEnv: '오프라인 환경',
docSource: '문서 출처',
withByRegion: '운영 지역 따름(기본값)',
withByLang: '시스템 언어 따름',
diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts
index a9b59021c..7ccaa75ed 100644
--- a/frontend/src/lang/modules/ms.ts
+++ b/frontend/src/lang/modules/ms.ts
@@ -2275,6 +2275,7 @@ const message = {
auto: 'Ikut Sistem',
language: 'Bahasa',
runtimeEnv: 'Persekitaran operasi',
+ offlineEnv: 'Persekitaran luar talian',
docSource: 'Sumber dokumentasi',
withByRegion: 'Ikut wilayah operasi (Lalai)',
withByLang: 'Ikut bahasa sistem',
diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts
index 188b33c20..f4293baae 100644
--- a/frontend/src/lang/modules/pt-br.ts
+++ b/frontend/src/lang/modules/pt-br.ts
@@ -2387,6 +2387,7 @@ const message = {
auto: 'Seguir o sistema',
language: 'Idioma',
runtimeEnv: 'Ambiente de execução',
+ offlineEnv: 'Ambiente offline',
docSource: 'Fonte da documentação',
withByRegion: 'Seguir região de operação (Padrão)',
withByLang: 'Seguir idioma do sistema',
diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts
index 6c3066121..4285a5383 100644
--- a/frontend/src/lang/modules/ru.ts
+++ b/frontend/src/lang/modules/ru.ts
@@ -2252,6 +2252,7 @@ const message = {
auto: 'Как в системе',
language: 'Язык',
runtimeEnv: 'Среда выполнения',
+ offlineEnv: 'Офлайн-среда',
docSource: 'Источник документации',
withByRegion: 'Следовать региону работы (по умолчанию)',
withByLang: 'Следовать языку системы',
diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts
index 8bef79c99..ea07ee84a 100644
--- a/frontend/src/lang/modules/tr.ts
+++ b/frontend/src/lang/modules/tr.ts
@@ -2266,6 +2266,7 @@ const message = {
auto: 'Sistemi takip et',
language: 'Dil',
runtimeEnv: 'Çalışma ortamı',
+ offlineEnv: 'Çevrimdışı ortam',
docSource: 'Dokümantasyon kaynağı',
withByRegion: 'Çalışma bölgesini takip et (Varsayılan)',
withByLang: 'Sistem dilini takip et',
diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts
index 8f991c7e4..4ba38e000 100644
--- a/frontend/src/lang/modules/zh-Hant.ts
+++ b/frontend/src/lang/modules/zh-Hant.ts
@@ -2093,6 +2093,7 @@ const message = {
auto: '跟隨系統',
language: '系統語言',
runtimeEnv: '運行環境',
+ offlineEnv: '離線環境',
docSource: '文件來源',
withByRegion: '跟隨運行區域(預設)',
withByLang: '跟隨系統語言',
diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts
index 547b8e56d..1f58f65ac 100644
--- a/frontend/src/lang/modules/zh.ts
+++ b/frontend/src/lang/modules/zh.ts
@@ -2093,6 +2093,7 @@ const message = {
withByRegion: '跟随运行区域(默认)',
withByLang: '跟随系统语言',
runtimeEnv: '运行环境',
+ offlineEnv: '离线环境',
region: '运行区域',
cn: '中国大陆',
intl: '全球',
diff --git a/frontend/src/layout/components/Sidebar/components/user-info/index.vue b/frontend/src/layout/components/Sidebar/components/user-info/index.vue
index b43454124..c431e4eaf 100644
--- a/frontend/src/layout/components/Sidebar/components/user-info/index.vue
+++ b/frontend/src/layout/components/Sidebar/components/user-info/index.vue
@@ -88,7 +88,6 @@