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') }}
+
+
+
+
@@ -176,6 +190,7 @@ const form = reactive({
complexityVerification: '',
defaultNetwork: '',
defaultNetworkVal: '',
+ developerMode: '',
proHideMenus: ref(i18n.t('setting.unSetting')),
hideMenuList: '',
@@ -219,6 +234,7 @@ const search = async () => {
form.defaultNetworkVal = res.data.defaultNetwork === 'all' ? i18n.t('commons.table.all') : res.data.defaultNetwork;
form.proHideMenus = res.data.xpackHideMenu;
form.hideMenuList = res.data.xpackHideMenu;
+ form.developerMode = res.data.developerMode;
// 提取隐藏节点的 title 并显示
const json: Node = JSON.parse(res.data.xpackHideMenu);