diff --git a/backend/app/dto/setting.go b/backend/app/dto/setting.go index 13dc156f9..65da34309 100644 --- a/backend/app/dto/setting.go +++ b/backend/app/dto/setting.go @@ -8,6 +8,7 @@ type SettingInfo struct { SystemIP string `json:"systemIP"` SystemVersion string `json:"systemVersion"` DockerSockPath string `json:"dockerSockPath"` + DeveloperMode string `json:"developerMode"` SessionTimeout string `json:"sessionTimeout"` LocalTime string `json:"localTime"` @@ -140,6 +141,7 @@ type SnapshotInfo struct { } type UpgradeInfo struct { + TestVersion string `json:"testVersion"` NewVersion string `json:"newVersion"` LatestVersion string `json:"latestVersion"` ReleaseNote string `json:"releaseNote"` diff --git a/backend/app/service/upgrade.go b/backend/app/service/upgrade.go index 8e662fa17..6b8d243d6 100644 --- a/backend/app/service/upgrade.go +++ b/backend/app/service/upgrade.go @@ -7,12 +7,11 @@ import ( "net/http" "os" "path" + "strconv" "strings" "time" "github.com/1Panel-dev/1Panel/backend/app/dto" - "github.com/1Panel-dev/1Panel/backend/buserr" - "github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/utils/cmd" "github.com/1Panel-dev/1Panel/backend/utils/common" @@ -37,45 +36,30 @@ func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) { if err != nil { return nil, err } - - latestVersion, err := u.loadVersion(true, currentVersion.Value) + DeveloperMode, err := settingRepo.Get(settingRepo.WithByKey("DeveloperMode")) if err != nil { - global.LOG.Infof("load latest version failed, err: %v", err) - return nil, err - } - if !common.ComparePanelVersion(string(latestVersion), currentVersion.Value) { return nil, err } - upgrade.LatestVersion = latestVersion - if latestVersion[0:4] == currentVersion.Value[0:4] { - upgrade.NewVersion = "" - } else { - newerVersion, err := u.loadVersion(false, currentVersion.Value) - if err != nil { - global.LOG.Infof("load newer version failed, err: %v", err) - return nil, err - } - if newerVersion == currentVersion.Value { - upgrade.NewVersion = "" - } else { - upgrade.NewVersion = newerVersion - } - } + + upgrade.TestVersion, upgrade.NewVersion, upgrade.LatestVersion = u.loadVersionByMode(DeveloperMode.Value, currentVersion.Value) itemVersion := upgrade.LatestVersion if upgrade.NewVersion != "" { itemVersion = upgrade.NewVersion } notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.System.RepoUrl, global.CONF.System.Mode, itemVersion, itemVersion)) - if err != nil { - return nil, fmt.Errorf("load releases-notes of version %s failed, err: %v", latestVersion, err) + return nil, fmt.Errorf("load releases-notes of version %s failed, err: %v", itemVersion, err) } upgrade.ReleaseNote = notes return &upgrade, nil } func (u *UpgradeService) LoadNotes(req dto.Upgrade) (string, error) { - notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.System.RepoUrl, global.CONF.System.Mode, req.Version, req.Version)) + mode := global.CONF.System.Mode + if strings.Contains(req.Version, "beta") { + mode = "beta" + } + notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.System.RepoUrl, mode, req.Version, req.Version)) if err != nil { return "", fmt.Errorf("load releases-notes of version %s failed, err: %v", req.Version, err) } @@ -211,36 +195,92 @@ func (u *UpgradeService) handleRollback(originalDir string, errStep int) { } } -func (u *UpgradeService) loadVersion(isLatest bool, currentVersion string) (string, error) { - path := fmt.Sprintf("%s/%s/latest", global.CONF.System.RepoUrl, global.CONF.System.Mode) +func (u *UpgradeService) loadVersionByMode(developer, currentVersion string) (string, string, string) { + var current, latest string + if global.CONF.System.Mode == "dev" { + betaVersionLatest := u.loadVersion(true, currentVersion, "beta") + devVersionLatest := u.loadVersion(true, currentVersion, "dev") + if common.ComparePanelVersion(betaVersionLatest, devVersionLatest) { + return betaVersionLatest, "", "" + } + return devVersionLatest, "", "" + } + + latest = u.loadVersion(true, currentVersion, "stable") + current = u.loadVersion(false, currentVersion, "stable") + if len(developer) == 0 || developer == "disable" { + return "", current, latest + } + betaVersionLatest := u.loadVersion(true, currentVersion, "beta") + + return betaVersionLatest, current, latest +} + +func (u *UpgradeService) loadVersion(isLatest bool, currentVersion, mode string) string { + path := fmt.Sprintf("%s/%s/latest", global.CONF.System.RepoUrl, mode) if !isLatest { - path = fmt.Sprintf("%s/%s/latest.current", global.CONF.System.RepoUrl, global.CONF.System.Mode) + path = fmt.Sprintf("%s/%s/latest.current", global.CONF.System.RepoUrl, mode) } latestVersionRes, err := http.Get(path) if err != nil { - return "", buserr.New(constant.ErrOSSConn) + global.LOG.Errorf("load latest version from oss failed, err: %v", err) + return "" } defer latestVersionRes.Body.Close() - version, err := io.ReadAll(latestVersionRes.Body) - if err != nil { - return "", buserr.New(constant.ErrOSSConn) + versionByte, err := io.ReadAll(latestVersionRes.Body) + version := string(versionByte) + if err != nil || strings.Contains(version, "<") { + global.LOG.Errorf("load latest version from oss failed, err: %v", version) + return "" } if isLatest { - return string(version), nil + return u.checkVersion(version, currentVersion) } versionMap := make(map[string]string) - if err := json.Unmarshal(version, &versionMap); err != nil { - return "", buserr.New(constant.ErrOSSConn) + if err := json.Unmarshal(versionByte, &versionMap); err != nil { + global.LOG.Errorf("load latest version from oss failed (error unmarshal), err: %v", err) + return "" } - if len(currentVersion) < 4 { - return "", fmt.Errorf("current version is error format: %s", currentVersion) + versionPart := strings.Split(currentVersion, ".") + if len(versionPart) < 3 { + global.LOG.Errorf("current version is error format: %s", currentVersion) + return "" + } + num, _ := strconv.Atoi(versionPart[1]) + if num == 0 { + global.LOG.Errorf("current version is error format: %s", currentVersion) + return "" + } + if num >= 10 { + if version, ok := versionMap[currentVersion[0:5]]; ok { + return u.checkVersion(version, currentVersion) + } + return "" } if version, ok := versionMap[currentVersion[0:4]]; ok { - return version, nil + return u.checkVersion(version, currentVersion) + } + return "" +} + +func (u *UpgradeService) checkVersion(v2, v1 string) string { + addSuffix := false + if !strings.Contains(v1, "-") { + v1 = v1 + "-lts" + } + if !strings.Contains(v2, "-") { + addSuffix = true + v2 = v2 + "-lts" + } + if common.ComparePanelVersion(v2, v1) { + if addSuffix { + return strings.TrimSuffix(v2, "-lts") + } + return v2 } - return "", buserr.New(constant.ErrOSSConn) + return "" } func (u *UpgradeService) loadReleaseNotes(path string) (string, error) { diff --git a/backend/init/migration/migrate.go b/backend/init/migration/migrate.go index 3858dd631..cb29f6289 100644 --- a/backend/init/migration/migrate.go +++ b/backend/init/migration/migrate.go @@ -81,6 +81,7 @@ func Init() { migrations.AddNoAuthSetting, migrations.UpdateXpackHideMenu, migrations.AddMenuTabsSetting, + migrations.AddDeveloperSetting, }) if err := m.Migrate(); err != nil { global.LOG.Error(err) diff --git a/backend/init/migration/migrations/v_1_10.go b/backend/init/migration/migrations/v_1_10.go index 0e84b64ff..7ee32a68b 100644 --- a/backend/init/migration/migrations/v_1_10.go +++ b/backend/init/migration/migrations/v_1_10.go @@ -144,3 +144,13 @@ var AddMenuTabsSetting = &gormigrate.Migration{ return nil }, } + +var AddDeveloperSetting = &gormigrate.Migration{ + ID: "20240423-add-developer-setting", + Migrate: func(tx *gorm.DB) error { + if err := tx.Create(&model.Setting{Key: "DeveloperMode", Value: "disable"}).Error; err != nil { + return err + } + return nil + }, +} diff --git a/backend/utils/common/common.go b/backend/utils/common/common.go index 97dd4459b..5395e70c4 100644 --- a/backend/utils/common/common.go +++ b/backend/utils/common/common.go @@ -49,8 +49,8 @@ func ComparePanelVersion(version1, version2 string) bool { if version1 == version2 { return false } - version1s := strings.Split(version1, ".") - version2s := strings.Split(version2, ".") + version1s := SplitStr(version1, ".", "-") + version2s := SplitStr(version2, ".", "-") if len(version2s) > len(version1s) { for i := 0; i < len(version2s)-len(version1s); i++ { @@ -68,7 +68,15 @@ func ComparePanelVersion(version1, version2 string) bool { if version1s[i] == version2s[i] { continue } else { - return version1s[i] > version2s[i] + v1, err1 := strconv.Atoi(version1s[i]) + if err1 != nil { + return version1s[i] > version2s[i] + } + v2, err2 := strconv.Atoi(version2s[i]) + if err2 != nil { + return version1s[i] > version2s[i] + } + return v1 > v2 } } return true @@ -296,3 +304,16 @@ func PunycodeEncode(text string) (string, error) { } return ascii, nil } + +func SplitStr(str string, spi ...string) []string { + lists := []string{str} + var results []string + for _, s := range spi { + results = []string{} + for _, list := range lists { + results = append(results, strings.Split(list, s)...) + } + lists = results + } + return results +} diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index 9f7ea2be6..29bac1d03 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -14675,6 +14675,12 @@ const docTemplate = `{ "image": { "type": "string" }, + "ipv4": { + "type": "string" + }, + "ipv6": { + "type": "string" + }, "labels": { "type": "array", "items": { @@ -17775,6 +17781,9 @@ const docTemplate = `{ "defaultNetwork": { "type": "string" }, + "developerMode": { + "type": "string" + }, "dingVars": { "type": "string" }, @@ -17814,6 +17823,9 @@ const docTemplate = `{ "localTime": { "type": "string" }, + "menuTabs": { + "type": "string" + }, "messageType": { "type": "string" }, @@ -18076,6 +18088,9 @@ const docTemplate = `{ }, "releaseNote": { "type": "string" + }, + "testVersion": { + "type": "string" } } }, diff --git a/cmd/server/docs/swagger.json b/cmd/server/docs/swagger.json index eedc515e1..a33dcc827 100644 --- a/cmd/server/docs/swagger.json +++ b/cmd/server/docs/swagger.json @@ -14668,6 +14668,12 @@ "image": { "type": "string" }, + "ipv4": { + "type": "string" + }, + "ipv6": { + "type": "string" + }, "labels": { "type": "array", "items": { @@ -17768,6 +17774,9 @@ "defaultNetwork": { "type": "string" }, + "developerMode": { + "type": "string" + }, "dingVars": { "type": "string" }, @@ -17807,6 +17816,9 @@ "localTime": { "type": "string" }, + "menuTabs": { + "type": "string" + }, "messageType": { "type": "string" }, @@ -18069,6 +18081,9 @@ }, "releaseNote": { "type": "string" + }, + "testVersion": { + "type": "string" } } }, diff --git a/cmd/server/docs/swagger.yaml b/cmd/server/docs/swagger.yaml index 9ff795cbd..8ec77eb4d 100644 --- a/cmd/server/docs/swagger.yaml +++ b/cmd/server/docs/swagger.yaml @@ -444,6 +444,10 @@ definitions: type: boolean image: type: string + ipv4: + type: string + ipv6: + type: string labels: items: type: string @@ -2541,6 +2545,8 @@ definitions: type: string defaultNetwork: type: string + developerMode: + type: string dingVars: type: string dockerSockPath: @@ -2567,6 +2573,8 @@ definitions: type: string localTime: type: string + menuTabs: + type: string messageType: type: string mfaInterval: @@ -2740,6 +2748,8 @@ definitions: type: string releaseNote: type: string + testVersion: + type: string type: object dto.UserLoginInfo: properties: diff --git a/frontend/src/api/interface/setting.ts b/frontend/src/api/interface/setting.ts index 38fb9e345..b59018801 100644 --- a/frontend/src/api/interface/setting.ts +++ b/frontend/src/api/interface/setting.ts @@ -8,6 +8,7 @@ export namespace Setting { systemIP: string; systemVersion: string; dockerSockPath: string; + developerMode: string; sessionTimeout: number; localTime: string; diff --git a/frontend/src/components/system-upgrade/index.vue b/frontend/src/components/system-upgrade/index.vue index b582bea59..73d91777f 100644 --- a/frontend/src/components/system-upgrade/index.vue +++ b/frontend/src/components/system-upgrade/index.vue @@ -50,9 +50,12 @@ {{ upgradeInfo.newVersion }} {{ $t('setting.newVersion') }} - + {{ upgradeInfo.latestVersion }} {{ $t('setting.latestVersion') }} + + {{ upgradeInfo.testVersion }} {{ $t('setting.testVersion') }} + { await loadUpgradeInfo() .then((res) => { loading.value = false; - if (!res.data) { + if (res.data.testVersion || res.data.newVersion || res.data.latestVersion) { + upgradeInfo.value = res.data; + drawerVisible.value = true; + if (upgradeInfo.value.newVersion) { + upgradeVersion.value = upgradeInfo.value.newVersion; + return; + } + if (upgradeInfo.value.latestVersion) { + upgradeVersion.value = upgradeInfo.value.latestVersion; + return; + } + if (upgradeInfo.value.testVersion) { + upgradeVersion.value = upgradeInfo.value.testVersion; + return; + } + } else { MsgSuccess(i18n.global.t('setting.noUpgrade')); return; } - upgradeInfo.value = res.data; - upgradeVersion.value = upgradeInfo.value.newVersion || upgradeInfo.value.latestVersion; - drawerVisible.value = true; }) .catch(() => { loading.value = false; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index ff367e646..546fc9ffb 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -1266,6 +1266,7 @@ const message = { noSpace: 'Input information cannot include space characters', duplicatePassword: 'The new password cannot be the same as the original password, please re-enter!', diskClean: 'Cache Clean', + developerMode: 'Internal mode', thirdParty: 'Third-party account', createBackupAccount: 'Add {0}', @@ -1468,6 +1469,7 @@ const message = { 'Name rules: [major version].[functional version].[Bug fix version], as shown in the following example:', versionHelper1: 'v1.0.1 is a Bug fix after v1.0.0', versionHelper2: 'v1.1.0 is a feature release after v1.0.0', + testVersion: '(Beta version)', newVersion: '(Bug fix version)', latestVersion: '(Functional version)', upgradeCheck: 'Check for updates', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index ee2f16e0b..7fcde3b8d 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -1189,6 +1189,7 @@ const message = { noSpace: '輸入信息不能包括空格符號', duplicatePassword: '新密碼不能與原始密碼一致,請重新輸入!', diskClean: '缓存清理', + developerMode: '內測模式', thirdParty: '第三方賬號', createBackupAccount: '添加 {0}', @@ -1295,6 +1296,7 @@ const message = { versionHelper: '1Panel 版本號命名規則為: [大版本].[功能版本].[Bug 修復版本],例:', versionHelper1: 'v1.0.1 是 v1.0.0 之後的 Bug 修復版本', versionHelper2: 'v1.1.0 是 v1.0.0 之後的功能版本', + testVersion: '(內測版本)', newVersion: '(Bug 修復版本)', latestVersion: '(功能版本)', upgradeCheck: '檢查更新', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 92d8cdfd8..e1900f0be 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -1190,6 +1190,7 @@ const message = { noSpace: '输入信息不能包含空格符', duplicatePassword: '新密码不能与原始密码一致,请重新输入!', diskClean: '缓存清理', + developerMode: '内测模式', thirdParty: '第三方账号', createBackupAccount: '添加 {0}', @@ -1296,6 +1297,7 @@ const message = { versionHelper: '1Panel 版本号命名规则为: [大版本].[功能版本].[Bug 修复版本],例:', versionHelper1: 'v1.0.1 是 v1.0.0 之后的 Bug 修复版本', versionHelper2: 'v1.1.0 是 v1.0.0 之后的功能版本', + testVersion: '(内测版本)', newVersion: '(Bug 修复版本)', latestVersion: '(功能版本)', upgradeCheck: '检查更新', diff --git a/frontend/src/views/home/index.vue b/frontend/src/views/home/index.vue index dba101ab8..fa5d635c8 100644 --- a/frontend/src/views/home/index.vue +++ b/frontend/src/views/home/index.vue @@ -542,7 +542,7 @@ const hideEntrance = () => { const loadUpgradeStatus = async () => { const res = await loadUpgradeInfo(); - if (res.data) { + if (res.data.testVersion || res.data.newVersion || res.data.latestVersion) { globalStore.hasNewVersion = true; } else { globalStore.hasNewVersion = false; diff --git a/frontend/src/views/setting/panel/index.vue b/frontend/src/views/setting/panel/index.vue index 21155ae64..51d632b15 100644 --- a/frontend/src/views/setting/panel/index.vue +++ b/frontend/src/views/setting/panel/index.vue @@ -113,6 +113,20 @@ + + + + {{ $t('commons.button.enable') }} + + + {{ $t('commons.button.disable') }} + + + +