From 02d22ba63f62ac26301bb6e94e9a09c473616849 Mon Sep 17 00:00:00 2001 From: KOMATA <20227709+HynoR@users.noreply.github.com> Date: Tue, 11 Nov 2025 16:14:09 +0800 Subject: [PATCH] feat: refactor regex usage across multiple files and centralize patterns in a new utility (#10896) --- agent/app/service/app_utils.go | 5 +- agent/app/service/container.go | 4 +- agent/app/service/database_mysql.go | 4 +- agent/app/service/disk_utils.go | 15 ++-- agent/app/service/recycle_bin.go | 5 +- agent/app/service/runtime.go | 9 +- agent/app/service/runtime_utils.go | 8 +- agent/app/service/tensorrt_llm.go | 24 +++--- agent/app/service/website.go | 20 ++--- agent/app/service/website_utils.go | 5 +- agent/init/firewall/firwall.go | 3 + agent/init/validator/validator.go | 14 +--- agent/server/server.go | 2 + agent/utils/alert/alert.go | 20 +++-- agent/utils/cloud_storage/client/cos.go | 6 +- agent/utils/common/common.go | 12 +-- agent/utils/docker/compose.go | 10 +-- agent/utils/firewall/client/firewalld.go | 8 +- agent/utils/firewall/client/iptables.go | 10 +-- .../utils/firewall/client/iptables/common.go | 10 +-- .../utils/firewall/client/iptables/forward.go | 4 +- agent/utils/nginx/components/location.go | 9 +- agent/utils/re/re.go | 84 +++++++++++++++++++ 23 files changed, 181 insertions(+), 110 deletions(-) create mode 100644 agent/utils/re/re.go diff --git a/agent/app/service/app_utils.go b/agent/app/service/app_utils.go index d163e63b8..4fbf5419d 100644 --- a/agent/app/service/app_utils.go +++ b/agent/app/service/app_utils.go @@ -15,7 +15,6 @@ import ( "path" "path/filepath" "reflect" - "regexp" "strconv" "strings" "time" @@ -40,6 +39,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/files" "github.com/1Panel-dev/1Panel/agent/utils/nginx" "github.com/1Panel-dev/1Panel/agent/utils/nginx/parser" + "github.com/1Panel-dev/1Panel/agent/utils/re" "github.com/1Panel-dev/1Panel/agent/utils/req_helper" "github.com/1Panel-dev/1Panel/agent/utils/xpack" "github.com/compose-spec/compose-go/v2/types" @@ -1820,8 +1820,7 @@ func getAppCommonConfig(envs map[string]interface{}) request.AppContainerConfig config.CpuQuota = 0 } if memLimit, ok := envs[constant.MemoryLimit]; ok { - re := regexp.MustCompile(`(\d+)([A-Za-z]+)`) - matches := re.FindStringSubmatch(memLimit.(string)) + matches := re.GetRegex(re.NumberAlphaPattern).FindStringSubmatch(memLimit.(string)) if len(matches) == 3 { num, err := strconv.ParseFloat(matches[1], 64) if err == nil { diff --git a/agent/app/service/container.go b/agent/app/service/container.go index 8c1e2897f..7f41c07ac 100644 --- a/agent/app/service/container.go +++ b/agent/app/service/container.go @@ -12,7 +12,6 @@ import ( "os" "os/exec" "path/filepath" - "regexp" "sort" "strconv" "strings" @@ -34,6 +33,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/cmd" "github.com/1Panel-dev/1Panel/agent/utils/common" "github.com/1Panel-dev/1Panel/agent/utils/docker" + "github.com/1Panel-dev/1Panel/agent/utils/re" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/build" "github.com/docker/docker/api/types/container" @@ -1166,7 +1166,7 @@ func (u *ContainerService) DownloadContainerLogs(containerType, container, since errCh := make(chan error) go func() { scanner := bufio.NewScanner(stdout) - var ansiRegex = regexp.MustCompile(`\x1b\[[0-9;?]*[A-Za-z]|\x1b=|\x1b>`) + var ansiRegex = re.GetRegex(re.AnsiEscapePattern) for scanner.Scan() { line := scanner.Text() cleanLine := ansiRegex.ReplaceAllString(line, "") diff --git a/agent/app/service/database_mysql.go b/agent/app/service/database_mysql.go index b8b4ec10e..2bfe5cc79 100644 --- a/agent/app/service/database_mysql.go +++ b/agent/app/service/database_mysql.go @@ -7,7 +7,6 @@ import ( "os" "os/exec" "path/filepath" - "regexp" "strconv" "strings" "time" @@ -24,6 +23,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/compose" "github.com/1Panel-dev/1Panel/agent/utils/encrypt" "github.com/1Panel-dev/1Panel/agent/utils/mysql" + "github.com/1Panel-dev/1Panel/agent/utils/re" "github.com/1Panel-dev/1Panel/agent/utils/mysql/client" _ "github.com/go-sql-driver/mysql" "github.com/jinzhu/copier" @@ -623,7 +623,7 @@ func updateMyCnf(oldFiles []string, group string, param string, value interface{ isOn := false hasGroup := false hasKey := false - regItem, _ := regexp.Compile(`\[*\]`) + regItem := re.GetRegex(re.MysqlGroupPattern) var newFiles []string i := 0 for _, line := range oldFiles { diff --git a/agent/app/service/disk_utils.go b/agent/app/service/disk_utils.go index 44e4f6ebe..de178cc37 100644 --- a/agent/app/service/disk_utils.go +++ b/agent/app/service/disk_utils.go @@ -3,15 +3,16 @@ package service import ( "bufio" "fmt" + "os" + "os/exec" + "strconv" + "strings" + "github.com/1Panel-dev/1Panel/agent/app/dto" "github.com/1Panel-dev/1Panel/agent/app/dto/response" "github.com/1Panel-dev/1Panel/agent/buserr" "github.com/1Panel-dev/1Panel/agent/utils/cmd" - "os" - "os/exec" - "regexp" - "strconv" - "strings" + "github.com/1Panel-dev/1Panel/agent/utils/re" ) func organizeDiskInfo(diskInfos []response.DiskBasicInfo) response.CompleteDiskInfo { @@ -205,12 +206,10 @@ func getDiskType(rota string) string { return "Unknown" } -var kvRe = regexp.MustCompile(`([A-Za-z0-9_]+)=("([^"\\]|\\.)*"|[^ \t]+)`) - func parseKeyValuePairs(line string) map[string]string { fields := make(map[string]string) - matches := kvRe.FindAllStringSubmatch(line, -1) + matches := re.GetRegex(re.DiskKeyValuePattern).FindAllStringSubmatch(line, -1) for _, m := range matches { key := m[1] raw := m[2] diff --git a/agent/app/service/recycle_bin.go b/agent/app/service/recycle_bin.go index e1c888261..efcbb2e23 100644 --- a/agent/app/service/recycle_bin.go +++ b/agent/app/service/recycle_bin.go @@ -5,7 +5,6 @@ import ( "math" "os" "path" - "regexp" "strconv" "strings" "time" @@ -17,6 +16,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/constant" "github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/utils/files" + "github.com/1Panel-dev/1Panel/agent/utils/re" "github.com/shirou/gopsutil/v4/disk" ) @@ -184,8 +184,7 @@ func createClashDir(clashDir string) error { } func getRecycleBinDTOFromName(filename string) (*response.RecycleBinDTO, error) { - r := regexp.MustCompile(`_1p_file_1p_(.+)_p_(\d+)_(\d+)`) - matches := r.FindStringSubmatch(filename) + matches := re.GetRegex(re.RecycleBinFilePattern).FindStringSubmatch(filename) if len(matches) != 4 { return nil, fmt.Errorf("invalid filename format") } diff --git a/agent/app/service/runtime.go b/agent/app/service/runtime.go index 5a4f285ff..0b7868867 100644 --- a/agent/app/service/runtime.go +++ b/agent/app/service/runtime.go @@ -35,6 +35,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/docker" "github.com/1Panel-dev/1Panel/agent/utils/env" "github.com/1Panel-dev/1Panel/agent/utils/files" + "github.com/1Panel-dev/1Panel/agent/utils/re" "github.com/pkg/errors" "github.com/subosito/gotenv" ) @@ -244,8 +245,10 @@ func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.Runt runtimeDTO.Params[k] = v if strings.Contains(k, "CONTAINER_PORT") || strings.Contains(k, "HOST_PORT") { if strings.Contains(k, "CONTAINER_PORT") { - r := regexp.MustCompile(`_(\d+)$`) - matches := r.FindStringSubmatch(k) + matches := re.GetRegex(re.TrailingDigitsPattern).FindStringSubmatch(k) + if len(matches) < 2 { + continue + } containerPort, err := strconv.Atoi(v) if err != nil { continue @@ -829,7 +832,7 @@ func (r *RuntimeService) GetPHPConfig(id uint) (*response.PHPConfig, error) { if strings.HasPrefix(line, ";") { continue } - matches := regexp.MustCompile(`^\s*([a-z_]+)\s*=\s*(.*)$`).FindStringSubmatch(line) + matches := re.GetRegex(re.PhpAssignmentPattern).FindStringSubmatch(line) if len(matches) == 3 { params[matches[1]] = matches[2] } diff --git a/agent/app/service/runtime_utils.go b/agent/app/service/runtime_utils.go index e1d19a39c..50502b6ab 100644 --- a/agent/app/service/runtime_utils.go +++ b/agent/app/service/runtime_utils.go @@ -12,7 +12,6 @@ import ( "os/exec" "path" "path/filepath" - "regexp" "strconv" "strings" "time" @@ -34,6 +33,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/compose" "github.com/1Panel-dev/1Panel/agent/utils/docker" "github.com/1Panel-dev/1Panel/agent/utils/files" + "github.com/1Panel-dev/1Panel/agent/utils/re" "github.com/pkg/errors" "github.com/subosito/gotenv" "gopkg.in/yaml.v3" @@ -979,8 +979,10 @@ func handleRuntimeDTO(res *response.RuntimeDTO, runtime model.Runtime) error { for k, v := range envs { if strings.Contains(k, "CONTAINER_PORT") || strings.Contains(k, "HOST_PORT") { if strings.Contains(k, "CONTAINER_PORT") { - r := regexp.MustCompile(`_(\d+)$`) - matches := r.FindStringSubmatch(k) + matches := re.GetRegex(re.TrailingDigitsPattern).FindStringSubmatch(k) + if len(matches) < 2 { + return fmt.Errorf("invalid container port key: %s", k) + } containerPort, err := strconv.Atoi(v) if err != nil { return err diff --git a/agent/app/service/tensorrt_llm.go b/agent/app/service/tensorrt_llm.go index d924cff98..305937e28 100644 --- a/agent/app/service/tensorrt_llm.go +++ b/agent/app/service/tensorrt_llm.go @@ -3,6 +3,15 @@ package service import ( "errors" "fmt" + "os" + "path" + "path/filepath" + "strconv" + "strings" + + "github.com/subosito/gotenv" + "gopkg.in/yaml.v3" + "github.com/1Panel-dev/1Panel/agent/app/dto/request" "github.com/1Panel-dev/1Panel/agent/app/dto/response" "github.com/1Panel-dev/1Panel/agent/app/model" @@ -15,14 +24,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/docker" "github.com/1Panel-dev/1Panel/agent/utils/env" "github.com/1Panel-dev/1Panel/agent/utils/files" - "github.com/subosito/gotenv" - "gopkg.in/yaml.v3" - "os" - "path" - "path/filepath" - "regexp" - "strconv" - "strings" + "github.com/1Panel-dev/1Panel/agent/utils/re" ) type TensorRTLLMService struct{} @@ -61,8 +63,10 @@ func (t TensorRTLLMService) Page(req request.TensorRTLLMSearch) response.TensorR for k, v := range envs { if strings.Contains(k, "CONTAINER_PORT") || strings.Contains(k, "HOST_PORT") { if strings.Contains(k, "CONTAINER_PORT") { - r := regexp.MustCompile(`_(\d+)$`) - matches := r.FindStringSubmatch(k) + matches := re.GetRegex(re.TrailingDigitsPattern).FindStringSubmatch(k) + if len(matches) < 2 { + continue + } containerPort, err := strconv.Atoi(v) if err != nil { continue diff --git a/agent/app/service/website.go b/agent/app/service/website.go index 29ae7c7e8..f7d1a6b2b 100644 --- a/agent/app/service/website.go +++ b/agent/app/service/website.go @@ -14,7 +14,6 @@ import ( "os" "path" "reflect" - "regexp" "sort" "strconv" "strings" @@ -46,6 +45,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/nginx" "github.com/1Panel-dev/1Panel/agent/utils/nginx/components" "github.com/1Panel-dev/1Panel/agent/utils/nginx/parser" + "github.com/1Panel-dev/1Panel/agent/utils/re" "golang.org/x/crypto/bcrypt" ) @@ -1859,27 +1859,24 @@ func (w WebsiteService) GetProxyCache(id uint) (res response.NginxProxyCache, er if len(params) == 0 { return } - zoneRegexp := regexp.MustCompile(`keys_zone=proxy_cache_zone_of_[\w.]+:(\d+)([kmgt]?)`) - sizeRegexp := regexp.MustCompile(`max_size=([0-9.]+)([kmgt]?)`) - inactiveRegexp := regexp.MustCompile(`inactive=(\d+)([smhd])`) for _, param := range params { - if match, _ := regexp.MatchString(`keys_zone=proxy_cache_zone_of_[\w.]+:\d+[kmgt]?`, param); match { - matches := zoneRegexp.FindStringSubmatch(param) + if re.GetRegex(re.ProxyCacheZonePattern).MatchString(param) { + matches := re.GetRegex(re.ProxyCacheZonePattern).FindStringSubmatch(param) if len(matches) > 0 { res.ShareCache, _ = strconv.Atoi(matches[1]) res.ShareCacheUnit = matches[2] } } - if match, _ := regexp.MatchString(`max_size=\d+(\.\d+)?[kmgt]?`, param); match { - matches := sizeRegexp.FindStringSubmatch(param) + if re.GetRegex(re.ProxyCacheMaxSizeValidationPattern).MatchString(param) { + matches := re.GetRegex(re.ProxyCacheMaxSizePattern).FindStringSubmatch(param) if len(matches) > 0 { res.CacheLimit, _ = strconv.ParseFloat(matches[1], 64) res.CacheLimitUnit = matches[2] } } - if match, _ := regexp.MatchString(`inactive=\d+[smhd]`, param); match { - matches := inactiveRegexp.FindStringSubmatch(param) + if re.GetRegex(re.ProxyCacheInactivePattern).MatchString(param) { + matches := re.GetRegex(re.ProxyCacheInactivePattern).FindStringSubmatch(param) if len(matches) > 0 { res.CacheExpire, _ = strconv.Atoi(matches[1]) res.CacheExpireUnit = matches[2] @@ -2598,8 +2595,7 @@ func (w WebsiteService) GetAntiLeech(id uint) (*response.NginxAntiLeechRes, erro } if lDir.GetName() == "expires" { res.Cache = true - re := regexp.MustCompile(`^(\d+)(\w+)$`) - matches := re.FindStringSubmatch(lDir.GetParameters()[0]) + matches := re.GetRegex(re.NumberWordPattern).FindStringSubmatch(lDir.GetParameters()[0]) if matches == nil { continue } diff --git a/agent/app/service/website_utils.go b/agent/app/service/website_utils.go index e7ab54059..b92c18e35 100644 --- a/agent/app/service/website_utils.go +++ b/agent/app/service/website_utils.go @@ -9,7 +9,6 @@ import ( "os" "path" "path/filepath" - "regexp" "strconv" "strings" "syscall" @@ -34,6 +33,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/files" "github.com/1Panel-dev/1Panel/agent/utils/nginx" "github.com/1Panel-dev/1Panel/agent/utils/nginx/parser" + "github.com/1Panel-dev/1Panel/agent/utils/re" "github.com/pkg/errors" "gorm.io/gorm" ) @@ -1538,8 +1538,7 @@ func getServer(website model.Website) (*components.Server, error) { func parseTimeString(input string) (int, string, error) { input = strings.TrimSpace(input) - re := regexp.MustCompile(`^(\d+)([smhdw]?)$`) - matches := re.FindStringSubmatch(input) + matches := re.GetRegex(re.DurationWithOptionalUnitPattern).FindStringSubmatch(input) if len(matches) < 2 { return 0, "", fmt.Errorf("invalid time format: %s", input) diff --git a/agent/init/firewall/firwall.go b/agent/init/firewall/firwall.go index e1d1ab9e9..59981b6ea 100644 --- a/agent/init/firewall/firwall.go +++ b/agent/init/firewall/firwall.go @@ -3,6 +3,7 @@ package firewall import ( "github.com/1Panel-dev/1Panel/agent/utils/firewall" "github.com/1Panel-dev/1Panel/agent/utils/firewall/client/iptables" + "github.com/1Panel-dev/1Panel/agent/utils/re" ) func Init() { @@ -10,6 +11,8 @@ func Init() { if err != nil { return } + re.RegisterRegex(iptables.Chian1PanelBasicPortPattern) + re.RegisterRegex(iptables.Chain1PanelBasicAddressPattern) clientName := client.Name() if clientName == "ufw" || clientName == "iptables" { _ = iptables.LoadRulesFromFile(iptables.FilterTab, iptables.Chain1PanelForward, iptables.ForwardFileName) diff --git a/agent/init/validator/validator.go b/agent/init/validator/validator.go index bf75f9eff..6a58af582 100644 --- a/agent/init/validator/validator.go +++ b/agent/init/validator/validator.go @@ -1,10 +1,10 @@ package validator import ( - "regexp" "unicode" "github.com/1Panel-dev/1Panel/agent/global" + "github.com/1Panel-dev/1Panel/agent/utils/re" "github.com/go-playground/validator/v10" ) @@ -25,20 +25,12 @@ func Init() { func checkNamePattern(fl validator.FieldLevel) bool { value := fl.Field().String() - result, err := regexp.MatchString("^[a-zA-Z\u4e00-\u9fa5]{1}[a-zA-Z0-9_\u4e00-\u9fa5]{0,30}$", value) - if err != nil { - global.LOG.Errorf("regexp matchString failed, %v", err) - } - return result + return re.GetRegex(re.ValidatorNamePattern).MatchString(value) } func checkIpPattern(fl validator.FieldLevel) bool { value := fl.Field().String() - result, err := regexp.MatchString(`^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$`, value) - if err != nil { - global.LOG.Errorf("regexp check ip matchString failed, %v", err) - } - return result + return re.GetRegex(re.ValidatorIPPattern).MatchString(value) } func checkPasswordPattern(fl validator.FieldLevel) bool { diff --git a/agent/server/server.go b/agent/server/server.go index 63b96512a..5a56fe5fd 100644 --- a/agent/server/server.go +++ b/agent/server/server.go @@ -29,9 +29,11 @@ import ( "github.com/1Panel-dev/1Panel/agent/init/validator" "github.com/1Panel-dev/1Panel/agent/init/viper" "github.com/1Panel-dev/1Panel/agent/utils/encrypt" + "github.com/1Panel-dev/1Panel/agent/utils/re" ) func Start() { + re.Init() viper.Init() dir.Init() log.Init() diff --git a/agent/utils/alert/alert.go b/agent/utils/alert/alert.go index 43850204b..74b229c22 100644 --- a/agent/utils/alert/alert.go +++ b/agent/utils/alert/alert.go @@ -4,6 +4,15 @@ import ( "encoding/json" "errors" "fmt" + "net/http" + "os" + "os/exec" + "strings" + "sync" + "time" + + "github.com/jinzhu/copier" + "github.com/1Panel-dev/1Panel/agent/app/dto" "github.com/1Panel-dev/1Panel/agent/app/model" "github.com/1Panel-dev/1Panel/agent/app/repo" @@ -12,14 +21,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/i18n" "github.com/1Panel-dev/1Panel/agent/utils/email" - "github.com/jinzhu/copier" - "net/http" - "os" - "os/exec" - "regexp" - "strings" - "sync" - "time" + "github.com/1Panel-dev/1Panel/agent/utils/re" ) var cronJobAlertTypes = []string{"shell", "app", "website", "database", "directory", "log", "snapshot", "curl", "cutWebsiteLog", "clean", "ntp"} @@ -424,7 +426,7 @@ func FindRecentSuccessLoginNotInWhitelist(minutes int, whitelist []string) ([]st whitelistMap[ip] = struct{}{} } - ipRegex := regexp.MustCompile(`from\s+([0-9.]+)\s+port\s+(\d+)`) + ipRegex := re.GetRegex(re.AlertIPPattern) for _, line := range lines { line = strings.TrimSpace(line) diff --git a/agent/utils/cloud_storage/client/cos.go b/agent/utils/cloud_storage/client/cos.go index 7a4f996e7..9ee46f820 100644 --- a/agent/utils/cloud_storage/client/cos.go +++ b/agent/utils/cloud_storage/client/cos.go @@ -6,9 +6,10 @@ import ( "net/http" "net/url" "os" - "regexp" cosSDK "github.com/tencentyun/cos-go-sdk-v5" + + "github.com/1Panel-dev/1Panel/agent/utils/re" ) type cosClient struct { @@ -30,8 +31,7 @@ func NewCosClient(vars map[string]interface{}) (*cosClient, error) { endpointType := "cos" if len(endpoint) != 0 { - re := regexp.MustCompile(`.*cos-dualstack\..*`) - if re.MatchString(endpoint) { + if re.GetRegex(re.CosDualStackPattern).MatchString(endpoint) { endpointType = "cos-dualstack" } } diff --git a/agent/utils/common/common.go b/agent/utils/common/common.go index 23063185d..0fab2a42f 100644 --- a/agent/utils/common/common.go +++ b/agent/utils/common/common.go @@ -8,7 +8,6 @@ import ( "net" "os/exec" "reflect" - "regexp" "sort" "strconv" "strings" @@ -19,6 +18,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/buserr" "github.com/1Panel-dev/1Panel/agent/utils/cmd" + "github.com/1Panel-dev/1Panel/agent/utils/re" "golang.org/x/net/idna" ) @@ -303,12 +303,7 @@ func LoadTimeZoneByCmd() string { } func IsValidDomain(domain string) bool { - pattern := `^([\w\p{Han}\-\*]{1,100}\.){1,10}([\w\p{Han}\-]{1,24}|[\w\p{Han}\-]{1,24}\.[\w\p{Han}\-]{1,24})(:\d{1,5})?$` - match, err := regexp.MatchString(pattern, domain) - if err != nil { - return false - } - return match + return re.GetRegex(re.DomainPattern).MatchString(domain) } func ContainsChinese(text string) bool { @@ -398,8 +393,7 @@ func HandleIPList(content string) ([]string, error) { } func GetSystemVersion(versionString string) string { - re := regexp.MustCompile(`v(\d+\.\d+\.\d+)`) - match := re.FindStringSubmatch(versionString) + match := re.GetRegex(re.VersionPattern).FindStringSubmatch(versionString) if len(match) > 1 { return match[1] } diff --git a/agent/utils/docker/compose.go b/agent/utils/docker/compose.go index a2e43ac7e..6cab5e5d0 100644 --- a/agent/utils/docker/compose.go +++ b/agent/utils/docker/compose.go @@ -6,7 +6,6 @@ import ( "context" "fmt" "path" - "regexp" "strings" "github.com/compose-spec/compose-go/v2/loader" @@ -14,6 +13,8 @@ import ( "github.com/docker/compose/v2/pkg/api" "github.com/joho/godotenv" "gopkg.in/yaml.v3" + + "github.com/1Panel-dev/1Panel/agent/utils/re" ) type ComposeService struct { @@ -36,8 +37,7 @@ func GetComposeProject(projectName, workDir string, yml []byte, env []byte, skip Environment: envMap, } projectName = strings.ToLower(projectName) - reg, _ := regexp.Compile(`[^a-z0-9_-]+`) - projectName = reg.ReplaceAllString(projectName, "") + projectName = re.GetRegex(re.ComposeDisallowedCharsPattern).ReplaceAllString(projectName, "") project, err := loader.LoadWithContext(context.Background(), details, func(options *loader.Options) { options.SetProjectName(projectName, true) options.ResolvePaths = true @@ -141,9 +141,7 @@ func loadEnvFile(env []byte) (map[string]string, error) { } func replaceEnvVars(input string, envVars map[string]string) string { - re := regexp.MustCompile(`\$\{([^}]+)\}`) - - return re.ReplaceAllStringFunc(input, func(match string) string { + return re.GetRegex(re.ComposeEnvVarPattern).ReplaceAllStringFunc(input, func(match string) string { varName := match[2 : len(match)-1] if value, exists := envVars[varName]; exists { return value diff --git a/agent/utils/firewall/client/firewalld.go b/agent/utils/firewall/client/firewalld.go index 8d510ea40..22725b8a9 100644 --- a/agent/utils/firewall/client/firewalld.go +++ b/agent/utils/firewall/client/firewalld.go @@ -2,7 +2,6 @@ package client import ( "fmt" - "regexp" "strings" "sync" @@ -10,10 +9,9 @@ import ( "github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/utils/cmd" "github.com/1Panel-dev/1Panel/agent/utils/controller" + "github.com/1Panel-dev/1Panel/agent/utils/re" ) -var ForwardListRegex = regexp.MustCompile(`^port=(\d{1,5}):proto=(.+?):toport=(\d{1,5}):toaddr=(.*)$`) - type Firewall struct{} func NewFirewalld() (*Firewall, error) { @@ -124,8 +122,8 @@ func (f *Firewall) ListForward() ([]FireInfo, error) { line = strings.TrimFunc(line, func(r rune) bool { return r <= 32 }) - if ForwardListRegex.MatchString(line) { - match := ForwardListRegex.FindStringSubmatch(line) + if re.GetRegex(re.FirewalldForwardPattern).MatchString(line) { + match := re.GetRegex(re.FirewalldForwardPattern).FindStringSubmatch(line) if len(match) < 4 { continue } diff --git a/agent/utils/firewall/client/iptables.go b/agent/utils/firewall/client/iptables.go index cb1718a88..a2e7b8298 100644 --- a/agent/utils/firewall/client/iptables.go +++ b/agent/utils/firewall/client/iptables.go @@ -2,7 +2,6 @@ package client import ( "fmt" - "regexp" "strconv" "strings" @@ -10,11 +9,9 @@ import ( "github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/utils/cmd" "github.com/1Panel-dev/1Panel/agent/utils/firewall/client/iptables" + "github.com/1Panel-dev/1Panel/agent/utils/re" ) -var portRuleRegex = regexp.MustCompile(`-A\s+INPUT\s+-p\s+(\w+)(?:\s+-m\s+\w+)*\s+--dport\s+(\d+(?::\d+)?)\s+-j\s+(\w+)`) -var addressRuleRegex = regexp.MustCompile(`-A\s+(INPUT|OUTPUT)\s+-s\s+(\S+)\s+-j\s+(\w+)`) - type Iptables struct{} func NewIptables() (*Iptables, error) { @@ -70,7 +67,7 @@ func (i *Iptables) ListPort() ([]FireInfo, error) { var datas []FireInfo lines := strings.Split(stdout, "\n") - chainPortRegex := regexp.MustCompile(fmt.Sprintf(`-A\s+%s\s+(?:-s\s+(\S+)\s+)?-p\s+(\w+)(?:\s+-m\s+\w+)*\s+--dport\s+(\d+(?::\d+)?)\s+-j\s+(\w+)`, iptables.Chain1PanelBasic)) + chainPortRegex := re.GetRegex(iptables.Chian1PanelBasicPortPattern) for _, line := range lines { line = strings.TrimSpace(line) if !strings.HasPrefix(line, fmt.Sprintf("-A %s", iptables.Chain1PanelBasic)) { @@ -111,8 +108,7 @@ func (i *Iptables) ListAddress() ([]FireInfo, error) { lines := strings.Split(stdout, "\n") addressMap := make(map[string]FireInfo) - chainAddressRegex := regexp.MustCompile(fmt.Sprintf(`-A\s+%s\s+(?:-s\s+(\S+)|(?:-d\s+(\S+)))?\s+-j\s+(\w+)`, iptables.Chain1PanelBasic)) - + chainAddressRegex := re.GetRegex(iptables.Chain1PanelBasicAddressPattern) for _, line := range lines { line = strings.TrimSpace(line) if !strings.HasPrefix(line, fmt.Sprintf("-A %s", iptables.Chain1PanelBasic)) { diff --git a/agent/utils/firewall/client/iptables/common.go b/agent/utils/firewall/client/iptables/common.go index 742c0f1cc..f932ea1a1 100644 --- a/agent/utils/firewall/client/iptables/common.go +++ b/agent/utils/firewall/client/iptables/common.go @@ -2,7 +2,6 @@ package iptables import ( "fmt" - "regexp" "strconv" "strings" "time" @@ -31,10 +30,6 @@ const ( AllowSSH = "-p tcp --dport ssh -j ACCEPT" ) -var ( - natListRegex = regexp.MustCompile(`^(\d+)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)(?:\s+(.+?) .+?:(\d{1,5}(?::\d+)?).+?[ :](.+-.+|(?:.+:)?\d{1,5}(?:-\d{1,5})?))?$`) -) - const ( ACCEPT = "ACCEPT" DROP = "DROP" @@ -47,6 +42,11 @@ const ( NatTab = "nat" ) +var ( + Chain1PanelBasicAddressPattern = fmt.Sprintf(`-A\s+%s\s+(?:-s\s+(\S+)|(?:-d\s+(\S+)))?\s+-j\s+(\w+)`, Chain1PanelBasic) + Chian1PanelBasicPortPattern = fmt.Sprintf(`-A\s+%s\s+(?:-s\s+(\S+)\s+)?-p\s+(\w+)(?:\s+-m\s+\w+)*\s+--dport\s+(\d+(?::\d+)?)\s+-j\s+(\w+)`, Chain1PanelBasic) +) + func RunWithStd(tab, rule string) (string, error) { cmdMgr := cmd.NewCommandMgr(cmd.WithIgnoreExist1(), cmd.WithTimeout(20*time.Second)) stdout, err := cmdMgr.RunWithStdoutBashCf("%s iptables -t %s %s", cmd.SudoHandleCmd(), tab, rule) diff --git a/agent/utils/firewall/client/iptables/forward.go b/agent/utils/firewall/client/iptables/forward.go index bfb80ca0a..9925e5e80 100644 --- a/agent/utils/firewall/client/iptables/forward.go +++ b/agent/utils/firewall/client/iptables/forward.go @@ -3,6 +3,8 @@ package iptables import ( "fmt" "strings" + + "github.com/1Panel-dev/1Panel/agent/utils/re" ) func AddForward(protocol, srcPort, dest, destPort, iface string, save bool) error { @@ -69,7 +71,7 @@ func ListForward(chain ...string) ([]IptablesNatInfo, error) { if err != nil { return nil, err } - + natListRegex := re.GetRegex(re.IptablesNatListPattern) var forwardList []IptablesNatInfo for _, line := range strings.Split(stdout, "\n") { line = strings.TrimFunc(line, func(r rune) bool { diff --git a/agent/utils/nginx/components/location.go b/agent/utils/nginx/components/location.go index ab81a543a..a915f8c94 100644 --- a/agent/utils/nginx/components/location.go +++ b/agent/utils/nginx/components/location.go @@ -2,9 +2,10 @@ package components import ( "fmt" - "regexp" "strconv" "strings" + + "github.com/1Panel-dev/1Panel/agent/utils/re" ) type Location struct { @@ -60,8 +61,7 @@ func NewLocation(directive IDirective) *Location { dirs := dir.GetBlock().GetDirectives() for _, di := range dirs { if di.GetName() == "expires" { - re := regexp.MustCompile(`^(\d+)(\w+)$`) - matches := re.FindStringSubmatch(di.GetParameters()[0]) + matches := re.GetRegex(re.NumberWordPattern).FindStringSubmatch(di.GetParameters()[0]) if matches == nil { continue } @@ -80,8 +80,7 @@ func NewLocation(directive IDirective) *Location { } case "proxy_cache_valid": timeParam := params[len(params)-1] - re := regexp.MustCompile(`^(\d+)(\w+)$`) - matches := re.FindStringSubmatch(timeParam) + matches := re.GetRegex(re.NumberWordPattern).FindStringSubmatch(timeParam) if matches == nil { continue } diff --git a/agent/utils/re/re.go b/agent/utils/re/re.go new file mode 100644 index 000000000..be5540b5e --- /dev/null +++ b/agent/utils/re/re.go @@ -0,0 +1,84 @@ +package re + +import ( + "fmt" + "regexp" +) + +const ( + NumberAlphaPattern = `(\d+)([A-Za-z]+)` + ComposeDisallowedCharsPattern = `[^a-z0-9_-]+` + ComposeEnvVarPattern = `\$\{([^}]+)\}` + DiskKeyValuePattern = `([A-Za-z0-9_]+)=("([^"\\]|\\.)*"|[^ \t]+)` + FirewalldForwardPattern = `^port=(\d{1,5}):proto=(.+?):toport=(\d{1,5}):toaddr=(.*)$` + IptablesNatListPattern = `^(\d+)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)(?:\s+(.+?) .+?:(\d{1,5}(?::\d+)?).+?[ :](.+-.+|(?:.+:)?\d{1,5}(?:-\d{1,5})?))?$` + ValidatorNamePattern = `^[a-zA-Z\p{Han}]{1}[a-zA-Z0-9_\p{Han}]{0,30}$` + ValidatorIPPattern = `^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$` + DomainPattern = `^([\w\p{Han}\-\*]{1,100}\.){1,10}([\w\p{Han}\-]{1,24}|[\w\p{Han}\-]{1,24}\.[\w\p{Han}\-]{1,24})(:\d{1,5})?$` + ProxyCacheZonePattern = `keys_zone=proxy_cache_zone_of_[\w.]+:(\d+)([kmgt]?)` + ProxyCacheMaxSizePattern = `max_size=([0-9.]+)([kmgt]?)` + ProxyCacheMaxSizeValidationPattern = `max_size=\d+(\.\d+)?[kmgt]?` + ProxyCacheInactivePattern = `inactive=(\d+)([smhd])` + NumberWordPattern = `^(\d+)(\w+)$` + TrailingDigitsPattern = `_(\d+)$` + AlertIPPattern = `from\s+([0-9.]+)\s+port\s+(\d+)` + CosDualStackPattern = `.*cos-dualstack\..*` + VersionPattern = `v(\d+\.\d+\.\d+)` + PhpAssignmentPattern = `^\s*([a-z_]+)\s*=\s*(.*)$` + DurationWithOptionalUnitPattern = `^(\d+)([smhdw]?)$` + MysqlGroupPattern = `\[*\]` + AnsiEscapePattern = "\x1b\\[[0-9;?]*[A-Za-z]|\x1b=|\x1b>" + RecycleBinFilePattern = `_1p_file_1p_(.+)_p_(\d+)_(\d+)` +) + +var regexMap = make(map[string]*regexp.Regexp) + +// InitRegex compiles all regex patterns and stores them in the map. +// This function should be called once at program startup. +func Init() { + patterns := []string{ + NumberAlphaPattern, + ComposeDisallowedCharsPattern, + ComposeEnvVarPattern, + DiskKeyValuePattern, + FirewalldForwardPattern, + IptablesNatListPattern, + ValidatorNamePattern, + ValidatorIPPattern, + DomainPattern, + ProxyCacheZonePattern, + ProxyCacheMaxSizePattern, + ProxyCacheMaxSizeValidationPattern, + ProxyCacheInactivePattern, + NumberWordPattern, + TrailingDigitsPattern, + AlertIPPattern, + CosDualStackPattern, + VersionPattern, + PhpAssignmentPattern, + DurationWithOptionalUnitPattern, + MysqlGroupPattern, + AnsiEscapePattern, + RecycleBinFilePattern, + } + + for _, pattern := range patterns { + regexMap[pattern] = regexp.MustCompile(pattern) + } +} + +// GetRegex retrieves a compiled regex by its pattern string. +// Panics if the pattern is not found in the map. +func GetRegex(pattern string) *regexp.Regexp { + regex, exists := regexMap[pattern] + if !exists { + panic(fmt.Sprintf("regex pattern not found: %s", pattern)) + } + return regex +} + +// RegisterRegex registers a regex pattern and stores it in the map. +// This function should be called once at program startup. +func RegisterRegex(pattern string) { + regexMap[pattern] = regexp.MustCompile(pattern) +}