From c391177d34af91e3340ecef32295d3aa5d633f89 Mon Sep 17 00:00:00 2001 From: CityFun <31820853+zhengkunwang223@users.noreply.github.com> Date: Sun, 13 Jul 2025 22:36:21 +0800 Subject: [PATCH] feat: Add MySQL master-slave installation feature (#9505) --- agent/app/api/v2/app_install.go | 22 ++++++++++ agent/app/dto/response/app.go | 13 ++++++ agent/app/service/app_install.go | 27 ++++++++++++ agent/app/service/app_utils.go | 4 +- agent/router/ro_app.go | 1 + core/app/task/task.go | 15 ++++--- core/constant/common.go | 5 +++ core/constant/status.go | 4 ++ core/init/migration/helper/menu.go | 42 +++++++++++++++++++ core/init/migration/migrate.go | 1 + core/init/migration/migrations/init.go | 14 +++++++ frontend/package.json | 2 +- frontend/src/api/interface/app.ts | 11 +++++ frontend/src/api/modules/app.ts | 13 +++++- frontend/src/api/modules/setting.ts | 5 ++- frontend/src/components/log/compose/index.vue | 4 ++ .../src/components/log/container/index.vue | 7 ++++ frontend/src/lang/modules/en.ts | 8 ++++ frontend/src/lang/modules/ja.ts | 8 ++++ frontend/src/lang/modules/ko.ts | 8 ++++ frontend/src/lang/modules/ms.ts | 8 ++++ frontend/src/lang/modules/pt-br.ts | 8 ++++ frontend/src/lang/modules/ru.ts | 8 ++++ frontend/src/lang/modules/tr.ts | 8 ++++ frontend/src/lang/modules/zh-Hant.ts | 8 ++++ frontend/src/lang/modules/zh.ts | 8 ++++ frontend/src/utils/app.ts | 28 +++++++++++++ frontend/src/views/app-store/apps/index.vue | 22 ++++------ frontend/src/views/app-store/detail/index.vue | 25 ++++------- frontend/src/views/home/app/index.vue | 14 ++----- 30 files changed, 295 insertions(+), 56 deletions(-) create mode 100644 frontend/src/utils/app.ts diff --git a/agent/app/api/v2/app_install.go b/agent/app/api/v2/app_install.go index 534c48f78..e12e2a365 100644 --- a/agent/app/api/v2/app_install.go +++ b/agent/app/api/v2/app_install.go @@ -327,3 +327,25 @@ func (b *BaseApi) UpdateAppConfig(c *gin.Context) { } helper.Success(c) } + +// @Tags App +// @Summary Get app install info +// @Accept json +// @Param appInstallId path integer true "App install id" +// @Success 200 {object} dto.AppInstallInfo +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /apps/installed/info/:appInstallId [get] +func (b *BaseApi) GetAppInstallInfo(c *gin.Context) { + appInstallId, err := helper.GetIntParamByKey(c, "appInstallId") + if err != nil { + helper.BadRequest(c, err) + return + } + info, err := appInstallService.GetAppInstallInfo(appInstallId) + if err != nil { + helper.InternalServer(c, err) + return + } + helper.SuccessWithData(c, info) +} diff --git a/agent/app/dto/response/app.go b/agent/app/dto/response/app.go index cfba2b13a..1dd3618b6 100644 --- a/agent/app/dto/response/app.go +++ b/agent/app/dto/response/app.go @@ -129,6 +129,19 @@ type AppInstallDTO struct { Container string `json:"container"` } +type AppInstallInfo struct { + ID uint `json:"id"` + Name string `json:"name"` + Version string `json:"version"` + Status string `json:"status"` + Message string `json:"message"` + HttpPort int `json:"HttpPort"` + Container string `json:"container"` + ComposePath string `json:"composePath"` + + Env map[string]interface{} `json:"env"` +} + type DatabaseConn struct { Status string `json:"status"` Username string `json:"username"` diff --git a/agent/app/service/app_install.go b/agent/app/service/app_install.go index ee0ee48dc..942a5496d 100644 --- a/agent/app/service/app_install.go +++ b/agent/app/service/app_install.go @@ -56,6 +56,7 @@ type IAppInstallService interface { UpdateAppConfig(req request.AppConfigUpdate) error GetInstallList() ([]dto.AppInstallInfo, error) + GetAppInstallInfo(appInstallID uint) (*response.AppInstallInfo, error) } func NewIAppInstalledService() IAppInstallService { @@ -898,3 +899,29 @@ func updateInstallInfoInDB(appKey, appName, param string, value interface{}) err } return nil } + +func (a *AppInstallService) GetAppInstallInfo(installID uint) (*response.AppInstallInfo, error) { + appInstall, _ := appInstallRepo.GetFirst(repo.WithByID(installID)) + if appInstall.ID == 0 { + return &response.AppInstallInfo{ + Status: constant.StatusDeleted, + }, nil + } + var envMap map[string]interface{} + err := json.Unmarshal([]byte(appInstall.Env), &envMap) + if err != nil { + return nil, err + } + res := &response.AppInstallInfo{ + ID: appInstall.ID, + Name: appInstall.Name, + Version: appInstall.Version, + Container: appInstall.ContainerName, + HttpPort: appInstall.HttpPort, + Status: appInstall.Status, + Message: appInstall.Message, + Env: envMap, + ComposePath: appInstall.GetComposePath(), + } + return res, nil +} diff --git a/agent/app/service/app_utils.go b/agent/app/service/app_utils.go index 99a62f5f8..097e42da9 100644 --- a/agent/app/service/app_utils.go +++ b/agent/app/service/app_utils.go @@ -974,8 +974,8 @@ func runScript(task *task.Task, appInstall *model.AppInstall, operate string) er logStr := i18n.GetWithName("ExecShell", operate) task.LogStart(logStr) - cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute), cmd.WithScriptPath(scriptPath), cmd.WithWorkDir(workDir)) - out, err := cmdMgr.RunWithStdout("bash") + cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute), cmd.WithWorkDir(workDir)) + out, err := cmdMgr.RunWithStdoutBashCf(scriptPath) if err != nil { if out != "" { err = errors.New(out) diff --git a/agent/router/ro_app.go b/agent/router/ro_app.go index 49d06dccd..7510af97f 100644 --- a/agent/router/ro_app.go +++ b/agent/router/ro_app.go @@ -38,6 +38,7 @@ func (a *AppRouter) InitRouter(Router *gin.RouterGroup) { appRouter.POST("/installed/params/update", baseApi.UpdateInstalled) appRouter.POST("/installed/update/versions", baseApi.GetUpdateVersions) appRouter.POST("/installed/config/update", baseApi.UpdateAppConfig) + appRouter.GET("/installed/info/:appInstallId", baseApi.GetAppInstallInfo) appRouter.POST("/installed/ignore", baseApi.IgnoreAppUpgrade) appRouter.GET("/ignored/detail", baseApi.ListAppIgnored) diff --git a/core/app/task/task.go b/core/app/task/task.go index 324cbf868..c3d1ddc7d 100644 --- a/core/app/task/task.go +++ b/core/app/task/task.go @@ -45,16 +45,19 @@ type SubTask struct { } const ( - TaskUpgrade = "TaskUpgrade" - TaskAddNode = "TaskAddNode" - TaskSync = "TaskSync" - TaskRsync = "TaskRsync" + TaskUpgrade = "TaskUpgrade" + TaskAddNode = "TaskAddNode" + TaskSync = "TaskSync" + TaskRsync = "TaskRsync" + TaskInstallCluster = "TaskInstallCluster" + TaskCreateCluster = "TaskCreateCluster" ) const ( - TaskScopeSystem = "System" - TaskScopeScript = "Script" + TaskScopeSystem = "System" + TaskScopeScript = "Script" TaskScopeNodeFile = "NodeFile" + TaskScopeCluster = "Cluster" ) func GetTaskName(resourceName, operate, scope string) string { diff --git a/core/constant/common.go b/core/constant/common.go index af21c4164..8f916b372 100644 --- a/core/constant/common.go +++ b/core/constant/common.go @@ -176,3 +176,8 @@ var DynamicRoutes = []string{ var CertStore atomic.Value var DaemonJsonPath = "/etc/docker/daemon.json" + +const ( + RoleMaster = "master" + RoleSlave = "slave" +) diff --git a/core/constant/status.go b/core/constant/status.go index ebb871738..39b8eb7fd 100644 --- a/core/constant/status.go +++ b/core/constant/status.go @@ -19,4 +19,8 @@ const ( StatusEnable = "Enable" StatusDisable = "Disable" + + StatusInstalling = "Installing" + StatusNormal = "Normal" + StatusDeleted = "Deleted" ) diff --git a/core/init/migration/helper/menu.go b/core/init/migration/helper/menu.go index cc92f2376..406cd8b1a 100644 --- a/core/init/migration/helper/menu.go +++ b/core/init/migration/helper/menu.go @@ -2,6 +2,10 @@ package helper import ( "encoding/json" + "fmt" + "github.com/1Panel-dev/1Panel/core/app/model" + "gorm.io/gorm" + "strings" "github.com/1Panel-dev/1Panel/core/app/dto" ) @@ -53,3 +57,41 @@ func LoadMenus() string { menu, _ := json.Marshal(item) return string(menu) } + +func AddMenu(newMenu dto.ShowMenu, parentMenuID string, tx *gorm.DB) error { + var menuJSON string + if err := tx.Model(&model.Setting{}).Where("key = ?", "HideMenu").Pluck("value", &menuJSON).Error; err != nil { + return err + } + if strings.Contains(menuJSON, fmt.Sprintf(`"%s"`, newMenu.Label)) && strings.Contains(menuJSON, fmt.Sprintf(`"%s"`, newMenu.Path)) { + return nil + } + var menus []dto.ShowMenu + if err := json.Unmarshal([]byte(menuJSON), &menus); err != nil { + return tx.Model(&model.Setting{}). + Where("key = ?", "HideMenu"). + Update("value", LoadMenus()).Error + } + for i, menu := range menus { + if menu.ID == parentMenuID { + exists := false + for _, child := range menu.Children { + if child.ID == newMenu.ID { + exists = true + break + } + } + if !exists { + menus[i].Children = append([]dto.ShowMenu{newMenu}, menus[i].Children...) + } + break + } + } + updatedJSON, err := json.Marshal(menus) + if err != nil { + return tx.Model(&model.Setting{}). + Where("key = ?", "HideMenu"). + Update("value", LoadMenus()).Error + } + return tx.Model(&model.Setting{}).Where("key = ?", "HideMenu").Update("value", string(updatedJSON)).Error +} diff --git a/core/init/migration/migrate.go b/core/init/migration/migrate.go index fa5af682b..7359c1ee7 100644 --- a/core/init/migration/migrate.go +++ b/core/init/migration/migrate.go @@ -20,6 +20,7 @@ func Init() { migrations.UpdateGoogle, migrations.UpdateXpackHideMenu, migrations.UpdateOnedrive, + migrations.AddClusterMenu, }) 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 dd55ef084..508dec81e 100644 --- a/core/init/migration/migrations/init.go +++ b/core/init/migration/migrations/init.go @@ -489,3 +489,17 @@ var UpdateOnedrive = &gormigrate.Migration{ return nil }, } + +var AddClusterMenu = &gormigrate.Migration{ + ID: "20250707-add-cluster-menu", + Migrate: func(tx *gorm.DB) error { + return helper.AddMenu(dto.ShowMenu{ + ID: "120", + Disabled: false, + Title: "xpack.cluster.cluster", + IsShow: true, + Label: "Cluster", + Path: "/xpack/cluster", + }, "11", tx) + }, +} diff --git a/frontend/package.json b/frontend/package.json index 663349f0b..de7c2a6a3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -38,7 +38,7 @@ "codemirror": "^6.0.1", "crypto-js": "^4.2.0", "echarts": "^5.5.0", - "element-plus": "2.9.8", + "element-plus": "2.9.9", "fit2cloud-ui-plus": "^1.2.2", "highlight.js": "^11.9.0", "js-base64": "^3.7.7", diff --git a/frontend/src/api/interface/app.ts b/frontend/src/api/interface/app.ts index 92d3be97d..e3de49610 100644 --- a/frontend/src/api/interface/app.ts +++ b/frontend/src/api/interface/app.ts @@ -151,6 +151,17 @@ export namespace App { webUI: string; } + export interface AppInstalledInfo { + id: number; + name: string; + version: string; + status: string; + message: string; + httpPort: number; + container: string; + env: { [key: string]: string }; + } + export interface AppInstallDto { id: number; name: string; diff --git a/frontend/src/api/modules/app.ts b/frontend/src/api/modules/app.ts index 9d675ea95..5109ec3c4 100644 --- a/frontend/src/api/modules/app.ts +++ b/frontend/src/api/modules/app.ts @@ -19,6 +19,10 @@ export const getAppByKey = (key: string) => { return http.get('apps/' + key); }; +export const getAppByKeyWithNode = (key: string, node: string) => { + return http.get('apps/' + key + `?operateNode=${node}`); +}; + export const getAppTags = () => { return http.get('apps/tags'); }; @@ -59,14 +63,19 @@ export const checkAppInstalled = (key: string, name: string) => { return http.post(`apps/installed/check`, { key: key, name: name }); }; -export const appInstalledDeleteCheck = (appInstallId: number) => { - return http.get(`apps/installed/delete/check/${appInstallId}`); +export const appInstalledDeleteCheck = (appInstallId: number, node?: string) => { + const params = node ? `?operateNode=${node}` : ''; + return http.get(`apps/installed/delete/check/${appInstallId}${params}`); }; export const getAppInstalled = (search: App.AppInstalledSearch) => { return http.post>('apps/installed/search', search); }; +export const getAppInstalledByID = (installID: number, node: string) => { + return http.get(`apps/installed/info/${installID}?operateNode=${node}`); +}; + export const installedOp = (op: App.AppInstalledOp) => { return http.post('apps/installed/op', op, TimeoutEnum.T_40S); }; diff --git a/frontend/src/api/modules/setting.ts b/frontend/src/api/modules/setting.ts index c4ee2f074..0f7d97915 100644 --- a/frontend/src/api/modules/setting.ts +++ b/frontend/src/api/modules/setting.ts @@ -126,8 +126,9 @@ export const loadMFA = (param: Setting.MFARequest) => { export const bindMFA = (param: Setting.MFABind) => { return http.post(`/core/settings/mfa/bind`, param); }; -export const getAppStoreConfig = () => { - return http.get(`/core/settings/apps/store/config`); +export const getAppStoreConfig = (node?: string) => { + const params = node ? `?operateNode=${node}` : ''; + return http.get(`/core/settings/apps/store/config${params}`); }; export const updateAppStoreConfig = (req: App.AppStoreConfigUpdate) => { return http.post(`/core/settings/apps/store/update`, req); diff --git a/frontend/src/components/log/compose/index.vue b/frontend/src/components/log/compose/index.vue index d8f8dfb6e..d8620fc36 100644 --- a/frontend/src/components/log/compose/index.vue +++ b/frontend/src/components/log/compose/index.vue @@ -16,6 +16,7 @@ :compose="compose" :resource="resource" :container="container" + :node="node" :highlightDiff="highlightDiff" /> @@ -36,11 +37,13 @@ const globalStore = GlobalStore(); const logVisible = ref(false); const compose = ref(''); const highlightDiff = ref(320); +const node = ref(''); interface DialogProps { compose: string; resource: string; container: string; + node: string; } const defaultProps = defineProps({ @@ -75,6 +78,7 @@ const acceptParams = (props: DialogProps): void => { compose.value = props.compose; resource.value = props.resource; container.value = props.container; + node.value = props.node; open.value = true; }; diff --git a/frontend/src/components/log/container/index.vue b/frontend/src/components/log/container/index.vue index d589a1df5..e1d3c9c6c 100644 --- a/frontend/src/components/log/container/index.vue +++ b/frontend/src/components/log/container/index.vue @@ -65,6 +65,10 @@ const props = defineProps({ type: Number, default: 320, }, + node: { + type: String, + default: '', + }, }); const styleVars = computed(() => ({ @@ -137,6 +141,9 @@ const searchLogs = async () => { } logs.value = []; let currentNode = globalStore.currentNode; + if (props.node && props.node !== '') { + currentNode = props.node; + } let url = `/api/v2/containers/search/log?container=${logSearch.container}&since=${logSearch.mode}&tail=${logSearch.tail}&follow=${logSearch.isWatch}&operateNode=${currentNode}`; if (logSearch.compose !== '') { url = `/api/v2/containers/search/log?compose=${logSearch.compose}&since=${logSearch.mode}&tail=${logSearch.tail}&follow=${logSearch.isWatch}&operateNode=${currentNode}`; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 23a916584..fc241fd0c 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -3628,6 +3628,14 @@ const message = { exchange: 'File Exchange', exchangeConfirm: "Do you want to transfer the file/folder {1} from {0} node to {2} node's {3} directory?", }, + cluster: { + cluster: 'Application High Availability', + name: 'Cluster Name', + addCluster: 'Add Cluster', + installNode: 'Install Node', + master: 'Master Node', + slave: 'Slave Node', + }, }, }; diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index 70ae3bfee..0683eee76 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -3491,6 +3491,14 @@ const message = { exchange: 'ファイル交換', exchangeConfirm: '{0} ノードのファイル/フォルダ {1} を {2} ノードの {3} ディレクトリに転送しますか?', }, + cluster: { + cluster: 'アプリケーションの高可用性', + name: 'クラスタ名', + addCluster: 'クラスタを追加', + installNode: 'ノードをインストール', + master: 'マスターノード', + slave: 'スレーブノード', + }, }, }; export default { diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index 0ca734c6e..4af9e8aaf 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -3429,6 +3429,14 @@ const message = { exchange: '파일 교환', exchangeConfirm: '{0} 노드의 파일/폴더 {1}을(를) {2} 노드의 {3} 디렉토리로 전송하시겠습니까?', }, + cluster: { + cluster: '애플리케이션 고가용성', + name: '클러스터 이름', + addCluster: '클러스터 추가', + installNode: '노드 설치', + master: '마스터 노드', + slave: '슬레이브 노드', + }, }, }; diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index faf4b698a..bef272fa8 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -3572,6 +3572,14 @@ const message = { exchange: 'Pertukaran Fail', exchangeConfirm: 'Adakah anda mahu memindahkan fail/folder {1} dari node {0} ke direktori {3} node {2}?', }, + cluster: { + cluster: 'Aplikasi Tinggi Ketersediaan', + name: 'Nama Kluster', + addCluster: 'Tambah Kluster', + installNode: 'Pasang Node', + master: 'Node Utama', + slave: 'Node Hamba', + }, }, }; diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index 9249e7cce..e21f3b108 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -3579,6 +3579,14 @@ const message = { exchange: 'Troca de Arquivos', exchangeConfirm: 'Deseja transferir o arquivo/pasta {1} do nó {0} para o diretório {3} do nó {2}?', }, + cluster: { + cluster: 'Alta Disponibilidade de Aplicações', + name: 'Nome do Cluster', + addCluster: 'Adicionar Cluster', + installNode: 'Instalar Nó', + master: 'Nó Mestre', + slave: 'Nó Escravo', + }, }, }; diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index d70589265..7b190b72f 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -3570,6 +3570,14 @@ const message = { exchange: 'Обмен файлами', exchangeConfirm: 'Хотите перенести файл/папку {1} с узла {0} в каталог {3} узла {2}?', }, + cluster: { + cluster: 'Высокая доступность приложений', + name: 'Имя кластера', + addCluster: 'Добавить кластер', + installNode: 'Установить узел', + master: 'Главный узел', + slave: 'Подчиненный узел', + }, }, }; diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index 6a7ecd6a8..b48624b9e 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -3669,6 +3669,14 @@ const message = { exchange: 'Dosya Değişimi', exchangeConfirm: '{0} düğümünden {1} dosya/klasörünü {2} düğümünün {3} dizinine aktarmak istiyor musunuz?', }, + cluster: { + cluster: 'Высокая доступность приложений', + name: 'Имя кластера', + addCluster: 'Добавить кластер', + installNode: 'Установить узел', + master: 'Главный узел', + slave: 'Подчиненный узел', + }, }, }; diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index 746b08933..afbcf0c69 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -3377,6 +3377,14 @@ const message = { exchange: '文件對傳', exchangeConfirm: '是否將 {0} 節點文件/文件夾 {1} 傳輸到 {2} 節點 {3} 目錄?', }, + cluster: { + cluster: '應用高可用', + name: '集群名稱', + addCluster: '添加集群', + installNode: '安裝節點', + master: '主節點', + slave: '從節點', + }, }, }; export default { diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 132fb1892..89d6f74f5 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -3357,6 +3357,14 @@ const message = { exchange: '文件对传', exchangeConfirm: '是否将 {0} 节点文件/文件夹 {1} 传输到 {2} 节点 {3} 目录?', }, + cluster: { + cluster: '应用高可用', + name: '集群名称', + addCluster: '添加集群', + installNode: '安装节点', + master: '主节点', + slave: '从节点', + }, }, }; export default { diff --git a/frontend/src/utils/app.ts b/frontend/src/utils/app.ts new file mode 100644 index 000000000..8928ee6b4 --- /dev/null +++ b/frontend/src/utils/app.ts @@ -0,0 +1,28 @@ +import { jumpToPath } from './util'; +import router from '@/routers'; + +export const jumpToInstall = (type: string, key: string) => { + switch (type) { + case 'php': + case 'node': + case 'java': + case 'go': + case 'python': + case 'dotnet': + jumpToPath(router, '/websites/runtimes/' + type); + return true; + } + switch (key) { + case 'mysql-cluster': + console.log('jumpToInstall mysql-cluster'); + jumpToPath(router, '/xpack/cluster/mysql'); + return true; + case 'redis-cluster': + jumpToPath(router, '/xpack/cluster/redis'); + return true; + case 'postgres-cluster': + jumpToPath(router, '/xpack/cluster/postgres'); + return true; + } + return false; +}; diff --git a/frontend/src/views/app-store/apps/index.vue b/frontend/src/views/app-store/apps/index.vue index 32a5396f5..70aa033f8 100644 --- a/frontend/src/views/app-store/apps/index.vue +++ b/frontend/src/views/app-store/apps/index.vue @@ -132,13 +132,14 @@ import Install from '../detail/install/index.vue'; import router from '@/routers'; import { MsgSuccess } from '@/utils/message'; import { GlobalStore } from '@/store'; -import { newUUID, jumpToPath } from '@/utils/util'; +import { newUUID } from '@/utils/util'; import Detail from '../detail/index.vue'; import TaskLog from '@/components/log/task/index.vue'; import { storeToRefs } from 'pinia'; import bus from '@/global/bus'; import Tags from '@/views/app-store/components/tag.vue'; import DockerStatus from '@/views/container/docker-status/index.vue'; +import { jumpToInstall } from '@/utils/app'; const globalStore = GlobalStore(); const { isProductPro } = storeToRefs(globalStore); @@ -208,20 +209,11 @@ const search = async (req: App.AppReq) => { }; const openInstall = (app: App.App) => { - switch (app.type) { - case 'php': - case 'node': - case 'java': - case 'go': - case 'python': - case 'dotnet': - jumpToPath(router, '/websites/runtimes/' + app.type); - break; - default: - const params = { - app: app, - }; - installRef.value.acceptParams(params); + if (!jumpToInstall(app.type, app.key)) { + const params = { + app: app, + }; + installRef.value.acceptParams(params); } }; diff --git a/frontend/src/views/app-store/detail/index.vue b/frontend/src/views/app-store/detail/index.vue index 63809e884..338cef6ec 100644 --- a/frontend/src/views/app-store/detail/index.vue +++ b/frontend/src/views/app-store/detail/index.vue @@ -70,10 +70,10 @@ import { getAppByKey, getAppDetail } from '@/api/modules/app'; import MdEditor from 'md-editor-v3'; import { ref } from 'vue'; import Install from './install/index.vue'; -import router from '@/routers'; import { GlobalStore } from '@/store'; -import { computeSizeFromMB, jumpToPath } from '@/utils/util'; +import { computeSizeFromMB } from '@/utils/util'; import { storeToRefs } from 'pinia'; +import { jumpToInstall } from '@/utils/app'; const globalStore = GlobalStore(); const { isDarkTheme } = storeToRefs(globalStore); @@ -131,21 +131,12 @@ const toLink = (link: string) => { }; const openInstall = () => { - switch (app.value.type) { - case 'php': - case 'node': - case 'java': - case 'go': - case 'python': - case 'dotnet': - jumpToPath(router, '/websites/runtimes/' + app.value.type); - break; - default: - const params = { - app: app.value, - }; - installRef.value.acceptParams(params); - open.value = false; + if (!jumpToInstall(app.value.type, app.value.key)) { + const params = { + app: app.value, + }; + installRef.value.acceptParams(params); + open.value = false; } }; diff --git a/frontend/src/views/home/app/index.vue b/frontend/src/views/home/app/index.vue index d3f33ad60..fadf45012 100644 --- a/frontend/src/views/home/app/index.vue +++ b/frontend/src/views/home/app/index.vue @@ -189,6 +189,7 @@ import { ref } from 'vue'; import { useRouter } from 'vue-router'; import { toFolder } from '@/global/business'; import { jumpToPath } from '@/utils/util'; +import { jumpToInstall } from '@/utils/app'; const router = useRouter(); const globalStore = GlobalStore(); @@ -209,17 +210,8 @@ const acceptParams = (): void => { }; const goInstall = (key: string, type: string) => { - switch (type) { - case 'php': - case 'node': - case 'java': - case 'go': - case 'python': - case 'dotnet': - router.push({ path: '/websites/runtimes/' + type }); - break; - default: - router.push({ name: 'AppAll', query: { install: key } }); + if (!jumpToInstall(type, key)) { + router.push({ name: 'AppAll', query: { install: key } }); } };