diff --git a/core/init/router/proxy.go b/core/init/router/proxy.go index 60dda8d0e..807525429 100644 --- a/core/init/router/proxy.go +++ b/core/init/router/proxy.go @@ -2,10 +2,16 @@ package router import ( "context" + "github.com/1Panel-dev/1Panel/core/app/repo" + "github.com/1Panel-dev/1Panel/core/cmd/server/res" + "github.com/1Panel-dev/1Panel/core/constant" + "github.com/1Panel-dev/1Panel/core/global" + "github.com/1Panel-dev/1Panel/core/utils/security" "net" "net/http" "net/http/httputil" "os" + "strconv" "strings" "github.com/1Panel-dev/1Panel/core/app/api/v2/helper" @@ -38,6 +44,13 @@ func Proxy() gin.HandlerFunc { currentNode = c.Request.Header.Get("CurrentNode") } + if strings.HasPrefix(c.Request.URL.Path, "/api/v2/") && !checkSession(c) { + data, _ := res.ErrorMsg.ReadFile("html/401.html") + c.Data(401, "text/html; charset=utf-8", data) + c.Abort() + return + } + if !strings.HasPrefix(c.Request.URL.Path, "/api/v2/core") && (currentNode == "local" || len(currentNode) == 0 || currentNode == "127.0.0.1") { sockPath := "/etc/1panel/agent.sock" if _, err := os.Stat(sockPath); err != nil { @@ -58,6 +71,13 @@ func Proxy() gin.HandlerFunc { req.URL.Host = "unix" }, Transport: transport, + ModifyResponse: func(response *http.Response) error { + if response.StatusCode == 404 { + security.HandleNotSecurity(c, "") + c.Abort() + } + return nil + }, } proxy.ServeHTTP(c.Writer, c.Request) c.Abort() @@ -67,3 +87,22 @@ func Proxy() gin.HandlerFunc { c.Abort() } } + +func checkSession(c *gin.Context) bool { + psession, err := global.SESSION.Get(c) + if err != nil { + return false + } + settingRepo := repo.NewISettingRepo() + setting, err := settingRepo.Get(repo.WithByKey("SessionTimeout")) + if err != nil { + return false + } + lifeTime, _ := strconv.Atoi(setting.Value) + httpsSetting, err := settingRepo.Get(repo.WithByKey("SSL")) + if err != nil { + return false + } + _ = global.SESSION.Set(c, psession, httpsSetting.Value == constant.StatusEnable, lifeTime) + return true +} diff --git a/core/init/router/router.go b/core/init/router/router.go index 47f8ddd4d..1426d396f 100644 --- a/core/init/router/router.go +++ b/core/init/router/router.go @@ -3,157 +3,26 @@ package router import ( "encoding/base64" "fmt" - "net/http" - "path" - "regexp" - "strconv" - "strings" - - "github.com/1Panel-dev/1Panel/core/app/repo" - "github.com/1Panel-dev/1Panel/core/utils/common" - "github.com/1Panel-dev/1Panel/core/app/service" - "github.com/1Panel-dev/1Panel/core/cmd/server/res" - "github.com/1Panel-dev/1Panel/core/constant" - "github.com/1Panel-dev/1Panel/core/cmd/server/docs" "github.com/1Panel-dev/1Panel/core/cmd/server/web" "github.com/1Panel-dev/1Panel/core/global" "github.com/1Panel-dev/1Panel/core/i18n" "github.com/1Panel-dev/1Panel/core/middleware" rou "github.com/1Panel-dev/1Panel/core/router" + "github.com/1Panel-dev/1Panel/core/utils/security" "github.com/gin-contrib/gzip" "github.com/gin-gonic/gin" swaggerfiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" + "net/http" + "path" ) var ( Router *gin.Engine ) -func toIndexHtml(c *gin.Context) { - c.Writer.Header().Set("Content-Type", "text/html; charset=utf-8") - c.Writer.WriteHeader(http.StatusOK) - _, _ = c.Writer.Write(web.IndexByte) - c.Writer.Flush() -} - -func isEntrancePath(c *gin.Context) bool { - entrance := service.NewIAuthService().GetSecurityEntrance() - if entrance != "" && strings.TrimSuffix(c.Request.URL.Path, "/") == "/"+entrance { - return true - } - return false -} - -func checkEntrance(c *gin.Context) bool { - authService := service.NewIAuthService() - entrance := authService.GetSecurityEntrance() - if entrance == "" { - return true - } - - cookieValue, err := c.Cookie("SecurityEntrance") - if err != nil { - return false - } - entranceValue, err := base64.StdEncoding.DecodeString(cookieValue) - if err != nil { - return false - } - return string(entranceValue) == entrance -} - -func handleNoRoute(c *gin.Context, resType string) { - resPage, err := service.NewIAuthService().GetResponsePage() - if err != nil { - c.String(http.StatusInternalServerError, "Internal Server Error") - return - } - if resPage == "444" { - c.String(444, "") - return - } - - file := fmt.Sprintf("html/%s.html", resPage) - if resPage == "200" && resType != "" { - file = fmt.Sprintf("html/200_%s.html", resType) - } - data, err := res.ErrorMsg.ReadFile(file) - if err != nil { - c.String(http.StatusInternalServerError, "Internal Server Error") - return - } - statusCode, err := strconv.Atoi(resPage) - if err != nil { - c.String(http.StatusInternalServerError, "Internal Server Error") - return - } - c.Data(statusCode, "text/html; charset=utf-8", data) -} - -func isFrontendPath(c *gin.Context) bool { - reqUri := strings.TrimSuffix(c.Request.URL.Path, "/") - if _, ok := constant.WebUrlMap[reqUri]; ok { - return true - } - for _, route := range constant.DynamicRoutes { - if match, _ := regexp.MatchString(route, reqUri); match { - return true - } - } - return false -} - -func checkFrontendPath(c *gin.Context) bool { - if !isFrontendPath(c) { - return false - } - authService := service.NewIAuthService() - if authService.GetSecurityEntrance() != "" { - return authService.IsLogin(c) - } - return true -} - -func checkBindDomain(c *gin.Context) bool { - settingRepo := repo.NewISettingRepo() - status, _ := settingRepo.Get(repo.WithByKey("BindDomain")) - if len(status.Value) == 0 { - return true - } - domains := c.Request.Host - parts := strings.Split(c.Request.Host, ":") - if len(parts) > 0 { - domains = parts[0] - } - return domains == status.Value -} - -func checkIPLimit(c *gin.Context) bool { - settingRepo := repo.NewISettingRepo() - status, _ := settingRepo.Get(repo.WithByKey("AllowIPs")) - if len(status.Value) == 0 { - return true - } - clientIP := c.ClientIP() - for _, ip := range strings.Split(status.Value, ",") { - if len(ip) == 0 { - continue - } - if ip == clientIP || (strings.Contains(ip, "/") && common.CheckIpInCidr(ip, clientIP)) { - return true - } - } - return false -} - -func checkSession(c *gin.Context) bool { - _, err := global.SESSION.Get(c) - return err == nil -} - func setWebStatic(rootRouter *gin.RouterGroup) { rootRouter.StaticFS("/public", http.FS(web.Favicon)) rootRouter.StaticFS("/favicon.ico", http.FS(web.Favicon)) @@ -172,23 +41,14 @@ func setWebStatic(rootRouter *gin.RouterGroup) { rootRouter.GET("/"+entrance, func(c *gin.Context) { currentEntrance := authService.GetSecurityEntrance() if currentEntrance != entrance { - handleNoRoute(c, "") + security.HandleNotSecurity(c, "") return } - toIndexHtml(c) + security.ToIndexHtml(c) }) } rootRouter.GET("/", func(c *gin.Context) { - if !checkEntrance(c) && !checkSession(c) { - handleNoRoute(c, "") - return - } - if !checkBindDomain(c) { - handleNoRoute(c, "err_domain") - return - } - if !checkIPLimit(c) { - handleNoRoute(c, "err_ip_limit") + if !security.CheckSecurity(c) { return } entrance = authService.GetSecurityEntrance() @@ -229,26 +89,13 @@ func Routers() *gin.Engine { router.InitRouter(PrivateGroup) } - Router.Use(middleware.SessionAuth()) + Router.Use(middleware.ApiAuth()) Router.Use(Proxy()) Router.NoRoute(func(c *gin.Context) { - if !checkBindDomain(c) { - handleNoRoute(c, "err_domain") + if !security.HandleNotRoute(c) { return } - if !checkIPLimit(c) { - handleNoRoute(c, "err_ip_limit") - return - } - if checkFrontendPath(c) { - toIndexHtml(c) - return - } - if isEntrancePath(c) { - toIndexHtml(c) - return - } - handleNoRoute(c, "") + security.HandleNotSecurity(c, "") }) return Router diff --git a/core/middleware/api_auth.go b/core/middleware/api_auth.go new file mode 100644 index 000000000..31d03ded0 --- /dev/null +++ b/core/middleware/api_auth.go @@ -0,0 +1,117 @@ +package middleware + +import ( + "crypto/md5" + "encoding/hex" + "github.com/1Panel-dev/1Panel/core/app/api/v2/helper" + "github.com/1Panel-dev/1Panel/core/constant" + "github.com/1Panel-dev/1Panel/core/global" + "github.com/1Panel-dev/1Panel/core/utils/common" + "github.com/gin-gonic/gin" + "net" + "strconv" + "strings" + "time" +) + +func ApiAuth() gin.HandlerFunc { + return func(c *gin.Context) { + if strings.HasPrefix(c.Request.URL.Path, "/api/v2/core/auth") { + c.Next() + return + } + + panelToken := c.GetHeader("1Panel-Token") + panelTimestamp := c.GetHeader("1Panel-Timestamp") + if panelToken != "" || panelTimestamp != "" { + if global.Api.ApiInterfaceStatus == constant.StatusEnable { + clientIP := c.ClientIP() + if !isValid1PanelTimestamp(panelTimestamp) { + helper.BadAuth(c, "ErrApiConfigKeyTimeInvalid", nil) + return + } + if !isValid1PanelToken(panelToken, panelTimestamp) { + helper.BadAuth(c, "ErrApiConfigKeyInvalid", nil) + return + } + + if !isIPInWhiteList(clientIP) { + helper.BadAuth(c, "ErrApiConfigIPInvalid", nil) + return + } + c.Next() + return + } else { + helper.BadAuth(c, "ErrApiConfigStatusInvalid", nil) + return + } + } + } +} + +func isValid1PanelTimestamp(panelTimestamp string) bool { + apiKeyValidityTime := global.Api.ApiKeyValidityTime + apiTime, err := strconv.Atoi(apiKeyValidityTime) + if err != nil || apiTime < 0 { + global.LOG.Errorf("apiTime %d, err: %v", apiTime, err) + return false + } + if apiTime == 0 { + return true + } + panelTime, err := strconv.ParseInt(panelTimestamp, 10, 64) + if err != nil { + global.LOG.Errorf("panelTimestamp %s, panelTime %d, apiTime %d, err: %v", panelTimestamp, apiTime, panelTime, err) + return false + } + nowTime := time.Now().Unix() + tolerance := int64(60) + if panelTime > nowTime+tolerance { + global.LOG.Errorf("Valid Panel Timestamp, apiTime %d, panelTime %d, nowTime %d, err: %v", apiTime, panelTime, nowTime, err) + return false + } + return nowTime-panelTime <= int64(apiTime)*60+tolerance +} + +func isValid1PanelToken(panelToken string, panelTimestamp string) bool { + system1PanelToken := global.Api.ApiKey + return panelToken == GenerateMD5("1panel"+system1PanelToken+panelTimestamp) +} + +func isIPInWhiteList(clientIP string) bool { + ipWhiteString := global.Api.IpWhiteList + if len(ipWhiteString) == 0 { + global.LOG.Error("IP whitelist is empty") + return false + } + ipWhiteList, ipErr := common.HandleIPList(ipWhiteString) + if ipErr != nil { + global.LOG.Errorf("Failed to handle IP list: %v", ipErr) + return false + } + clientParsedIP := net.ParseIP(clientIP) + if clientParsedIP == nil { + return false + } + iPv4 := clientParsedIP.To4() + iPv6 := clientParsedIP.To16() + for _, cidr := range ipWhiteList { + if (iPv4 != nil && (cidr == "0.0.0.0" || cidr == "0.0.0.0/0" || iPv4.String() == cidr)) || (iPv6 != nil && (cidr == "::/0" || iPv6.String() == cidr)) { + return true + } + _, ipNet, err := net.ParseCIDR(cidr) + if err != nil { + continue + } + if (iPv4 != nil && ipNet.Contains(iPv4)) || (iPv6 != nil && ipNet.Contains(iPv6)) { + return true + } + } + return false +} + +func GenerateMD5(param string) string { + hash := md5.New() + hash.Write([]byte(param)) + return hex.EncodeToString(hash.Sum(nil)) +} diff --git a/core/middleware/session.go b/core/middleware/session.go index d92a66eea..40550f815 100644 --- a/core/middleware/session.go +++ b/core/middleware/session.go @@ -1,19 +1,13 @@ package middleware import ( - "crypto/md5" - "encoding/hex" - "net" - "strconv" - "strings" - "time" - "github.com/1Panel-dev/1Panel/core/app/api/v2/helper" "github.com/1Panel-dev/1Panel/core/app/repo" "github.com/1Panel-dev/1Panel/core/constant" "github.com/1Panel-dev/1Panel/core/global" - "github.com/1Panel-dev/1Panel/core/utils/common" "github.com/gin-gonic/gin" + "strconv" + "strings" ) func SessionAuth() gin.HandlerFunc { @@ -23,32 +17,6 @@ func SessionAuth() gin.HandlerFunc { return } - panelToken := c.GetHeader("1Panel-Token") - panelTimestamp := c.GetHeader("1Panel-Timestamp") - if panelToken != "" || panelTimestamp != "" { - if global.Api.ApiInterfaceStatus == constant.StatusEnable { - clientIP := c.ClientIP() - if !isValid1PanelTimestamp(panelTimestamp) { - helper.BadAuth(c, "ErrApiConfigKeyTimeInvalid", nil) - return - } - if !isValid1PanelToken(panelToken, panelTimestamp) { - helper.BadAuth(c, "ErrApiConfigKeyInvalid", nil) - return - } - - if !isIPInWhiteList(clientIP) { - helper.BadAuth(c, "ErrApiConfigIPInvalid", nil) - return - } - c.Next() - return - } else { - helper.BadAuth(c, "ErrApiConfigStatusInvalid", nil) - return - } - } - psession, err := global.SESSION.Get(c) if err != nil { helper.BadAuth(c, "ErrNotLogin", err) @@ -70,70 +38,3 @@ func SessionAuth() gin.HandlerFunc { c.Next() } } - -func isValid1PanelTimestamp(panelTimestamp string) bool { - apiKeyValidityTime := global.Api.ApiKeyValidityTime - apiTime, err := strconv.Atoi(apiKeyValidityTime) - if err != nil || apiTime < 0 { - global.LOG.Errorf("apiTime %d, err: %v", apiTime, err) - return false - } - if apiTime == 0 { - return true - } - panelTime, err := strconv.ParseInt(panelTimestamp, 10, 64) - if err != nil { - global.LOG.Errorf("panelTimestamp %s, panelTime %d, apiTime %d, err: %v", panelTimestamp, apiTime, panelTime, err) - return false - } - nowTime := time.Now().Unix() - tolerance := int64(60) - if panelTime > nowTime+tolerance { - global.LOG.Errorf("Valid Panel Timestamp, apiTime %d, panelTime %d, nowTime %d, err: %v", apiTime, panelTime, nowTime, err) - return false - } - return nowTime-panelTime <= int64(apiTime)*60+tolerance -} - -func isValid1PanelToken(panelToken string, panelTimestamp string) bool { - system1PanelToken := global.Api.ApiKey - return panelToken == GenerateMD5("1panel"+system1PanelToken+panelTimestamp) -} - -func isIPInWhiteList(clientIP string) bool { - ipWhiteString := global.Api.IpWhiteList - if len(ipWhiteString) == 0 { - global.LOG.Error("IP whitelist is empty") - return false - } - ipWhiteList, ipErr := common.HandleIPList(ipWhiteString) - if ipErr != nil { - global.LOG.Errorf("Failed to handle IP list: %v", ipErr) - return false - } - clientParsedIP := net.ParseIP(clientIP) - if clientParsedIP == nil { - return false - } - iPv4 := clientParsedIP.To4() - iPv6 := clientParsedIP.To16() - for _, cidr := range ipWhiteList { - if (iPv4 != nil && (cidr == "0.0.0.0" || cidr == "0.0.0.0/0" || iPv4.String() == cidr)) || (iPv6 != nil && (cidr == "::/0" || iPv6.String() == cidr)) { - return true - } - _, ipNet, err := net.ParseCIDR(cidr) - if err != nil { - continue - } - if (iPv4 != nil && ipNet.Contains(iPv4)) || (iPv6 != nil && ipNet.Contains(iPv6)) { - return true - } - } - return false -} - -func GenerateMD5(param string) string { - hash := md5.New() - hash.Write([]byte(param)) - return hex.EncodeToString(hash.Sum(nil)) -} diff --git a/core/utils/security/security.go b/core/utils/security/security.go new file mode 100644 index 000000000..2fcc6867d --- /dev/null +++ b/core/utils/security/security.go @@ -0,0 +1,176 @@ +package security + +import ( + "encoding/base64" + "fmt" + "github.com/1Panel-dev/1Panel/core/app/repo" + "github.com/1Panel-dev/1Panel/core/app/service" + "github.com/1Panel-dev/1Panel/core/cmd/server/res" + "github.com/1Panel-dev/1Panel/core/cmd/server/web" + "github.com/1Panel-dev/1Panel/core/constant" + "github.com/1Panel-dev/1Panel/core/global" + "github.com/1Panel-dev/1Panel/core/utils/common" + "github.com/gin-gonic/gin" + "net/http" + "regexp" + "strconv" + "strings" +) + +func HandleNotRoute(c *gin.Context) bool { + if !checkBindDomain(c) { + HandleNotSecurity(c, "err_domain") + return false + } + if !checkIPLimit(c) { + HandleNotSecurity(c, "err_ip_limit") + return false + } + if checkFrontendPath(c) { + ToIndexHtml(c) + return false + } + if isEntrancePath(c) { + ToIndexHtml(c) + return false + } + return true +} + +func CheckSecurity(c *gin.Context) bool { + if !checkEntrance(c) && !checkSession(c) { + HandleNotSecurity(c, "") + return false + } + if !checkBindDomain(c) { + HandleNotSecurity(c, "err_domain") + return false + } + if !checkIPLimit(c) { + HandleNotSecurity(c, "err_ip_limit") + return false + } + return true +} + +func ToIndexHtml(c *gin.Context) { + c.Writer.Header().Set("Content-Type", "text/html; charset=utf-8") + c.Writer.WriteHeader(http.StatusOK) + _, _ = c.Writer.Write(web.IndexByte) + c.Writer.Flush() +} + +func isEntrancePath(c *gin.Context) bool { + entrance := service.NewIAuthService().GetSecurityEntrance() + if entrance != "" && strings.TrimSuffix(c.Request.URL.Path, "/") == "/"+entrance { + return true + } + return false +} + +func checkEntrance(c *gin.Context) bool { + authService := service.NewIAuthService() + entrance := authService.GetSecurityEntrance() + if entrance == "" { + return true + } + + cookieValue, err := c.Cookie("SecurityEntrance") + if err != nil { + return false + } + entranceValue, err := base64.StdEncoding.DecodeString(cookieValue) + if err != nil { + return false + } + return string(entranceValue) == entrance +} + +func HandleNotSecurity(c *gin.Context, resType string) { + resPage, err := service.NewIAuthService().GetResponsePage() + if err != nil { + c.String(http.StatusInternalServerError, "Internal Server Error") + return + } + if resPage == "444" { + c.String(444, "") + return + } + + file := fmt.Sprintf("html/%s.html", resPage) + if resPage == "200" && resType != "" { + file = fmt.Sprintf("html/200_%s.html", resType) + } + data, err := res.ErrorMsg.ReadFile(file) + if err != nil { + c.String(http.StatusInternalServerError, "Internal Server Error") + return + } + statusCode, err := strconv.Atoi(resPage) + if err != nil { + c.String(http.StatusInternalServerError, "Internal Server Error") + return + } + c.Data(statusCode, "text/html; charset=utf-8", data) +} + +func isFrontendPath(c *gin.Context) bool { + reqUri := strings.TrimSuffix(c.Request.URL.Path, "/") + if _, ok := constant.WebUrlMap[reqUri]; ok { + return true + } + for _, route := range constant.DynamicRoutes { + if match, _ := regexp.MatchString(route, reqUri); match { + return true + } + } + return false +} + +func checkFrontendPath(c *gin.Context) bool { + if !isFrontendPath(c) { + return false + } + authService := service.NewIAuthService() + if authService.GetSecurityEntrance() != "" { + return authService.IsLogin(c) + } + return true +} + +func checkBindDomain(c *gin.Context) bool { + settingRepo := repo.NewISettingRepo() + status, _ := settingRepo.Get(repo.WithByKey("BindDomain")) + if len(status.Value) == 0 { + return true + } + domains := c.Request.Host + parts := strings.Split(c.Request.Host, ":") + if len(parts) > 0 { + domains = parts[0] + } + return domains == status.Value +} + +func checkIPLimit(c *gin.Context) bool { + settingRepo := repo.NewISettingRepo() + status, _ := settingRepo.Get(repo.WithByKey("AllowIPs")) + if len(status.Value) == 0 { + return true + } + clientIP := c.ClientIP() + for _, ip := range strings.Split(status.Value, ",") { + if len(ip) == 0 { + continue + } + if ip == clientIP || (strings.Contains(ip, "/") && common.CheckIpInCidr(ip, clientIP)) { + return true + } + } + return false +} + +func checkSession(c *gin.Context) bool { + _, err := global.SESSION.Get(c) + return err == nil +} diff --git a/frontend/src/components/log/task/index.vue b/frontend/src/components/log/task/index.vue index 7c901b2d8..edca31335 100644 --- a/frontend/src/components/log/task/index.vue +++ b/frontend/src/components/log/task/index.vue @@ -44,6 +44,7 @@ const config = reactive({ }); const open = ref(false); const showTail = ref(true); +const emit = defineEmits(['close']); const openWithTaskID = (id: string, tail: boolean) => { config.taskID = id; @@ -65,7 +66,7 @@ const openWithResourceID = (taskType: string, taskOperate: string, resourceID: n const handleClose = () => { open.value = false; - bus.emit('close', true); + emit('close', true); bus.emit('refreshTask', true); }; diff --git a/frontend/src/routers/index.ts b/frontend/src/routers/index.ts index 2c278edce..9e38c1c39 100644 --- a/frontend/src/routers/index.ts +++ b/frontend/src/routers/index.ts @@ -42,13 +42,14 @@ router.beforeEach((to, from, next) => { if (to.path === '/apps/all' && to.query.install != undefined) { return next(); } + const activeMenuKey = 'cachedRoute' + (to.meta.activeMenu || ''); if (to.query.uncached != undefined) { const query = { ...to.query }; delete query.uncached; + localStorage.removeItem(activeMenuKey); return next({ path: to.path, query }); } - const activeMenuKey = 'cachedRoute' + (to.meta.activeMenu || ''); const cachedRoute = localStorage.getItem(activeMenuKey); if ( to.meta.activeMenu && diff --git a/frontend/src/views/app-store/apps/index.vue b/frontend/src/views/app-store/apps/index.vue index 68b0bf2d8..83220c8ce 100644 --- a/frontend/src/views/app-store/apps/index.vue +++ b/frontend/src/views/app-store/apps/index.vue @@ -185,7 +185,7 @@ import Install from '../detail/install/index.vue'; import router from '@/routers'; import { MsgSuccess } from '@/utils/message'; import { GlobalStore } from '@/store'; -import { newUUID } from '@/utils/util'; +import { newUUID, jumpToPath } from '@/utils/util'; import Detail from '../detail/index.vue'; import TaskLog from '@/components/log/task/index.vue'; import { storeToRefs } from 'pinia'; @@ -254,7 +254,7 @@ const openInstall = (app: App.App) => { case 'go': case 'python': case 'dotnet': - router.push({ path: '/websites/runtimes/' + app.type }); + jumpToPath(router, '/websites/runtimes/' + app.type); break; default: const params = { diff --git a/frontend/src/views/app-store/detail/index.vue b/frontend/src/views/app-store/detail/index.vue index 700ae523b..ee71fc953 100644 --- a/frontend/src/views/app-store/detail/index.vue +++ b/frontend/src/views/app-store/detail/index.vue @@ -72,7 +72,7 @@ import { ref } from 'vue'; import Install from './install/index.vue'; import router from '@/routers'; import { GlobalStore } from '@/store'; -import { computeSizeFromMB } from '@/utils/util'; +import { computeSizeFromMB, jumpToPath } from '@/utils/util'; import { storeToRefs } from 'pinia'; const globalStore = GlobalStore(); @@ -133,16 +133,12 @@ const toLink = (link: string) => { const openInstall = () => { switch (app.value.type) { case 'php': - router.push({ path: '/websites/runtimes/php' }); - break; case 'node': - router.push({ path: '/websites/runtimes/node' }); - break; case 'java': - router.push({ path: '/websites/runtimes/java' }); - break; case 'go': - router.push({ path: '/websites/runtimes/go' }); + case 'python': + case 'dotnet': + jumpToPath(router, '/websites/runtimes/' + app.value.type); break; default: const params = { diff --git a/frontend/src/views/app-store/installed/detail/index.vue b/frontend/src/views/app-store/installed/detail/index.vue index a6eb3c5a5..c221232f8 100644 --- a/frontend/src/views/app-store/installed/detail/index.vue +++ b/frontend/src/views/app-store/installed/detail/index.vue @@ -32,7 +32,7 @@
@@ -57,7 +57,7 @@ - +
diff --git a/frontend/src/views/app-store/installed/index.vue b/frontend/src/views/app-store/installed/index.vue index 2dd47eb17..81b31f100 100644 --- a/frontend/src/views/app-store/installed/index.vue +++ b/frontend/src/views/app-store/installed/index.vue @@ -354,7 +354,7 @@ - +