mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-23 00:00:52 +00:00
195 lines
5.6 KiB
Go
195 lines
5.6 KiB
Go
package middleware
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/1Panel-dev/1Panel/core/app/dto"
|
|
"github.com/1Panel-dev/1Panel/core/buserr"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
var (
|
|
demoRouteMu sync.RWMutex
|
|
|
|
demoReadOnlyPostRoutes = map[string]struct{}{
|
|
"/api/v2/dashboard/app/launcher/option": {},
|
|
"/api/v2/websites/config": {},
|
|
"/api/v2/websites/waf/config": {},
|
|
"/api/v2/websites/options": {},
|
|
"/api/v2/websites/rewrite": {},
|
|
"/api/v2/websites/dir": {},
|
|
"/api/v2/websites/proxies": {},
|
|
"/api/v2/websites/auths": {},
|
|
"/api/v2/websites/leech": {},
|
|
"/api/v2/websites/redirect": {},
|
|
"/api/v2/files/size": {},
|
|
"/api/v2/files/tree": {},
|
|
"/api/v2/toolbox/device/base": {},
|
|
"/api/v2/files/user/group": {},
|
|
"/api/v2/files/mount": {},
|
|
"/api/v2/hosts/ssh/log": {},
|
|
"/api/v2/toolbox/clam/base": {},
|
|
"/api/v2/backups/record/size": {},
|
|
"/api/v2/containers/info": {},
|
|
"/api/v2/containers/list": {},
|
|
"/api/v2/containers/list/byimage": {},
|
|
"/api/v2/containers/users": {},
|
|
"/api/v2/containers/files/size": {},
|
|
"/api/v2/logs/system/read": {},
|
|
"/api/v2/logs/tasks/read": {},
|
|
|
|
"/api/v2/core/auth/login": {},
|
|
"/api/v2/core/auth/mfalogin": {},
|
|
"/api/v2/core/auth/passkey/begin": {},
|
|
"/api/v2/core/auth/passkey/finish": {},
|
|
"/api/v2/core/logs/login": {},
|
|
"/api/v2/core/logs/operation": {},
|
|
"/api/v2/core/auth/logout": {},
|
|
"/api/v2/core/settings/search/base": {},
|
|
|
|
"/api/v2/apps/installed/loadport": {},
|
|
"/api/v2/apps/installed/check": {},
|
|
"/api/v2/apps/installed/conninfo": {},
|
|
"/api/v2/databases/common/info": {},
|
|
"/api/v2/databases/common/load/file": {},
|
|
"/api/v2/databases/load/file": {},
|
|
"/api/v2/databases/variables": {},
|
|
"/api/v2/databases/status": {},
|
|
"/api/v2/databases/baseinfo": {},
|
|
"/api/v2/backups/search/files": {},
|
|
"/api/v2/backups/record/search/bycronjob": {},
|
|
"/api/v2/cronjobs/search/records": {},
|
|
|
|
"/api/v2/xpack/waf/attack/stat": {},
|
|
"/api/v2/xpack/waf/config/website": {},
|
|
"/api/v2/xpack/waf/relation/stat": {},
|
|
|
|
"/api/v2/xpack/monitor/stat": {},
|
|
"/api/v2/xpack/monitor/visitors": {},
|
|
"/api/v2/xpack/monitor/visitors/loc": {},
|
|
"/api/v2/xpack/monitor/qps": {},
|
|
"/api/v2/xpack/monitor/logs/stat": {},
|
|
"/api/v2/xpack/monitor/websites": {},
|
|
"/api/v2/xpack/monitor/trend": {},
|
|
"/api/v2/xpack/monitor/rank": {},
|
|
"/api/v2/xpack/waf/cdn": {},
|
|
"/api/v2/xpack/tampers/search/log": {},
|
|
"/api/v2/xpack/tampers/search/file": {},
|
|
|
|
"/api/v2/core/nodes/list": {},
|
|
"/api/v2/core/xpack/nodes/search/upgrade/logs": {},
|
|
}
|
|
|
|
demoBlockedGetRoutes = map[string]struct{}{
|
|
"/api/v2/containers/exec": {},
|
|
"/api/v2/core/script/run": {},
|
|
"/api/v2/files/download": {},
|
|
"/api/v2/hosts/terminal/local": {},
|
|
"/api/v2/hosts/terminal/ssh": {},
|
|
"/api/v2/hosts/terminal/container": {},
|
|
"/api/v2/process/:pid": {},
|
|
}
|
|
)
|
|
|
|
const demoReadOnlyContextKey = "DEMO_READ_ONLY_REQUEST"
|
|
|
|
func RegisterDemoReadOnlyPostRoutes(paths ...string) {
|
|
demoRouteMu.Lock()
|
|
defer demoRouteMu.Unlock()
|
|
for _, routePath := range paths {
|
|
if routePath != "" {
|
|
demoReadOnlyPostRoutes[routePath] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
|
|
func RegisterDemoBlockedGetRoutes(paths ...string) {
|
|
demoRouteMu.Lock()
|
|
defer demoRouteMu.Unlock()
|
|
for _, routePath := range paths {
|
|
if routePath != "" {
|
|
demoBlockedGetRoutes[routePath] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
|
|
func demoRequestPath(c *gin.Context) string {
|
|
if routePath := c.FullPath(); routePath != "" {
|
|
return routePath
|
|
}
|
|
return c.Request.URL.Path
|
|
}
|
|
|
|
func isDemoSearchRoute(routePath string) bool {
|
|
return strings.HasSuffix(routePath, "/search") || strings.HasSuffix(routePath, "/ai-search")
|
|
}
|
|
|
|
func isDemoReadOnlyPost(routePath string) bool {
|
|
if isDemoSearchRoute(routePath) {
|
|
return true
|
|
}
|
|
demoRouteMu.RLock()
|
|
defer demoRouteMu.RUnlock()
|
|
for pattern := range demoReadOnlyPostRoutes {
|
|
if matchDemoRoute(pattern, routePath) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isDemoBlockedGet(routePath string) bool {
|
|
demoRouteMu.RLock()
|
|
defer demoRouteMu.RUnlock()
|
|
for pattern := range demoBlockedGetRoutes {
|
|
if matchDemoRoute(pattern, routePath) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func matchDemoRoute(pattern, routePath string) bool {
|
|
if pattern == routePath {
|
|
return true
|
|
}
|
|
patternParts := strings.Split(strings.Trim(pattern, "/"), "/")
|
|
pathParts := strings.Split(strings.Trim(routePath, "/"), "/")
|
|
if len(patternParts) != len(pathParts) {
|
|
return false
|
|
}
|
|
for i := range patternParts {
|
|
if strings.HasPrefix(patternParts[i], ":") {
|
|
continue
|
|
}
|
|
if patternParts[i] != pathParts[i] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func DemoHandle() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
routePath := demoRequestPath(c)
|
|
if c.Request.Method == http.MethodGet && !isDemoBlockedGet(routePath) {
|
|
c.Set(demoReadOnlyContextKey, true)
|
|
c.Next()
|
|
return
|
|
}
|
|
if c.Request.Method == http.MethodPost && isDemoReadOnlyPost(routePath) {
|
|
c.Set(demoReadOnlyContextKey, true)
|
|
c.Next()
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusInternalServerError, dto.Response{
|
|
Code: http.StatusInternalServerError,
|
|
Message: buserr.New("ErrDemoEnvironment").Error(),
|
|
})
|
|
c.Abort()
|
|
}
|
|
}
|