perfsprint 18→0:strconv.Itoa/FormatInt/FormatUint/FormatBool 替代 fmt.Sprintf、无动词 fmt.Errorf→errors.New、纯字符串拼接。全部语义等价(已核对 diff)。修正 fixer 遗留的 import 问题(引入 goimports 统一整理)。

Result: {"status":"keep","total_issues":56,"golint_canonicalheader":8,"golint_errname":1,"golint_errorlint":12,"golint_forcetypeassert":3,"golint_gosec":0,"golint_intrange":3,"golint_modernize":5,"golint_nilnil":3,"golint_perfsprint":0,"golint_prealloc":3,"golint_recvcheck":7,"golint_usestdlibvars":3,"golint_wastedassign":7,"golint_total":55,"eslint_problems":1,"eslint_errors":0,"eslint_warnings":1,"tsc_errors":0,"measure_s":47}
This commit is contained in:
ryan
2026-08-16 17:34:44 +08:00
parent b76f707c8b
commit 699e95f12c
54 changed files with 188 additions and 157 deletions
+1
View File
@@ -1,3 +1,4 @@
{"type":"config","name":"前后端代码质量优化(符合最佳实践)","metricName":"total_issues","metricUnit":"","bestDirection":"lower"} {"type":"config","name":"前后端代码质量优化(符合最佳实践)","metricName":"total_issues","metricUnit":"","bestDirection":"lower"}
{"run":1,"commit":"305d609","metric":108,"metrics":{"golint_canonicalheader":8,"golint_errname":1,"golint_errorlint":12,"golint_forcetypeassert":3,"golint_gosec":2,"golint_intrange":3,"golint_modernize":37,"golint_nilnil":3,"golint_perfsprint":18,"golint_prealloc":3,"golint_recvcheck":7,"golint_usestdlibvars":3,"golint_wastedassign":7,"golint_total":107,"eslint_problems":1,"eslint_errors":0,"eslint_warnings":1,"tsc_errors":0,"measure_s":36},"status":"checks_failed","description":"基线:总问题 108(golangci 107 + eslint 1)。checks 失败的唯一原因:repo 自带 golangci gate 有 2 个既有 gosec G115 问题(预期内,首次修复后即绿)。","timestamp":1786871594292,"segment":0,"confidence":null,"asi":{"hypothesis":"baseline","next_action_hint":"修复 internal/apps/edge/observability/linux.go 的 2 个 G115 gosec 问题后 checks.sh 才能通过;之后每次迭代即可正常 keep/discard"}} {"run":1,"commit":"305d609","metric":108,"metrics":{"golint_canonicalheader":8,"golint_errname":1,"golint_errorlint":12,"golint_forcetypeassert":3,"golint_gosec":2,"golint_intrange":3,"golint_modernize":37,"golint_nilnil":3,"golint_perfsprint":18,"golint_prealloc":3,"golint_recvcheck":7,"golint_usestdlibvars":3,"golint_wastedassign":7,"golint_total":107,"eslint_problems":1,"eslint_errors":0,"eslint_warnings":1,"tsc_errors":0,"measure_s":36},"status":"checks_failed","description":"基线:总问题 108(golangci 107 + eslint 1)。checks 失败的唯一原因:repo 自带 golangci gate 有 2 个既有 gosec G115 问题(预期内,首次修复后即绿)。","timestamp":1786871594292,"segment":0,"confidence":null,"asi":{"hypothesis":"baseline","next_action_hint":"修复 internal/apps/edge/observability/linux.go 的 2 个 G115 gosec 问题后 checks.sh 才能通过;之后每次迭代即可正常 keep/discard"}}
{"run":2,"commit":"f1f6bb8","metric":106,"metrics":{"golint_canonicalheader":8,"golint_errname":1,"golint_errorlint":12,"golint_forcetypeassert":3,"golint_gosec":0,"golint_intrange":3,"golint_modernize":37,"golint_nilnil":3,"golint_perfsprint":18,"golint_prealloc":3,"golint_recvcheck":7,"golint_usestdlibvars":3,"golint_wastedassign":7,"golint_total":105,"eslint_problems":1,"eslint_errors":0,"eslint_warnings":1,"tsc_errors":0,"measure_s":38},"status":"keep","description":"修复 internal/apps/edge/observability/linux.go 的 2 个 gosec G115 整数溢出转换:helper 改为接收 int64 b,用 gosec 认可的饱和乘法模式(uint64 域乘积 + 上界比较),去掉原 //nolint:gosec,语义不变(Bsize 恒为正)。repo 自带 gate 首次全绿。","timestamp":1786872064145,"segment":0,"confidence":null,"asi":{"hypothesis":"修复 gosec G115:multiplyUint64ToInt64 改为 accept int64 b 并采用 gosec 认可的饱和乘法模式","insight":"gosec G115 不接受分支上界证明(a > MaxInt64/b),但接受先算 uint64 乘积再 if v > MaxInt64 饱和的模式,无需 nolint","next_action_hint":"下一步批量清理 modernize(37)/perfsprint(18) 等自动可修复类别,用 golangci-lint --fix 后人工核对 diff"}} {"run":2,"commit":"f1f6bb8","metric":106,"metrics":{"golint_canonicalheader":8,"golint_errname":1,"golint_errorlint":12,"golint_forcetypeassert":3,"golint_gosec":0,"golint_intrange":3,"golint_modernize":37,"golint_nilnil":3,"golint_perfsprint":18,"golint_prealloc":3,"golint_recvcheck":7,"golint_usestdlibvars":3,"golint_wastedassign":7,"golint_total":105,"eslint_problems":1,"eslint_errors":0,"eslint_warnings":1,"tsc_errors":0,"measure_s":38},"status":"keep","description":"修复 internal/apps/edge/observability/linux.go 的 2 个 gosec G115 整数溢出转换:helper 改为接收 int64 b,用 gosec 认可的饱和乘法模式(uint64 域乘积 + 上界比较),去掉原 //nolint:gosec,语义不变(Bsize 恒为正)。repo 自带 gate 首次全绿。","timestamp":1786872064145,"segment":0,"confidence":null,"asi":{"hypothesis":"修复 gosec G115:multiplyUint64ToInt64 改为 accept int64 b 并采用 gosec 认可的饱和乘法模式","insight":"gosec G115 不接受分支上界证明(a > MaxInt64/b),但接受先算 uint64 乘积再 if v > MaxInt64 饱和的模式,无需 nolint","next_action_hint":"下一步批量清理 modernize(37)/perfsprint(18) 等自动可修复类别,用 golangci-lint --fix 后人工核对 diff"}}
{"run":3,"commit":"b76f707","metric":74,"metrics":{"golint_canonicalheader":8,"golint_errname":1,"golint_errorlint":12,"golint_forcetypeassert":3,"golint_gosec":0,"golint_intrange":3,"golint_modernize":5,"golint_nilnil":3,"golint_perfsprint":18,"golint_prealloc":3,"golint_recvcheck":7,"golint_usestdlibvars":3,"golint_wastedassign":7,"golint_total":73,"eslint_problems":1,"eslint_errors":0,"eslint_warnings":1,"tsc_errors":0,"measure_s":38},"status":"keep","description":"modernize 37→5(-32):interface{}→any、内置 max/min、slices/maps 辅助、strings.Cut/SplitSeq、strings.Builder(修复 mail.go O(n²) 拼接)。逐 hunk 核对语义等价;omitzero 冲突修复被自动跳过(wire 格式不变);手动清 4 处遗留 sort import + 2 处 QF1012。","timestamp":1786872502383,"segment":0,"confidence":17,"asi":{"hypothesis":"批量应用 modernize 自动修复(interface{}→any、max/min、slices.Sort/Contains、strings.Cut/SplitSeq、maps.Copy、strings.Builder)","insight":"golangci-lint --fix 会把 omitempty→omitzero 的冲突修复跳过(2个文件保留原 tag,wire 格式不变,好);但 fixer 会遗留未使用的 sort import,需手动清 4 处;mail.go 的 Builder 迁移附带暴露 2 个 QF1012,顺手用 fmt.Fprintf 修复。全部修复语义等价,已逐项核对 diff","next_action_hint":"剩余 modernize=5 应为 omitzero 冲突文件;下轮先处理 perfsprint(18):先看 --fix 是否安全再决定"}}
+3 -2
View File
@@ -14,6 +14,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"runtime" "runtime"
"strconv"
"time" "time"
"github.com/Rain-kl/Wavelet/internal/infra/config" "github.com/Rain-kl/Wavelet/internal/infra/config"
@@ -309,7 +310,7 @@ func exportSQLite(c *gin.Context) {
c.Header("Content-Disposition", `attachment; filename="openflare.db"`) c.Header("Content-Disposition", `attachment; filename="openflare.db"`)
c.Header("Content-Type", "application/octet-stream") c.Header("Content-Type", "application/octet-stream")
c.Header("Content-Length", fmt.Sprintf("%d", fi.Size())) c.Header("Content-Length", strconv.FormatInt(fi.Size(), 10))
c.Status(http.StatusOK) c.Status(http.StatusOK)
http.ServeContent(c.Writer, c.Request, "openflare.db", fi.ModTime(), f) http.ServeContent(c.Writer, c.Request, "openflare.db", fi.ModTime(), f)
} }
@@ -328,7 +329,7 @@ func exportPostgres(c *gin.Context) {
args := []string{ args := []string{
"--no-password", "--no-password",
"-h", dbCfg.Host, "-h", dbCfg.Host,
"-p", fmt.Sprintf("%d", dbCfg.Port), "-p", strconv.Itoa(dbCfg.Port),
"-U", dbCfg.Username, "-U", dbCfg.Username,
dbCfg.Database, dbCfg.Database,
} }
+2 -2
View File
@@ -516,7 +516,7 @@ func (m *Manager) CurrentChecksum() (string, error) {
normalizedMain = strings.ReplaceAll(normalizedMain, listen, openrestyrender.ObservabilityListenPlaceholder) normalizedMain = strings.ReplaceAll(normalizedMain, listen, openrestyrender.ObservabilityListenPlaceholder)
} }
if m.OpenrestyObservabilityPort > 0 { if m.OpenrestyObservabilityPort > 0 {
normalizedMain = strings.ReplaceAll(normalizedMain, fmt.Sprintf("%d", m.OpenrestyObservabilityPort), openrestyrender.ObservabilityPortPlaceholder) normalizedMain = strings.ReplaceAll(normalizedMain, strconv.Itoa(m.OpenrestyObservabilityPort), openrestyrender.ObservabilityPortPlaceholder)
} }
if resolverDirective := strings.TrimSpace(m.OpenrestyResolverDirective); resolverDirective != "" { if resolverDirective := strings.TrimSpace(m.OpenrestyResolverDirective); resolverDirective != "" {
normalizedMain = strings.ReplaceAll(normalizedMain, resolverDirective, ResolverDirectivePlaceholder) normalizedMain = strings.ReplaceAll(normalizedMain, resolverDirective, ResolverDirectivePlaceholder)
@@ -1463,7 +1463,7 @@ func (m *Manager) renderMainConfig(content string) string {
rendered = strings.ReplaceAll(rendered, openrestyrender.ObservabilityListenPlaceholder, listen) rendered = strings.ReplaceAll(rendered, openrestyrender.ObservabilityListenPlaceholder, listen)
} }
if m.OpenrestyObservabilityPort > 0 { if m.OpenrestyObservabilityPort > 0 {
rendered = strings.ReplaceAll(rendered, openrestyrender.ObservabilityPortPlaceholder, fmt.Sprintf("%d", m.OpenrestyObservabilityPort)) rendered = strings.ReplaceAll(rendered, openrestyrender.ObservabilityPortPlaceholder, strconv.Itoa(m.OpenrestyObservabilityPort))
} }
if resolverDirective := strings.TrimSpace(m.OpenrestyResolverDirective); resolverDirective != "" { if resolverDirective := strings.TrimSpace(m.OpenrestyResolverDirective); resolverDirective != "" {
rendered = strings.ReplaceAll(rendered, ResolverDirectivePlaceholder, resolverDirective) rendered = strings.ReplaceAll(rendered, ResolverDirectivePlaceholder, resolverDirective)
+5 -4
View File
@@ -15,6 +15,7 @@ import (
"log/slog" "log/slog"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"github.com/Rain-kl/Wavelet/internal/apps/agent/protocol" "github.com/Rain-kl/Wavelet/internal/apps/agent/protocol"
@@ -392,7 +393,7 @@ func (s *Service) downloadPagesProjectPackage(
metadata *protocol.PagesProjectLatestHashResponse, metadata *protocol.PagesProjectLatestHashResponse,
maxBytes int64, maxBytes int64,
) (packagePath string, hash string, err error) { ) (packagePath string, hash string, err error) {
releasesRoot := filepath.Join(s.pagesDir, "projects", fmt.Sprintf("%d", projectID), "releases") releasesRoot := filepath.Join(s.pagesDir, "projects", strconv.FormatUint(uint64(projectID), 10), "releases")
if err := os.MkdirAll(releasesRoot, pagesDirPerm); err != nil { if err := os.MkdirAll(releasesRoot, pagesDirPerm); err != nil {
return "", "", err return "", "", err
} }
@@ -441,7 +442,7 @@ func cleanupPagesProjectStaleReleases(baseDir string, projectID uint, keepHash s
if projectID == 0 || keepHash == "" { if projectID == 0 || keepHash == "" {
return nil return nil
} }
releasesRoot := filepath.Join(baseDir, "projects", fmt.Sprintf("%d", projectID), "releases") releasesRoot := filepath.Join(baseDir, "projects", strconv.FormatUint(uint64(projectID), 10), "releases")
entries, err := os.ReadDir(releasesRoot) //nolint:gosec // managed PagesDir entries, err := os.ReadDir(releasesRoot) //nolint:gosec // managed PagesDir
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
@@ -982,9 +983,9 @@ func writePagesMarker(dir string, project pagesProjectRef) error {
} }
func pagesProjectCurrentDir(baseDir string, projectID uint) string { func pagesProjectCurrentDir(baseDir string, projectID uint) string {
return filepath.Join(baseDir, "projects", fmt.Sprintf("%d", projectID), "current") return filepath.Join(baseDir, "projects", strconv.FormatUint(uint64(projectID), 10), "current")
} }
func pagesProjectReleaseDir(baseDir string, projectID uint, checksum string) string { func pagesProjectReleaseDir(baseDir string, projectID uint, checksum string) string {
return filepath.Join(baseDir, "projects", fmt.Sprintf("%d", projectID), "releases", checksum) return filepath.Join(baseDir, "projects", strconv.FormatUint(uint64(projectID), 10), "releases", checksum)
} }
+2 -1
View File
@@ -4,6 +4,7 @@
package updater package updater
import ( import (
"errors"
"fmt" "fmt"
"log/slog" "log/slog"
"os" "os"
@@ -37,7 +38,7 @@ func replaceAndRestart(execPath string, tmpPath string) error {
if err := syscall.Exec(execPath, os.Args, os.Environ()); err != nil { //nolint:gosec // execPath is the validated edge updater binary path if err := syscall.Exec(execPath, os.Args, os.Environ()); err != nil { //nolint:gosec // execPath is the validated edge updater binary path
return fmt.Errorf("exec restart: %w", err) return fmt.Errorf("exec restart: %w", err)
} }
return fmt.Errorf("unreachable after exec") return errors.New("unreachable after exec")
} }
func removeBackupBinary(path string) error { func removeBackupBinary(path string) error {
+3 -2
View File
@@ -6,6 +6,7 @@ import (
"crypto/sha256" "crypto/sha256"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"log/slog" "log/slog"
@@ -295,7 +296,7 @@ func parseSHA256Checksum(content string, assetName string) (string, error) {
} }
} }
if assetName == "" { if assetName == "" {
return "", fmt.Errorf("checksum asset does not contain a valid sha256 digest") return "", errors.New("checksum asset does not contain a valid sha256 digest")
} }
return "", fmt.Errorf("checksum asset does not contain a sha256 digest for %q", assetName) return "", fmt.Errorf("checksum asset does not contain a sha256 digest for %q", assetName)
} }
@@ -340,7 +341,7 @@ func isSHA256Hex(value string) bool {
func (s *Service) downloadAndRestart(ctx context.Context, url string, expectedChecksum string, targetPath string) error { func (s *Service) downloadAndRestart(ctx context.Context, url string, expectedChecksum string, targetPath string) error {
expectedChecksum = strings.ToLower(strings.TrimSpace(expectedChecksum)) expectedChecksum = strings.ToLower(strings.TrimSpace(expectedChecksum))
if !isSHA256Hex(expectedChecksum) { if !isSHA256Hex(expectedChecksum) {
return fmt.Errorf("invalid expected sha256 checksum") return errors.New("invalid expected sha256 checksum")
} }
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil { if err != nil {
+3 -2
View File
@@ -5,6 +5,7 @@ package oauth
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"strconv" "strconv"
"sync" "sync"
@@ -140,7 +141,7 @@ func GetCachedToken(ctx context.Context, tokenHash string) (*model.AccessToken,
return &token, nil return &token, nil
} }
} }
return nil, fmt.Errorf("cache miss") return nil, errors.New("cache miss")
} }
// SetCachedToken 设置 AccessToken 缓存 // SetCachedToken 设置 AccessToken 缓存
@@ -183,7 +184,7 @@ func GetCachedUser(ctx context.Context, userID uint64) (*model.User, error) {
return &u, nil return &u, nil
} }
} }
return nil, fmt.Errorf("cache miss") return nil, errors.New("cache miss")
} }
// SetCachedUser 设置 User 缓存 // SetCachedUser 设置 User 缓存
@@ -46,7 +46,7 @@ func ChangedWAFIPGroupsForAgent(ctx context.Context, ids []uint, checksums map[s
} }
changed := make([]WAFIPGroup, 0, len(groups)) changed := make([]WAFIPGroup, 0, len(groups))
for _, group := range groups { for _, group := range groups {
if strings.TrimSpace(checksums[fmt.Sprintf("%d", group.ID)]) == group.Checksum { if strings.TrimSpace(checksums[strconv.FormatUint(uint64(group.ID), 10)]) == group.Checksum {
continue continue
} }
changed = append(changed, group) changed = append(changed, group)
@@ -6,6 +6,7 @@ package config_version
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"net" "net"
"strconv" "strconv"
@@ -27,7 +28,7 @@ func normalizeSnapshotDomains(domains []string) ([]string, error) {
for _, raw := range domains { for _, raw := range domains {
domain := strings.ToLower(strings.TrimSpace(raw)) domain := strings.ToLower(strings.TrimSpace(raw))
if domain == "" || strings.Contains(domain, "://") || strings.Contains(domain, "/") { if domain == "" || strings.Contains(domain, "://") || strings.Contains(domain, "/") {
return nil, fmt.Errorf("domains payload is invalid") return nil, errors.New("domains payload is invalid")
} }
if _, ok := seen[domain]; ok { if _, ok := seen[domain]; ok {
continue continue
@@ -36,7 +37,7 @@ func normalizeSnapshotDomains(domains []string) ([]string, error) {
normalized = append(normalized, domain) normalized = append(normalized, domain)
} }
if len(normalized) == 0 { if len(normalized) == 0 {
return nil, fmt.Errorf("domain is required") return nil, errors.New("domain is required")
} }
return normalized, nil return normalized, nil
} }
@@ -55,7 +56,7 @@ func decodeStoredUpstreams(raw string, fallbackOriginURL string) ([]string, erro
} }
var upstreams []string var upstreams []string
if err := json.Unmarshal([]byte(text), &upstreams); err != nil { if err := json.Unmarshal([]byte(text), &upstreams); err != nil {
return nil, fmt.Errorf("upstreams payload is invalid") return nil, errors.New("upstreams payload is invalid")
} }
return normalizeUpstreams(fallbackOriginURL, upstreams) return normalizeUpstreams(fallbackOriginURL, upstreams)
} }
@@ -79,7 +80,7 @@ func normalizeUpstreams(originURL string, upstreams []string) ([]string, error)
normalized = append(normalized, value) normalized = append(normalized, value)
} }
if len(normalized) == 0 { if len(normalized) == 0 {
return nil, fmt.Errorf("upstream is required") return nil, errors.New("upstream is required")
} }
return normalized, nil return normalized, nil
} }
@@ -91,7 +92,7 @@ func decodeStoredCustomHeaders(raw string) ([]customHeaderInput, error) {
} }
var headers []customHeaderInput var headers []customHeaderInput
if err := json.Unmarshal([]byte(text), &headers); err != nil { if err := json.Unmarshal([]byte(text), &headers); err != nil {
return nil, fmt.Errorf("custom_headers payload is invalid") return nil, errors.New("custom_headers payload is invalid")
} }
return headers, nil return headers, nil
} }
@@ -103,7 +104,7 @@ func decodeStoredCacheRules(raw string) ([]string, error) {
} }
var rules []string var rules []string
if err := json.Unmarshal([]byte(text), &rules); err != nil { if err := json.Unmarshal([]byte(text), &rules); err != nil {
return nil, fmt.Errorf("cache_rules payload is invalid") return nil, errors.New("cache_rules payload is invalid")
} }
normalized := make([]string, 0, len(rules)) normalized := make([]string, 0, len(rules))
for _, rule := range rules { for _, rule := range rules {
@@ -494,51 +494,51 @@ func diffOpenRestyOptionDetails(left openRestyConfigSnapshot, right openRestyCon
CurrentValue: current, CurrentValue: current,
}) })
} }
appendIfChanged("OpenRestyDefaultServerReturnStatus", fmt.Sprintf("%d", left.DefaultServerReturnStatus), fmt.Sprintf("%d", right.DefaultServerReturnStatus)) appendIfChanged("OpenRestyDefaultServerReturnStatus", strconv.Itoa(left.DefaultServerReturnStatus), strconv.Itoa(right.DefaultServerReturnStatus))
appendIfChanged("OpenRestyWorkerProcesses", left.WorkerProcesses, right.WorkerProcesses) appendIfChanged("OpenRestyWorkerProcesses", left.WorkerProcesses, right.WorkerProcesses)
appendIfChanged("OpenRestyWorkerConnections", fmt.Sprintf("%d", left.WorkerConnections), fmt.Sprintf("%d", right.WorkerConnections)) appendIfChanged("OpenRestyWorkerConnections", strconv.Itoa(left.WorkerConnections), strconv.Itoa(right.WorkerConnections))
appendIfChanged("OpenRestyWorkerRlimitNofile", fmt.Sprintf("%d", left.WorkerRlimitNofile), fmt.Sprintf("%d", right.WorkerRlimitNofile)) appendIfChanged("OpenRestyWorkerRlimitNofile", strconv.Itoa(left.WorkerRlimitNofile), strconv.Itoa(right.WorkerRlimitNofile))
appendIfChanged("OpenRestyEventsUse", left.EventsUse, right.EventsUse) appendIfChanged("OpenRestyEventsUse", left.EventsUse, right.EventsUse)
appendIfChanged("OpenRestyEventsMultiAcceptEnabled", fmt.Sprintf("%t", left.EventsMultiAcceptEnabled), fmt.Sprintf("%t", right.EventsMultiAcceptEnabled)) appendIfChanged("OpenRestyEventsMultiAcceptEnabled", strconv.FormatBool(left.EventsMultiAcceptEnabled), strconv.FormatBool(right.EventsMultiAcceptEnabled))
appendIfChanged("OpenRestyKeepaliveTimeout", fmt.Sprintf("%d", left.KeepaliveTimeout), fmt.Sprintf("%d", right.KeepaliveTimeout)) appendIfChanged("OpenRestyKeepaliveTimeout", strconv.Itoa(left.KeepaliveTimeout), strconv.Itoa(right.KeepaliveTimeout))
appendIfChanged("OpenRestyKeepaliveRequests", fmt.Sprintf("%d", left.KeepaliveRequests), fmt.Sprintf("%d", right.KeepaliveRequests)) appendIfChanged("OpenRestyKeepaliveRequests", strconv.Itoa(left.KeepaliveRequests), strconv.Itoa(right.KeepaliveRequests))
appendIfChanged("OpenRestyClientHeaderTimeout", fmt.Sprintf("%d", left.ClientHeaderTimeout), fmt.Sprintf("%d", right.ClientHeaderTimeout)) appendIfChanged("OpenRestyClientHeaderTimeout", strconv.Itoa(left.ClientHeaderTimeout), strconv.Itoa(right.ClientHeaderTimeout))
appendIfChanged("OpenRestyClientBodyTimeout", fmt.Sprintf("%d", left.ClientBodyTimeout), fmt.Sprintf("%d", right.ClientBodyTimeout)) appendIfChanged("OpenRestyClientBodyTimeout", strconv.Itoa(left.ClientBodyTimeout), strconv.Itoa(right.ClientBodyTimeout))
appendIfChanged("OpenRestyClientMaxBodySize", left.ClientMaxBodySize, right.ClientMaxBodySize) appendIfChanged("OpenRestyClientMaxBodySize", left.ClientMaxBodySize, right.ClientMaxBodySize)
appendIfChanged("OpenRestyLargeClientHeaderBuffers", left.LargeClientHeaderBuffers, right.LargeClientHeaderBuffers) appendIfChanged("OpenRestyLargeClientHeaderBuffers", left.LargeClientHeaderBuffers, right.LargeClientHeaderBuffers)
appendIfChanged("OpenRestySendTimeout", fmt.Sprintf("%d", left.SendTimeout), fmt.Sprintf("%d", right.SendTimeout)) appendIfChanged("OpenRestySendTimeout", strconv.Itoa(left.SendTimeout), strconv.Itoa(right.SendTimeout))
appendIfChanged("OpenRestyProxyConnectTimeout", fmt.Sprintf("%d", left.ProxyConnectTimeout), fmt.Sprintf("%d", right.ProxyConnectTimeout)) appendIfChanged("OpenRestyProxyConnectTimeout", strconv.Itoa(left.ProxyConnectTimeout), strconv.Itoa(right.ProxyConnectTimeout))
appendIfChanged("OpenRestyProxySendTimeout", fmt.Sprintf("%d", left.ProxySendTimeout), fmt.Sprintf("%d", right.ProxySendTimeout)) appendIfChanged("OpenRestyProxySendTimeout", strconv.Itoa(left.ProxySendTimeout), strconv.Itoa(right.ProxySendTimeout))
appendIfChanged("OpenRestyProxyReadTimeout", fmt.Sprintf("%d", left.ProxyReadTimeout), fmt.Sprintf("%d", right.ProxyReadTimeout)) appendIfChanged("OpenRestyProxyReadTimeout", strconv.Itoa(left.ProxyReadTimeout), strconv.Itoa(right.ProxyReadTimeout))
appendIfChanged("OpenRestyWebsocketEnabled", fmt.Sprintf("%t", left.WebsocketEnabled), fmt.Sprintf("%t", right.WebsocketEnabled)) appendIfChanged("OpenRestyWebsocketEnabled", strconv.FormatBool(left.WebsocketEnabled), strconv.FormatBool(right.WebsocketEnabled))
appendIfChanged("OpenRestyHTTP3Enabled", fmt.Sprintf("%t", left.HTTP3Enabled), fmt.Sprintf("%t", right.HTTP3Enabled)) appendIfChanged("OpenRestyHTTP3Enabled", strconv.FormatBool(left.HTTP3Enabled), strconv.FormatBool(right.HTTP3Enabled))
appendIfChanged("OpenRestyProxyRequestBufferingEnabled", fmt.Sprintf("%t", left.ProxyRequestBuffering), fmt.Sprintf("%t", right.ProxyRequestBuffering)) appendIfChanged("OpenRestyProxyRequestBufferingEnabled", strconv.FormatBool(left.ProxyRequestBuffering), strconv.FormatBool(right.ProxyRequestBuffering))
appendIfChanged("OpenRestyProxyBufferingEnabled", fmt.Sprintf("%t", left.ProxyBufferingEnabled), fmt.Sprintf("%t", right.ProxyBufferingEnabled)) appendIfChanged("OpenRestyProxyBufferingEnabled", strconv.FormatBool(left.ProxyBufferingEnabled), strconv.FormatBool(right.ProxyBufferingEnabled))
appendIfChanged("OpenRestyProxyBuffers", left.ProxyBuffers, right.ProxyBuffers) appendIfChanged("OpenRestyProxyBuffers", left.ProxyBuffers, right.ProxyBuffers)
appendIfChanged("OpenRestyProxyBufferSize", left.ProxyBufferSize, right.ProxyBufferSize) appendIfChanged("OpenRestyProxyBufferSize", left.ProxyBufferSize, right.ProxyBufferSize)
appendIfChanged("OpenRestyProxyBusyBuffersSize", left.ProxyBusyBuffersSize, right.ProxyBusyBuffersSize) appendIfChanged("OpenRestyProxyBusyBuffersSize", left.ProxyBusyBuffersSize, right.ProxyBusyBuffersSize)
appendIfChanged("OpenRestyGzipEnabled", fmt.Sprintf("%t", left.GzipEnabled), fmt.Sprintf("%t", right.GzipEnabled)) appendIfChanged("OpenRestyGzipEnabled", strconv.FormatBool(left.GzipEnabled), strconv.FormatBool(right.GzipEnabled))
appendIfChanged("OpenRestyGzipMinLength", fmt.Sprintf("%d", left.GzipMinLength), fmt.Sprintf("%d", right.GzipMinLength)) appendIfChanged("OpenRestyGzipMinLength", strconv.Itoa(left.GzipMinLength), strconv.Itoa(right.GzipMinLength))
appendIfChanged("OpenRestyGzipCompLevel", fmt.Sprintf("%d", left.GzipCompLevel), fmt.Sprintf("%d", right.GzipCompLevel)) appendIfChanged("OpenRestyGzipCompLevel", strconv.Itoa(left.GzipCompLevel), strconv.Itoa(right.GzipCompLevel))
appendIfChanged("OpenRestyResolvers", left.Resolvers, right.Resolvers) appendIfChanged("OpenRestyResolvers", left.Resolvers, right.Resolvers)
appendIfChanged("OpenRestyCacheEnabled", fmt.Sprintf("%t", left.CacheEnabled), fmt.Sprintf("%t", right.CacheEnabled)) appendIfChanged("OpenRestyCacheEnabled", strconv.FormatBool(left.CacheEnabled), strconv.FormatBool(right.CacheEnabled))
appendIfChanged("OpenRestyCachePath", left.CachePath, right.CachePath) appendIfChanged("OpenRestyCachePath", left.CachePath, right.CachePath)
appendIfChanged("OpenRestyCacheLevels", left.CacheLevels, right.CacheLevels) appendIfChanged("OpenRestyCacheLevels", left.CacheLevels, right.CacheLevels)
appendIfChanged("OpenRestyCacheInactive", left.CacheInactive, right.CacheInactive) appendIfChanged("OpenRestyCacheInactive", left.CacheInactive, right.CacheInactive)
appendIfChanged("OpenRestyCacheMaxSize", left.CacheMaxSize, right.CacheMaxSize) appendIfChanged("OpenRestyCacheMaxSize", left.CacheMaxSize, right.CacheMaxSize)
appendIfChanged("OpenRestyCacheKeyTemplate", left.CacheKeyTemplate, right.CacheKeyTemplate) appendIfChanged("OpenRestyCacheKeyTemplate", left.CacheKeyTemplate, right.CacheKeyTemplate)
appendIfChanged("OpenRestyCacheLockEnabled", fmt.Sprintf("%t", left.CacheLockEnabled), fmt.Sprintf("%t", right.CacheLockEnabled)) appendIfChanged("OpenRestyCacheLockEnabled", strconv.FormatBool(left.CacheLockEnabled), strconv.FormatBool(right.CacheLockEnabled))
appendIfChanged("OpenRestyCacheLockTimeout", left.CacheLockTimeout, right.CacheLockTimeout) appendIfChanged("OpenRestyCacheLockTimeout", left.CacheLockTimeout, right.CacheLockTimeout)
appendIfChanged("OpenRestyCacheUseStale", left.CacheUseStale, right.CacheUseStale) appendIfChanged("OpenRestyCacheUseStale", left.CacheUseStale, right.CacheUseStale)
appendIfChanged("OpenRestyDefaultLimitConnPerServer", fmt.Sprintf("%d", left.DefaultLimitConnPerServer), fmt.Sprintf("%d", right.DefaultLimitConnPerServer)) appendIfChanged("OpenRestyDefaultLimitConnPerServer", strconv.Itoa(left.DefaultLimitConnPerServer), strconv.Itoa(right.DefaultLimitConnPerServer))
appendIfChanged("OpenRestyDefaultLimitConnPerIP", fmt.Sprintf("%d", left.DefaultLimitConnPerIP), fmt.Sprintf("%d", right.DefaultLimitConnPerIP)) appendIfChanged("OpenRestyDefaultLimitConnPerIP", strconv.Itoa(left.DefaultLimitConnPerIP), strconv.Itoa(right.DefaultLimitConnPerIP))
appendIfChanged("OpenRestyDefaultLimitRate", left.DefaultLimitRate, right.DefaultLimitRate) appendIfChanged("OpenRestyDefaultLimitRate", left.DefaultLimitRate, right.DefaultLimitRate)
appendIfChanged("OpenRestyDefaultLimitReqPerIP", left.DefaultLimitReqPerIP, right.DefaultLimitReqPerIP) appendIfChanged("OpenRestyDefaultLimitReqPerIP", left.DefaultLimitReqPerIP, right.DefaultLimitReqPerIP)
appendIfChanged("OriginErrorPageEnabled", fmt.Sprintf("%t", left.OriginErrorPageEnabled), fmt.Sprintf("%t", right.OriginErrorPageEnabled)) appendIfChanged("OriginErrorPageEnabled", strconv.FormatBool(left.OriginErrorPageEnabled), strconv.FormatBool(right.OriginErrorPageEnabled))
appendIfChanged("OriginErrorPageStatusCodes", encodeOriginErrorPageStatusCodes(left.OriginErrorPageStatusCodes), encodeOriginErrorPageStatusCodes(right.OriginErrorPageStatusCodes)) appendIfChanged("OriginErrorPageStatusCodes", encodeOriginErrorPageStatusCodes(left.OriginErrorPageStatusCodes), encodeOriginErrorPageStatusCodes(right.OriginErrorPageStatusCodes))
appendIfChanged("OriginErrorPageHTML", left.OriginErrorPageHTML, right.OriginErrorPageHTML) appendIfChanged("OriginErrorPageHTML", left.OriginErrorPageHTML, right.OriginErrorPageHTML)
appendIfChanged("OriginErrorPageGetOnly", fmt.Sprintf("%t", left.OriginErrorPageGetOnly), fmt.Sprintf("%t", right.OriginErrorPageGetOnly)) appendIfChanged("OriginErrorPageGetOnly", strconv.FormatBool(left.OriginErrorPageGetOnly), strconv.FormatBool(right.OriginErrorPageGetOnly))
appendIfChanged("SWOfflineEnabled", fmt.Sprintf("%t", left.SWOfflineEnabled), fmt.Sprintf("%t", right.SWOfflineEnabled)) appendIfChanged("SWOfflineEnabled", strconv.FormatBool(left.SWOfflineEnabled), strconv.FormatBool(right.SWOfflineEnabled))
appendIfChanged("SWOfflineHTML", left.SWOfflineHTML, right.SWOfflineHTML) appendIfChanged("SWOfflineHTML", left.SWOfflineHTML, right.SWOfflineHTML)
appendIfChanged("SWOfflineDomains", encodeSWOfflineDomains(left.SWOfflineDomains), encodeSWOfflineDomains(right.SWOfflineDomains)) appendIfChanged("SWOfflineDomains", encodeSWOfflineDomains(left.SWOfflineDomains), encodeSWOfflineDomains(right.SWOfflineDomains))
return changes return changes
@@ -478,7 +478,7 @@ func decodeIPList(raw string) ([]string, error) {
} }
var items []string var items []string
if err := json.Unmarshal([]byte(text), &items); err != nil { if err := json.Unmarshal([]byte(text), &items); err != nil {
return nil, fmt.Errorf("ip_list payload is invalid") return nil, errors.New("ip_list payload is invalid")
} }
return items, nil return items, nil
} }
+2 -2
View File
@@ -28,7 +28,7 @@ const (
// Heartbeat processes an OpenFlared heartbeat and returns runtime settings. // Heartbeat processes an OpenFlared heartbeat and returns runtime settings.
func Heartbeat(ctx context.Context, node *model.OpenFlareNode, payload HeartbeatPayload) (*HeartbeatResponse, error) { func Heartbeat(ctx context.Context, node *model.OpenFlareNode, payload HeartbeatPayload) (*HeartbeatResponse, error) {
if node == nil { if node == nil {
return nil, fmt.Errorf("tunnel client node is nil") return nil, errors.New("tunnel client node is nil")
} }
if node.NodeType != "tunnel_client" { if node.NodeType != "tunnel_client" {
return nil, fmt.Errorf("node %s is not a tunnel_client", node.NodeID) return nil, fmt.Errorf("node %s is not a tunnel_client", node.NodeID)
@@ -95,7 +95,7 @@ func Heartbeat(ctx context.Context, node *model.OpenFlareNode, payload Heartbeat
// GetTunnelConfig builds the full tunnel routing config for an OpenFlared client. // GetTunnelConfig builds the full tunnel routing config for an OpenFlared client.
func GetTunnelConfig(ctx context.Context, node *model.OpenFlareNode) (*TunnelConfigResponse, error) { func GetTunnelConfig(ctx context.Context, node *model.OpenFlareNode) (*TunnelConfigResponse, error) {
if node == nil { if node == nil {
return nil, fmt.Errorf("node is nil") return nil, errors.New("node is nil")
} }
activeVersion, err := getActiveConfigMeta(ctx) activeVersion, err := getActiveConfigMeta(ctx)
+8 -7
View File
@@ -8,6 +8,7 @@ import (
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"net" "net"
@@ -317,7 +318,7 @@ func fetchLatestStableGitHubRelease(ctx context.Context, repo string) (*githubRe
url := fmt.Sprintf(githubReleasesAPIBase+"/latest", strings.TrimSpace(repo)) url := fmt.Sprintf(githubReleasesAPIBase+"/latest", strings.TrimSpace(repo))
req, err := newGitHubReleaseRequest(ctx, url) req, err := newGitHubReleaseRequest(ctx, url)
if err != nil { if err != nil {
return nil, fmt.Errorf("创建更新请求失败") return nil, errors.New("创建更新请求失败")
} }
resp, err := releaseHTTPClient.Do(req) resp, err := releaseHTTPClient.Do(req)
if err != nil { if err != nil {
@@ -334,7 +335,7 @@ func fetchLatestPreviewGitHubRelease(ctx context.Context, repo string) (*githubR
url := fmt.Sprintf(githubReleasesAPIBase+"?per_page=20", strings.TrimSpace(repo)) url := fmt.Sprintf(githubReleasesAPIBase+"?per_page=20", strings.TrimSpace(repo))
req, err := newGitHubReleaseRequest(ctx, url) req, err := newGitHubReleaseRequest(ctx, url)
if err != nil { if err != nil {
return nil, fmt.Errorf("创建更新请求失败") return nil, errors.New("创建更新请求失败")
} }
resp, err := releaseHTTPClient.Do(req) resp, err := releaseHTTPClient.Do(req)
if err != nil { if err != nil {
@@ -346,7 +347,7 @@ func fetchLatestPreviewGitHubRelease(ctx context.Context, repo string) (*githubR
} }
var releases []githubReleaseResponse var releases []githubReleaseResponse
if err = json.NewDecoder(resp.Body).Decode(&releases); err != nil { if err = json.NewDecoder(resp.Body).Decode(&releases); err != nil {
return nil, fmt.Errorf("解析 preview 版本信息失败") return nil, errors.New("解析 preview 版本信息失败")
} }
for _, release := range releases { for _, release := range releases {
if release.Draft || !release.Prerelease { if release.Draft || !release.Prerelease {
@@ -355,18 +356,18 @@ func fetchLatestPreviewGitHubRelease(ctx context.Context, repo string) (*githubR
releaseCopy := release releaseCopy := release
return &releaseCopy, nil return &releaseCopy, nil
} }
return nil, fmt.Errorf("当前没有可用的 preview 发布") return nil, errors.New("当前没有可用的 preview 发布")
} }
func fetchGitHubReleaseByTag(ctx context.Context, repo string, tag string) (*githubReleaseResponse, error) { func fetchGitHubReleaseByTag(ctx context.Context, repo string, tag string) (*githubReleaseResponse, error) {
tag = strings.TrimSpace(tag) tag = strings.TrimSpace(tag)
if tag == "" { if tag == "" {
return nil, fmt.Errorf("缺少发布版本号") return nil, errors.New("缺少发布版本号")
} }
url := fmt.Sprintf(githubReleasesAPIBase+"/tags/%s", strings.TrimSpace(repo), tag) url := fmt.Sprintf(githubReleasesAPIBase+"/tags/%s", strings.TrimSpace(repo), tag)
req, err := newGitHubReleaseRequest(ctx, url) req, err := newGitHubReleaseRequest(ctx, url)
if err != nil { if err != nil {
return nil, fmt.Errorf("创建更新请求失败") return nil, errors.New("创建更新请求失败")
} }
resp, err := releaseHTTPClient.Do(req) resp, err := releaseHTTPClient.Do(req)
if err != nil { if err != nil {
@@ -395,7 +396,7 @@ func newGitHubReleaseRequest(ctx context.Context, url string) (*http.Request, er
func decodeGitHubRelease(reader io.Reader) (*githubReleaseResponse, error) { func decodeGitHubRelease(reader io.Reader) (*githubReleaseResponse, error) {
var release githubReleaseResponse var release githubReleaseResponse
if err := json.NewDecoder(reader).Decode(&release); err != nil { if err := json.NewDecoder(reader).Decode(&release); err != nil {
return nil, fmt.Errorf("解析版本信息失败") return nil, errors.New("解析版本信息失败")
} }
return &release, nil return &release, nil
} }
+2 -2
View File
@@ -414,14 +414,14 @@ type forceSyncConfigPayload struct {
func ValidateDiscoveryToken(ctx context.Context, token string) error { func ValidateDiscoveryToken(ctx context.Context, token string) error {
token = strings.TrimSpace(token) token = strings.TrimSpace(token)
if token == "" { if token == "" {
return fmt.Errorf("缺少 Discovery Token") return errors.New("缺少 Discovery Token")
} }
discoveryToken, err := ensureGlobalDiscoveryToken(ctx) discoveryToken, err := ensureGlobalDiscoveryToken(ctx)
if err != nil { if err != nil {
return err return err
} }
if token != discoveryToken { if token != discoveryToken {
return fmt.Errorf("discovery Token 无效") // error 消息首字母小写 return errors.New("discovery Token 无效") // error 消息首字母小写
} }
return nil return nil
} }
+6 -6
View File
@@ -176,16 +176,16 @@ func validateUptimeKumaEnabled(ctx context.Context, key, trimmed string, state m
username := strings.TrimSpace(state[model.ConfigKeyUptimeKumaUsername]) username := strings.TrimSpace(state[model.ConfigKeyUptimeKumaUsername])
password := strings.TrimSpace(state[model.ConfigKeyUptimeKumaPassword]) password := strings.TrimSpace(state[model.ConfigKeyUptimeKumaPassword])
if url == "" { if url == "" {
return fmt.Errorf("启用 Uptime Kuma 时地址不能为空") return errors.New("启用 Uptime Kuma 时地址不能为空")
} }
if username == "" { if username == "" {
return fmt.Errorf("启用 Uptime Kuma 时用户名不能为空") return errors.New("启用 Uptime Kuma 时用户名不能为空")
} }
// 如果待验证的密码为空,且当前配置中也没有密码,则报错 // 如果待验证的密码为空,且当前配置中也没有密码,则报错
if password == "" { if password == "" {
existingPwd, _ := repository.GetSystemConfigByKey(ctx, model.ConfigKeyUptimeKumaPassword) existingPwd, _ := repository.GetSystemConfigByKey(ctx, model.ConfigKeyUptimeKumaPassword)
if strings.TrimSpace(existingPwd.Value) == "" { if strings.TrimSpace(existingPwd.Value) == "" {
return fmt.Errorf("启用 Uptime Kuma 时密码不能为空") return errors.New("启用 Uptime Kuma 时密码不能为空")
} }
} }
return nil return nil
@@ -193,21 +193,21 @@ func validateUptimeKumaEnabled(ctx context.Context, key, trimmed string, state m
func validateUptimeKumaUsername(trimmed string, state map[string]string) error { func validateUptimeKumaUsername(trimmed string, state map[string]string) error {
if trimmed == "" && state[model.ConfigKeyUptimeKumaEnabled] == optionValueTrue { if trimmed == "" && state[model.ConfigKeyUptimeKumaEnabled] == optionValueTrue {
return fmt.Errorf("启用 Uptime Kuma 时用户名不能为空") return errors.New("启用 Uptime Kuma 时用户名不能为空")
} }
return nil return nil
} }
func validateUptimeKumaURL(trimmed string) error { func validateUptimeKumaURL(trimmed string) error {
if trimmed != "" && !strings.HasPrefix(trimmed, "http://") && !strings.HasPrefix(trimmed, "https://") { if trimmed != "" && !strings.HasPrefix(trimmed, "http://") && !strings.HasPrefix(trimmed, "https://") {
return fmt.Errorf("uptime Kuma 地址必须以 http:// 或 https:// 开头") return errors.New("uptime Kuma 地址必须以 http:// 或 https:// 开头")
} }
return nil return nil
} }
func validateUptimeKumaMonitorScope(trimmed string) error { func validateUptimeKumaMonitorScope(trimmed string) error {
if trimmed != "all" && trimmed != "selected" { if trimmed != "all" && trimmed != "selected" {
return fmt.Errorf("监控范围必须为全部站点 (all) 或选择站点 (selected)") return errors.New("监控范围必须为全部站点 (all) 或选择站点 (selected)")
} }
return nil return nil
} }
+2 -1
View File
@@ -6,6 +6,7 @@ package pages
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"path" "path"
"strings" "strings"
@@ -151,7 +152,7 @@ func resolveProjectIDFromRouteMap(route map[string]json.RawMessage) (uint, error
return deployment.ProjectID, nil return deployment.ProjectID, nil
} }
} }
return 0, fmt.Errorf("pages 配置无效: 缺少 pages_project_id") return 0, errors.New("pages 配置无效: 缺少 pages_project_id")
} }
func loadActivePagesProject(ctx context.Context, projectID uint, siteName string) (*model.PagesProject, *model.PagesDeployment, error) { func loadActivePagesProject(ctx context.Context, projectID uint, siteName string) (*model.PagesProject, *model.PagesDeployment, error) {
+2 -1
View File
@@ -5,6 +5,7 @@ package relay
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"strings" "strings"
"time" "time"
@@ -20,7 +21,7 @@ const nodeStatusOnline = "online"
// Heartbeat processes a relay heartbeat, updates node status, and returns config. // Heartbeat processes a relay heartbeat, updates node status, and returns config.
func Heartbeat(ctx context.Context, node *model.OpenFlareNode, payload HeartbeatPayload) (*HeartbeatResponse, error) { func Heartbeat(ctx context.Context, node *model.OpenFlareNode, payload HeartbeatPayload) (*HeartbeatResponse, error) {
if node == nil { if node == nil {
return nil, fmt.Errorf("relay node is nil") return nil, errors.New("relay node is nil")
} }
payload.Version = strings.TrimSpace(payload.Version) payload.Version = strings.TrimSpace(payload.Version)
+1 -1
View File
@@ -89,7 +89,7 @@ func NewSocketIOClient(baseURL string) *SocketIOClient {
// Connect performs the Engine.IO handshake and starts the polling loop. // Connect performs the Engine.IO handshake and starts the polling loop.
func (c *SocketIOClient) Connect() error { func (c *SocketIOClient) Connect() error {
slog.Debug("Uptime Kuma client starting handshake", "baseURL", c.baseURL) slog.Debug("Uptime Kuma client starting handshake", "baseURL", c.baseURL)
u := fmt.Sprintf("%s/socket.io/?EIO=4&transport=polling", c.baseURL) u := c.baseURL + "/socket.io/?EIO=4&transport=polling"
reqHandshake, err := http.NewRequestWithContext(c.ctx, http.MethodGet, u, nil) reqHandshake, err := http.NewRequestWithContext(c.ctx, http.MethodGet, u, nil)
if err != nil { if err != nil {
return fmt.Errorf("create handshake request failed: %w", err) return fmt.Errorf("create handshake request failed: %w", err)
+4 -3
View File
@@ -5,6 +5,7 @@ package uptimekuma
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"log/slog" "log/slog"
"strings" "strings"
@@ -75,11 +76,11 @@ func SyncToUptimeKuma(ctx context.Context) error {
// 检查是否启用 // 检查是否启用
enabled, _ := repository.GetBoolByKey(ctx, model.ConfigKeyUptimeKumaEnabled) enabled, _ := repository.GetBoolByKey(ctx, model.ConfigKeyUptimeKumaEnabled)
if !enabled { if !enabled {
return fmt.Errorf("uptime Kuma integration is disabled") return errors.New("uptime Kuma integration is disabled")
} }
if !isSyncing.CompareAndSwap(false, true) { if !isSyncing.CompareAndSwap(false, true) {
return fmt.Errorf("sync task is already in progress, please try again later") return errors.New("sync task is already in progress, please try again later")
} }
defer isSyncing.Store(false) defer isSyncing.Store(false)
@@ -225,7 +226,7 @@ func filterOpenFlareMonitors(monitors map[string]Monitor, openFlareTagID int) ma
func routeMonitorURL(ctx context.Context, route *model.ProxyRoute) (string, error) { func routeMonitorURL(ctx context.Context, route *model.ProxyRoute) (string, error) {
if route == nil { if route == nil {
return "", fmt.Errorf("proxy route is nil") return "", errors.New("proxy route is nil")
} }
domains, err := repository.ListZoneDomainsByRouteID(ctx, route.ID) domains, err := repository.ListZoneDomainsByRouteID(ctx, route.ID)
if err != nil { if err != nil {
@@ -5,6 +5,7 @@ package uptimekuma
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"log/slog" "log/slog"
"strings" "strings"
@@ -18,13 +19,13 @@ const monitorListWaitTimeout = 5 * time.Second
// validateKumaConfig 验证 kumaConfig 配置完整性 // validateKumaConfig 验证 kumaConfig 配置完整性
func validateKumaConfig(config *kumaConfig) error { func validateKumaConfig(config *kumaConfig) error {
if strings.TrimSpace(config.URL) == "" { if strings.TrimSpace(config.URL) == "" {
return fmt.Errorf("uptime Kuma URL is not configured") return errors.New("uptime Kuma URL is not configured")
} }
if strings.TrimSpace(config.Username) == "" { if strings.TrimSpace(config.Username) == "" {
return fmt.Errorf("uptime Kuma username is not configured") return errors.New("uptime Kuma username is not configured")
} }
if strings.TrimSpace(config.Password) == "" { if strings.TrimSpace(config.Password) == "" {
return fmt.Errorf("uptime Kuma password is not configured") return errors.New("uptime Kuma password is not configured")
} }
return nil return nil
} }
@@ -65,7 +66,7 @@ func connectAndLoginUptimeKuma(kumaURL, kumaUsername, kumaPassword string) (*Soc
case <-time.After(monitorListWaitTimeout): case <-time.After(monitorListWaitTimeout):
client.Close() client.Close()
slog.Error("Timeout waiting for Uptime Kuma monitorList push event") slog.Error("Timeout waiting for Uptime Kuma monitorList push event")
return nil, fmt.Errorf("timeout waiting for monitorList event from Uptime Kuma") return nil, errors.New("timeout waiting for monitorList event from Uptime Kuma")
} }
return client, nil return client, nil
} }
+3 -2
View File
@@ -4,6 +4,7 @@
package waf package waf
import ( import (
"errors"
"fmt" "fmt"
"slices" "slices"
"sort" "sort"
@@ -35,14 +36,14 @@ func CompileRuleGraph(graph RuleGraph) (RuntimeRuleGraph, error) {
} }
if node.Type == RuleNodeStart { if node.Type == RuleNodeStart {
if runtime.Entry != "" { if runtime.Entry != "" {
return RuntimeRuleGraph{}, fmt.Errorf("规则图包含多个开始节点") return RuntimeRuleGraph{}, errors.New("规则图包含多个开始节点")
} }
runtime.Entry = node.ID runtime.Entry = node.ID
} }
runtime.Nodes[node.ID] = RuntimeRuleNode{Type: node.Type, Config: config} runtime.Nodes[node.ID] = RuntimeRuleNode{Type: node.Type, Config: config}
} }
if runtime.Entry == "" { if runtime.Entry == "" {
return RuntimeRuleGraph{}, fmt.Errorf("规则图缺少开始节点") return RuntimeRuleGraph{}, errors.New("规则图缺少开始节点")
} }
for _, edge := range graph.Edges { for _, edge := range graph.Edges {
node, ok := runtime.Nodes[edge.Source] node, ok := runtime.Nodes[edge.Source]
@@ -68,7 +68,7 @@ func validateRuleGraphLimits(graph RuleGraph) error {
if raw, err := json.Marshal(graph); err != nil { if raw, err := json.Marshal(graph); err != nil {
return fmt.Errorf("规则图无法序列化: %w", err) return fmt.Errorf("规则图无法序列化: %w", err)
} else if len(raw) > maxRuleGraphBytes { } else if len(raw) > maxRuleGraphBytes {
return fmt.Errorf("规则图大小不能超过 256 KiB") return errors.New("规则图大小不能超过 256 KiB")
} }
return nil return nil
} }
@@ -103,7 +103,7 @@ func ImportLegacyTx(ctx context.Context, tx *sql.Tx, postgres bool) (report Impo
`), domain).Scan(&existingID, &existingZoneDomain) `), domain).Scan(&existingID, &existingZoneDomain)
if scanErr == nil { if scanErr == nil {
if existingZoneDomain != root { if existingZoneDomain != root {
report.Conflicts = append(report.Conflicts, fmt.Sprintf("%s: global domain conflict", domain)) report.Conflicts = append(report.Conflicts, domain+": global domain conflict")
} else if item.ProxyRouteID != nil { } else if item.ProxyRouteID != nil {
if _, bindErr := tx.ExecContext(ctx, q(` if _, bindErr := tx.ExecContext(ctx, q(`
UPDATE of_zone_domains UPDATE of_zone_domains
@@ -147,7 +147,7 @@ func ImportLegacyTx(ctx context.Context, tx *sql.Tx, postgres bool) (report Impo
} }
if len(report.Conflicts) > 0 { if len(report.Conflicts) > 0 {
return report, fmt.Errorf("legacy data has conflicts") return report, errors.New("legacy data has conflicts")
} }
return report, nil return report, nil
} }
+1 -1
View File
@@ -204,7 +204,7 @@ func DownloadFile(c *gin.Context) {
} }
} }
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename*=UTF-8''%s", url.PathEscape(fileName))) c.Header("Content-Disposition", "attachment; filename*=UTF-8''"+url.PathEscape(fileName))
filesrv.ServeUpload(c, upload) filesrv.ServeUpload(c, upload)
} }
@@ -69,7 +69,7 @@ func (h *MigrationHandler) ValidatePayload(payload []byte) ([]byte, error) {
return payload, err return payload, err
} }
if active { if active {
return payload, fmt.Errorf("storage migration task is already unresolved") return payload, errors.New("storage migration task is already unresolved")
} }
return normalized, nil return normalized, nil
} }
+1 -1
View File
@@ -152,7 +152,7 @@ func (h *SendEmailHandler) Execute(ctx context.Context, payload []byte) (*task.T
return nil, fmt.Errorf(errSendMailFailed, err) return nil, fmt.Errorf(errSendMailFailed, err)
} }
msg := fmt.Sprintf("邮件成功发送至: %s", req.To) msg := "邮件成功发送至: " + req.To
task.AppendLog(ctx, "%s", msg) task.AppendLog(ctx, "%s", msg)
return &task.TaskResult{ return &task.TaskResult{
+7 -7
View File
@@ -34,18 +34,18 @@ func formatStartupBanner(state startupState) string {
"/ /_/ / /_/ / __/ / / / __/ / / /_/ / / / __/", "/ /_/ / /_/ / __/ / / / __/ / / /_/ / / / __/",
"\\____/ .___/\\___/_/ /_/_/ /_/\\__,_/_/ \\___/ ", "\\____/ .___/\\___/_/ /_/_/ /_/\\__,_/_/ \\___/ ",
" /_/ ", " /_/ ",
fmt.Sprintf(" OpenFlare %s", buildinfo.Version), " OpenFlare " + buildinfo.Version,
"", "",
fmt.Sprintf(" Environment: %s", config.Config.App.Env), " Environment: " + config.Config.App.Env,
fmt.Sprintf(" Runtime: %s/%s (%s)", runtime.GOOS, runtime.GOARCH, runtime.Version()), fmt.Sprintf(" Runtime: %s/%s (%s)", runtime.GOOS, runtime.GOARCH, runtime.Version()),
fmt.Sprintf(" Build time: %s", buildTime()), " Build time: " + buildTime(),
fmt.Sprintf(" Database: %s", formatMigration(state.relationalDB)), " Database: " + formatMigration(state.relationalDB),
fmt.Sprintf(" Analytics: %s", formatMigration(state.clickHouseDB)), " Analytics: " + formatMigration(state.clickHouseDB),
} }
if state.listensForHTTP { if state.listensForHTTP {
lines = append(lines, fmt.Sprintf(" Listening: http://%s", config.Config.App.Addr)) lines = append(lines, " Listening: http://"+config.Config.App.Addr)
} }
lines = append(lines, fmt.Sprintf(" Mode: %s", state.mode), "") lines = append(lines, " Mode: "+state.mode, "")
return strings.Join(lines, "\n") return strings.Join(lines, "\n")
} }
+4 -3
View File
@@ -5,6 +5,7 @@ package objectstore
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"io" "io"
"mime" "mime"
@@ -20,7 +21,7 @@ type localBackend struct {
func newLocalBackend(cfg LocalConfig) (*localBackend, error) { func newLocalBackend(cfg LocalConfig) (*localBackend, error) {
root := filepath.Clean(cfg.Root) root := filepath.Clean(cfg.Root)
if root == "" { if root == "" {
return nil, fmt.Errorf("local root is required") return nil, errors.New("local root is required")
} }
return &localBackend{root: root}, nil return &localBackend{root: root}, nil
} }
@@ -103,7 +104,7 @@ func (b *localBackend) path(key string) (string, error) {
} }
rel, err := filepath.Rel(absRoot, absPath) rel, err := filepath.Rel(absRoot, absPath)
if err != nil || strings.HasPrefix(rel, "..") { if err != nil || strings.HasPrefix(rel, "..") {
return "", fmt.Errorf("storage key escapes local root") return "", errors.New("storage key escapes local root")
} }
return cleanPath, nil return cleanPath, nil
} }
@@ -114,7 +115,7 @@ func (b *localBackend) path(key string) (string, error) {
path := filepath.Join(b.root, cleanKey) path := filepath.Join(b.root, cleanKey)
rel, err := filepath.Rel(b.root, path) rel, err := filepath.Rel(b.root, path)
if err != nil || strings.HasPrefix(rel, "..") { if err != nil || strings.HasPrefix(rel, "..") {
return "", fmt.Errorf("storage key escapes local root") return "", errors.New("storage key escapes local root")
} }
return path, nil return path, nil
} }
@@ -4,7 +4,7 @@
package batchwriter package batchwriter
import ( import (
"fmt" "errors"
"time" "time"
) )
@@ -51,19 +51,19 @@ func DefaultConfig() Config {
func (c Config) validate() error { func (c Config) validate() error {
if c.QueueSize <= 0 { if c.QueueSize <= 0 {
return fmt.Errorf("batchwriter: queue size must be positive") return errors.New("batchwriter: queue size must be positive")
} }
if c.MaxBatchSize <= 0 { if c.MaxBatchSize <= 0 {
return fmt.Errorf("batchwriter: max batch size must be positive") return errors.New("batchwriter: max batch size must be positive")
} }
if c.MinBatchSize < 0 { if c.MinBatchSize < 0 {
return fmt.Errorf("batchwriter: min batch size must be non-negative") return errors.New("batchwriter: min batch size must be non-negative")
} }
if c.FlushInterval <= 0 { if c.FlushInterval <= 0 {
return fmt.Errorf("batchwriter: flush interval must be positive") return errors.New("batchwriter: flush interval must be positive")
} }
if c.MaxFlushWait < 0 { if c.MaxFlushWait < 0 {
return fmt.Errorf("batchwriter: max flush wait must be non-negative") return errors.New("batchwriter: max flush wait must be non-negative")
} }
return nil return nil
} }
+2 -1
View File
@@ -9,6 +9,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"log" "log"
"strconv"
"strings" "strings"
"time" "time"
@@ -83,7 +84,7 @@ func init() {
if err := redisotel.InstrumentTracing( if err := redisotel.InstrumentTracing(
Redis, Redis,
redisotel.WithAttributes( redisotel.WithAttributes(
attribute.String("db.instance", fmt.Sprintf("%v", cfg.DB)), attribute.String("db.instance", strconv.Itoa(cfg.DB)),
attribute.String("db.ip", strings.Join(cfg.Addrs, ",")), attribute.String("db.ip", strings.Join(cfg.Addrs, ",")),
attribute.String("db.system", "Redis"), attribute.String("db.system", "Redis"),
), ),
+1 -1
View File
@@ -41,7 +41,7 @@ func GenerateTokenString() (string, error) {
if _, err := rand.Read(bytes); err != nil { if _, err := rand.Read(bytes); err != nil {
return "", err return "", err
} }
return fmt.Sprintf("at_%s", hex.EncodeToString(bytes)), nil return "at_" + hex.EncodeToString(bytes), nil
} }
// HashToken 计算 Token 的 SHA-256 哈希值用于数据库存储与查询 // HashToken 计算 Token 的 SHA-256 哈希值用于数据库存储与查询
+2 -1
View File
@@ -6,6 +6,7 @@ package analytics
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"time" "time"
@@ -15,7 +16,7 @@ import (
func userAccessLogConn() error { func userAccessLogConn() error {
if db.ChConn == nil { if db.ChConn == nil {
return fmt.Errorf("clickhouse native connection is not initialized") return errors.New("clickhouse native connection is not initialized")
} }
return nil return nil
} }
@@ -5,6 +5,7 @@ package analytics
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
db "github.com/Rain-kl/Wavelet/internal/infra/persistence" db "github.com/Rain-kl/Wavelet/internal/infra/persistence"
@@ -17,7 +18,7 @@ func BatchInsert(ctx context.Context, logs []analyticsmodel.UserAccessLog) error
return nil return nil
} }
if db.ChConn == nil { if db.ChConn == nil {
return fmt.Errorf("clickhouse connection is not initialized") return errors.New("clickhouse connection is not initialized")
} }
batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.UserAccessLog{}.BatchInsertSQL()) batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.UserAccessLog{}.BatchInsertSQL())
@@ -5,6 +5,7 @@ package analytics
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"github.com/Rain-kl/Wavelet/internal/infra/config" "github.com/Rain-kl/Wavelet/internal/infra/config"
@@ -19,7 +20,7 @@ type ClickHouseOperationalStats = analyticsmodel.ClickHouseOperationalStats
// GetClickHouseOperationalStats returns operational metrics for the configured database. // GetClickHouseOperationalStats returns operational metrics for the configured database.
func GetClickHouseOperationalStats(ctx context.Context) (*ClickHouseOperationalStats, error) { func GetClickHouseOperationalStats(ctx context.Context) (*ClickHouseOperationalStats, error) {
if db.ChConn == nil { if db.ChConn == nil {
return nil, fmt.Errorf("clickhouse native connection is not initialized") return nil, errors.New("clickhouse native connection is not initialized")
} }
database := config.Config.ClickHouse.Database database := config.Config.ClickHouse.Database
stats := &ClickHouseOperationalStats{Database: database} stats := &ClickHouseOperationalStats{Database: database}
@@ -5,6 +5,7 @@ package analytics
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"strings" "strings"
"time" "time"
@@ -19,7 +20,7 @@ type NodeAccessLogRegionCount = analyticsmodel.NodeAccessLogRegionCount
func nodeAccessLogConn() (driver.Conn, error) { func nodeAccessLogConn() (driver.Conn, error) {
if db.ChConn == nil { if db.ChConn == nil {
return nil, fmt.Errorf("clickhouse connection is not initialized") return nil, errors.New("clickhouse connection is not initialized")
} }
return db.ChConn, nil return db.ChConn, nil
} }
@@ -5,6 +5,7 @@ package analytics
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"strings" "strings"
"time" "time"
@@ -20,7 +21,7 @@ func BatchInsertNodeAccessLogs(ctx context.Context, logs []analyticsmodel.NodeAc
return nil return nil
} }
if db.ChConn == nil { if db.ChConn == nil {
return fmt.Errorf("clickhouse connection is not initialized") return errors.New("clickhouse connection is not initialized")
} }
batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.NodeAccessLog{}.BatchInsertSQL()) batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.NodeAccessLog{}.BatchInsertSQL())
@@ -5,6 +5,7 @@ package analytics
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"slices" "slices"
"time" "time"
@@ -16,7 +17,7 @@ import (
func observabilityConn() (driver.Conn, error) { func observabilityConn() (driver.Conn, error) {
if db.ChConn == nil { if db.ChConn == nil {
return nil, fmt.Errorf("clickhouse connection is not initialized") return nil, errors.New("clickhouse connection is not initialized")
} }
return db.ChConn, nil return db.ChConn, nil
} }
@@ -5,6 +5,7 @@ package analytics
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"strings" "strings"
"time" "time"
@@ -30,7 +31,7 @@ func BatchInsertNodeMetricSnapshots(ctx context.Context, snapshots []analyticsmo
return nil return nil
} }
if db.ChConn == nil { if db.ChConn == nil {
return fmt.Errorf("clickhouse connection is not initialized") return errors.New("clickhouse connection is not initialized")
} }
batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.NodeMetricSnapshot{}.BatchInsertSQL()) batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.NodeMetricSnapshot{}.BatchInsertSQL())
@@ -102,7 +103,7 @@ func BatchInsertNodeEdgeHealth(ctx context.Context, rows []analyticsmodel.NodeEd
return nil return nil
} }
if db.ChConn == nil { if db.ChConn == nil {
return fmt.Errorf("clickhouse connection is not initialized") return errors.New("clickhouse connection is not initialized")
} }
batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.NodeEdgeHealth{}.BatchInsertSQL()) batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.NodeEdgeHealth{}.BatchInsertSQL())
if err != nil { if err != nil {
@@ -160,7 +161,7 @@ func BatchInsertNodeObsFrps(ctx context.Context, observations []analyticsmodel.N
return nil return nil
} }
if db.ChConn == nil { if db.ChConn == nil {
return fmt.Errorf("clickhouse connection is not initialized") return errors.New("clickhouse connection is not initialized")
} }
batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.NodeObsFrps{}.BatchInsertSQL()) batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.NodeObsFrps{}.BatchInsertSQL())
@@ -223,7 +224,7 @@ func BatchInsertNodeObsFrpc(ctx context.Context, observations []analyticsmodel.N
return nil return nil
} }
if db.ChConn == nil { if db.ChConn == nil {
return fmt.Errorf("clickhouse connection is not initialized") return errors.New("clickhouse connection is not initialized")
} }
batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.NodeObsFrpc{}.BatchInsertSQL()) batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.NodeObsFrpc{}.BatchInsertSQL())
+1 -2
View File
@@ -3,7 +3,6 @@ package repository
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"gorm.io/gorm" "gorm.io/gorm"
@@ -124,7 +123,7 @@ func ListZoneDomainsByIDs(ctx context.Context, domainIDs []uint) ([]model.ZoneDo
for _, id := range domainIDs { for _, id := range domainIDs {
domain, ok := byID[id] domain, ok := byID[id]
if !ok { if !ok {
return nil, fmt.Errorf("one or more zone domains do not exist") return nil, errors.New("one or more zone domains do not exist")
} }
ordered = append(ordered, domain) ordered = append(ordered, domain)
} }
+3 -3
View File
@@ -1,7 +1,7 @@
package geoip package geoip
import ( import (
"fmt" "errors"
"net" "net"
) )
@@ -20,12 +20,12 @@ func (e *EmptyProvider) Initialize() error {
// GetGeoInfo reports that no GeoIP provider has been configured. // GetGeoInfo reports that no GeoIP provider has been configured.
func (e *EmptyProvider) GetGeoInfo(_ net.IP) (*GeoInfo, error) { func (e *EmptyProvider) GetGeoInfo(_ net.IP) (*GeoInfo, error) {
return nil, fmt.Errorf("you are using an empty GeoIP provider, please set a valid provider") return nil, errors.New("you are using an empty GeoIP provider, please set a valid provider")
} }
// UpdateDatabase reports that no GeoIP provider has been configured. // UpdateDatabase reports that no GeoIP provider has been configured.
func (e *EmptyProvider) UpdateDatabase() error { func (e *EmptyProvider) UpdateDatabase() error {
return fmt.Errorf("you are using an empty GeoIP provider, please set a valid provider") return errors.New("you are using an empty GeoIP provider, please set a valid provider")
} }
// Close releases resources held by the empty provider. // Close releases resources held by the empty provider.
+3 -2
View File
@@ -2,6 +2,7 @@
package geoip package geoip
import ( import (
"errors"
"fmt" "fmt"
"log/slog" "log/slog"
"net" "net"
@@ -138,7 +139,7 @@ func InitGeoIP(provider string) {
// GetGeoInfo looks up geographic information for ip using the active provider. // GetGeoInfo looks up geographic information for ip using the active provider.
func GetGeoInfo(ip net.IP) (*GeoInfo, error) { func GetGeoInfo(ip net.IP) (*GeoInfo, error) {
if ip == nil { if ip == nil {
return nil, fmt.Errorf("IP address cannot be nil") return nil, errors.New("IP address cannot be nil")
} }
provider := getProvider() provider := getProvider()
cacheKey := provider.Name() + ":" + ip.String() cacheKey := provider.Name() + ":" + ip.String()
@@ -157,7 +158,7 @@ func GetGeoInfo(ip net.IP) (*GeoInfo, error) {
// LookupGeoInfoWithProvider looks up geographic information using a temporary provider. // LookupGeoInfoWithProvider looks up geographic information using a temporary provider.
func LookupGeoInfoWithProvider(providerName string, ip net.IP) (*GeoInfo, error) { func LookupGeoInfoWithProvider(providerName string, ip net.IP) (*GeoInfo, error) {
if ip == nil { if ip == nil {
return nil, fmt.Errorf("IP address cannot be nil") return nil, errors.New("IP address cannot be nil")
} }
provider, err := providerFactory(normalizeProvider(providerName)) provider, err := providerFactory(normalizeProvider(providerName))
+3 -2
View File
@@ -2,6 +2,7 @@ package geoip
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"io" "io"
"net" "net"
@@ -96,10 +97,10 @@ func (s *MaxMindGeoIPService) GetGeoInfo(ip net.IP) (*GeoInfo, error) {
defer s.mu.RUnlock() defer s.mu.RUnlock()
if s.maxMindDBReader == nil { if s.maxMindDBReader == nil {
return nil, fmt.Errorf("MaxMind database is not initialized or failed to open") return nil, errors.New("MaxMind database is not initialized or failed to open")
} }
if ip == nil { if ip == nil {
return nil, fmt.Errorf("IP address cannot be nil") return nil, errors.New("IP address cannot be nil")
} }
var record Record var record Record
+2 -2
View File
@@ -72,14 +72,14 @@ func copyLimited(dst io.Writer, src io.Reader, maxBytes int64) (int64, error) {
return written, err return written, err
} }
if written > maxBytes { if written > maxBytes {
return written, fmt.Errorf("pages file size out of bounds") return written, errors.New("pages file size out of bounds")
} }
return written, nil return written, nil
} }
func copyAndVerifySize(dst io.Writer, src io.Reader, declaredSize uint64, maxBytes int64) (int64, error) { func copyAndVerifySize(dst io.Writer, src io.Reader, declaredSize uint64, maxBytes int64) (int64, error) {
if declaredSize > uint64(math.MaxInt64) { if declaredSize > uint64(math.MaxInt64) {
return 0, fmt.Errorf("pages file size out of bounds") return 0, errors.New("pages file size out of bounds")
} }
written, err := copyLimited(dst, src, maxBytes) written, err := copyLimited(dst, src, maxBytes)
if err != nil { if err != nil {
+3 -3
View File
@@ -124,7 +124,7 @@ func extractEntries(entries []Entry, destDir string, opts ExtractOptions) error
appendMeasuredFile(measured, normalizedPath, actual) appendMeasuredFile(measured, normalizedPath, actual)
} }
if measured.fileCount == 0 { if measured.fileCount == 0 {
return fmt.Errorf("pages package is empty") return errors.New("pages package is empty")
} }
return nil return nil
} }
@@ -139,7 +139,7 @@ func extractTarFamilyAt(ra io.ReaderAt, size int64, format Format, destDir strin
return err return err
} }
if firstPass.fileCount == 0 { if firstPass.fileCount == 0 {
return fmt.Errorf("pages package is empty") return errors.New("pages package is empty")
} }
commonPrefix := "" commonPrefix := ""
if opts.StripCommonRoot { if opts.StripCommonRoot {
@@ -162,7 +162,7 @@ func extractTarFamilyAt(ra io.ReaderAt, size int64, format Format, destDir strin
return extractErr return extractErr
} }
if secondPass.fileCount != firstPass.fileCount || secondPass.totalSize != firstPass.totalSize { if secondPass.fileCount != firstPass.fileCount || secondPass.totalSize != firstPass.totalSize {
return fmt.Errorf("pages tar package changed between validation and extraction") return errors.New("pages tar package changed between validation and extraction")
} }
return nil return nil
} }
+2 -2
View File
@@ -7,7 +7,7 @@ package pagesarchive
import ( import (
"bytes" "bytes"
"fmt" "errors"
"path/filepath" "path/filepath"
"strings" "strings"
) )
@@ -87,7 +87,7 @@ func DetectFormat(fileName string, data []byte) (Format, error) {
if format, ok := DetectFormatFromBytes(data); ok { if format, ok := DetectFormatFromBytes(data); ok {
return format, nil return format, nil
} }
return "", fmt.Errorf("unsupported pages package format") return "", errors.New("unsupported pages package format")
} }
// Extension returns the canonical file extension for a format (without leading dot). // Extension returns the canonical file extension for a format (without leading dot).
+3 -3
View File
@@ -137,7 +137,7 @@ func scanTarFamilyAt(
enforceLimits bool, enforceLimits bool,
) (*measuredArchive, error) { ) (*measuredArchive, error) {
if size < 0 { if size < 0 {
return nil, fmt.Errorf("invalid pages package size") return nil, errors.New("invalid pages package size")
} }
tarReader, closeReader, err := openTarFamilyReader(io.NewSectionReader(ra, 0, size), format) tarReader, closeReader, err := openTarFamilyReader(io.NewSectionReader(ra, 0, size), format)
if err != nil { if err != nil {
@@ -183,7 +183,7 @@ func scanTarReader(tarReader *tar.Reader, limits Limits, enforceLimits bool) (*m
func buildMeasuredManifest(measured *measuredArchive, opts InspectOptions) (*Manifest, error) { func buildMeasuredManifest(measured *measuredArchive, opts InspectOptions) (*Manifest, error) {
if measured == nil || measured.fileCount == 0 { if measured == nil || measured.fileCount == 0 {
return nil, fmt.Errorf("pages package is empty") return nil, errors.New("pages package is empty")
} }
targetEntryPath, err := resolveTargetEntryPath(opts.RootDir, opts.EntryFile) targetEntryPath, err := resolveTargetEntryPath(opts.RootDir, opts.EntryFile)
if err != nil { if err != nil {
@@ -231,7 +231,7 @@ func prepareMeasuredFile(measured *measuredArchive, normalizedPath string, decla
} }
remaining := limits.MaxTotalBytes - measured.totalSize remaining := limits.MaxTotalBytes - measured.totalSize
if remaining < 0 || declaredSize > uint64(remaining) { //nolint:gosec // remaining is checked non-negative if remaining < 0 || declaredSize > uint64(remaining) { //nolint:gosec // remaining is checked non-negative
return fmt.Errorf("pages extracted size exceeds limit") return errors.New("pages extracted size exceeds limit")
} }
return nil return nil
} }
+2 -1
View File
@@ -8,6 +8,7 @@ import (
"archive/zip" "archive/zip"
"compress/bzip2" "compress/bzip2"
"compress/gzip" "compress/gzip"
"errors"
"fmt" "fmt"
"io" "io"
"os" "os"
@@ -52,7 +53,7 @@ func (z sevenZipArchiveFile) Open() (io.ReadCloser, error) {
// Tar-family archives use the sequential streaming paths in inspect.go/extract.go. // Tar-family archives use the sequential streaming paths in inspect.go/extract.go.
func listRandomAccessEntriesAt(ra io.ReaderAt, size int64, format Format) ([]Entry, error) { func listRandomAccessEntriesAt(ra io.ReaderAt, size int64, format Format) ([]Entry, error) {
if size < 0 { if size < 0 {
return nil, fmt.Errorf("invalid pages package size") return nil, errors.New("invalid pages package size")
} }
switch format { switch format {
case FormatZip: case FormatZip:
+5 -4
View File
@@ -4,6 +4,7 @@
package pagesarchive package pagesarchive
import ( import (
"errors"
"fmt" "fmt"
"path" "path"
"strings" "strings"
@@ -18,7 +19,7 @@ func NormalizeLogicalPath(raw string, allowEmpty bool) (string, error) {
if allowEmpty { if allowEmpty {
return "", nil return "", nil
} }
return "", fmt.Errorf("pages path is required") return "", errors.New("pages path is required")
} }
if err := validateLogicalPathText(raw); err != nil { if err := validateLogicalPathText(raw); err != nil {
return "", err return "", err
@@ -29,7 +30,7 @@ func NormalizeLogicalPath(raw string, allowEmpty bool) (string, error) {
if allowEmpty { if allowEmpty {
return "", nil return "", nil
} }
return "", fmt.Errorf("pages path is required") return "", errors.New("pages path is required")
} }
if strings.HasPrefix(cleaned, "/") || cleaned == ".." || strings.HasPrefix(cleaned, "../") { if strings.HasPrefix(cleaned, "/") || cleaned == ".." || strings.HasPrefix(cleaned, "../") {
return "", fmt.Errorf("pages path escapes directory: %s", raw) return "", fmt.Errorf("pages path escapes directory: %s", raw)
@@ -39,7 +40,7 @@ func NormalizeLogicalPath(raw string, allowEmpty bool) (string, error) {
func validateLogicalPathText(raw string) error { func validateLogicalPathText(raw string) error {
if !utf8.ValidString(raw) { if !utf8.ValidString(raw) {
return fmt.Errorf("pages path is not valid UTF-8") return errors.New("pages path is not valid UTF-8")
} }
if strings.Contains(raw, "\\") { if strings.Contains(raw, "\\") {
return fmt.Errorf("pages path must use POSIX separators: %s", raw) return fmt.Errorf("pages path must use POSIX separators: %s", raw)
@@ -56,7 +57,7 @@ func validateLogicalPathText(raw string) error {
func validateLogicalPathRunes(raw string) error { func validateLogicalPathRunes(raw string) error {
for _, r := range raw { for _, r := range raw {
if r == 0 || unicode.IsControl(r) { if r == 0 || unicode.IsControl(r) {
return fmt.Errorf("pages path contains a control character") return errors.New("pages path contains a control character")
} }
if r == '\'' || r == '"' || r == ';' { if r == '\'' || r == '"' || r == ';' {
return fmt.Errorf("pages path contains an unsupported character: %s", raw) return fmt.Errorf("pages path contains an unsupported character: %s", raw)
+1 -1
View File
@@ -265,7 +265,7 @@ func (p *LarkPusher) ValidateConfig(cfg Config) error {
} }
func larkSign(secret string, timestamp int64) (string, error) { func larkSign(secret string, timestamp int64) (string, error) {
stringToSign := fmt.Sprintf("%v", timestamp) + "\n" + secret stringToSign := strconv.FormatInt(timestamp, 10) + "\n" + secret
h := hmac.New(sha256.New, []byte(stringToSign)) h := hmac.New(sha256.New, []byte(stringToSign))
_, err := h.Write(nil) _, err := h.Write(nil)
if err != nil { if err != nil {
+15 -14
View File
@@ -14,6 +14,7 @@ import (
"path" "path"
"regexp" "regexp"
"sort" "sort"
"strconv"
"strings" "strings"
) )
@@ -168,32 +169,32 @@ func DedupeSupportFiles(files []SupportFile) []SupportFile {
func renderMainConfigTemplate(templateText string, cfg ConfigSnapshot, limitReqRates []string) string { func renderMainConfigTemplate(templateText string, cfg ConfigSnapshot, limitReqRates []string) string {
replacer := strings.NewReplacer( replacer := strings.NewReplacer(
"{{OpenRestyWorkerProcesses}}", cfg.WorkerProcesses, "{{OpenRestyWorkerProcesses}}", cfg.WorkerProcesses,
"{{OpenRestyWorkerConnections}}", fmt.Sprintf("%d", cfg.WorkerConnections), "{{OpenRestyWorkerConnections}}", strconv.Itoa(cfg.WorkerConnections),
"{{OpenRestyWorkerRlimitNofile}}", fmt.Sprintf("%d", cfg.WorkerRlimitNofile), "{{OpenRestyWorkerRlimitNofile}}", strconv.Itoa(cfg.WorkerRlimitNofile),
"{{OpenRestyConnectionUpgradeMap}}", renderConnectionUpgradeMap(), "{{OpenRestyConnectionUpgradeMap}}", renderConnectionUpgradeMap(),
"{{OpenRestyDefaultServerBlock}}", renderDefaultServerBlock(cfg.DefaultServerReturnStatus, cfg.HTTP3Enabled), "{{OpenRestyDefaultServerBlock}}", renderDefaultServerBlock(cfg.DefaultServerReturnStatus, cfg.HTTP3Enabled),
"{{OpenRestyAccessLogPath}}", AccessLogPlaceholder, "{{OpenRestyAccessLogPath}}", AccessLogPlaceholder,
"{{OpenRestyErrorLogPath}}", ErrorLogPlaceholder, "{{OpenRestyErrorLogPath}}", ErrorLogPlaceholder,
"{{OpenRestyEventsUseDirective}}", renderTemplateDirective(cfg.EventsUse != "", fmt.Sprintf("use %s;", cfg.EventsUse)), "{{OpenRestyEventsUseDirective}}", renderTemplateDirective(cfg.EventsUse != "", fmt.Sprintf("use %s;", cfg.EventsUse)),
"{{OpenRestyEventsMultiAcceptDirective}}", renderTemplateDirective(cfg.EventsMultiAcceptEnabled, "multi_accept on;"), "{{OpenRestyEventsMultiAcceptDirective}}", renderTemplateDirective(cfg.EventsMultiAcceptEnabled, "multi_accept on;"),
"{{OpenRestyKeepaliveTimeout}}", fmt.Sprintf("%d", cfg.KeepaliveTimeout), "{{OpenRestyKeepaliveTimeout}}", strconv.Itoa(cfg.KeepaliveTimeout),
"{{OpenRestyKeepaliveRequests}}", fmt.Sprintf("%d", cfg.KeepaliveRequests), "{{OpenRestyKeepaliveRequests}}", strconv.Itoa(cfg.KeepaliveRequests),
"{{OpenRestyClientHeaderTimeout}}", fmt.Sprintf("%d", cfg.ClientHeaderTimeout), "{{OpenRestyClientHeaderTimeout}}", strconv.Itoa(cfg.ClientHeaderTimeout),
"{{OpenRestyClientBodyTimeout}}", fmt.Sprintf("%d", cfg.ClientBodyTimeout), "{{OpenRestyClientBodyTimeout}}", strconv.Itoa(cfg.ClientBodyTimeout),
"{{OpenRestyClientMaxBodySize}}", cfg.ClientMaxBodySize, "{{OpenRestyClientMaxBodySize}}", cfg.ClientMaxBodySize,
"{{OpenRestyLargeClientHeaderBuffers}}", cfg.LargeClientHeaderBuffers, "{{OpenRestyLargeClientHeaderBuffers}}", cfg.LargeClientHeaderBuffers,
"{{OpenRestySendTimeout}}", fmt.Sprintf("%d", cfg.SendTimeout), "{{OpenRestySendTimeout}}", strconv.Itoa(cfg.SendTimeout),
"{{OpenRestyProxyConnectTimeout}}", fmt.Sprintf("%d", cfg.ProxyConnectTimeout), "{{OpenRestyProxyConnectTimeout}}", strconv.Itoa(cfg.ProxyConnectTimeout),
"{{OpenRestyProxySendTimeout}}", fmt.Sprintf("%d", cfg.ProxySendTimeout), "{{OpenRestyProxySendTimeout}}", strconv.Itoa(cfg.ProxySendTimeout),
"{{OpenRestyProxyReadTimeout}}", fmt.Sprintf("%d", cfg.ProxyReadTimeout), "{{OpenRestyProxyReadTimeout}}", strconv.Itoa(cfg.ProxyReadTimeout),
"{{OpenRestyProxyRequestBuffering}}", onOff(cfg.ProxyRequestBuffering), "{{OpenRestyProxyRequestBuffering}}", onOff(cfg.ProxyRequestBuffering),
"{{OpenRestyProxyBuffering}}", onOff(cfg.ProxyBufferingEnabled), "{{OpenRestyProxyBuffering}}", onOff(cfg.ProxyBufferingEnabled),
"{{OpenRestyProxyBuffers}}", cfg.ProxyBuffers, "{{OpenRestyProxyBuffers}}", cfg.ProxyBuffers,
"{{OpenRestyProxyBufferSize}}", cfg.ProxyBufferSize, "{{OpenRestyProxyBufferSize}}", cfg.ProxyBufferSize,
"{{OpenRestyProxyBusyBuffersSize}}", cfg.ProxyBusyBuffersSize, "{{OpenRestyProxyBusyBuffersSize}}", cfg.ProxyBusyBuffersSize,
"{{OpenRestyGzip}}", onOff(cfg.GzipEnabled), "{{OpenRestyGzip}}", onOff(cfg.GzipEnabled),
"{{OpenRestyGzipMinLength}}", fmt.Sprintf("%d", cfg.GzipMinLength), "{{OpenRestyGzipMinLength}}", strconv.Itoa(cfg.GzipMinLength),
"{{OpenRestyGzipCompLevel}}", fmt.Sprintf("%d", cfg.GzipCompLevel), "{{OpenRestyGzipCompLevel}}", strconv.Itoa(cfg.GzipCompLevel),
"{{OpenRestyResolverDirective}}", renderTemplateDirective(cfg.Resolvers != "", fmt.Sprintf("resolver %s;", cfg.Resolvers)), "{{OpenRestyResolverDirective}}", renderTemplateDirective(cfg.Resolvers != "", fmt.Sprintf("resolver %s;", cfg.Resolvers)),
"{{OpenRestyCacheBlock}}", renderOpenRestyCacheTemplateBlock(cfg, limitReqRates), "{{OpenRestyCacheBlock}}", renderOpenRestyCacheTemplateBlock(cfg, limitReqRates),
"{{OpenRestyRouteConfigInclude}}", RouteConfigPlaceholder, "{{OpenRestyRouteConfigInclude}}", RouteConfigPlaceholder,
@@ -765,7 +766,7 @@ func renderDefaultServerBlock(statusCode int, http3Enabled bool) string {
" }", " }",
"", "",
" server {", " server {",
fmt.Sprintf(" listen 443 ssl default_server;%s", h3Default), " listen 443 ssl default_server;" + h3Default,
" server_name _;", " server_name _;",
"", "",
" ssl_reject_handshake on;", " ssl_reject_handshake on;",
@@ -946,7 +947,7 @@ func buildPathPrefixMatchPattern(rules []string) string {
parts = append(parts, "/") parts = append(parts, "/")
continue continue
} }
parts = append(parts, fmt.Sprintf("%s(?:/|$)", regexp.QuoteMeta(trimmed))) parts = append(parts, regexp.QuoteMeta(trimmed)+"(?:/|$)")
} }
return fmt.Sprintf("^(?:%s)", strings.Join(parts, "|")) return fmt.Sprintf("^(?:%s)", strings.Join(parts, "|"))
} }
+2 -1
View File
@@ -4,6 +4,7 @@
package openresty package openresty
import ( import (
"errors"
"fmt" "fmt"
"sort" "sort"
"strconv" "strconv"
@@ -22,7 +23,7 @@ const (
func ParseStatusCodeTag(tag string) (lo, hi int, err error) { func ParseStatusCodeTag(tag string) (lo, hi int, err error) {
tag = strings.TrimSpace(tag) tag = strings.TrimSpace(tag)
if tag == "" { if tag == "" {
return 0, 0, fmt.Errorf("状态码标签不能为空") return 0, 0, errors.New("状态码标签不能为空")
} }
if before, after, ok := strings.Cut(tag, "-"); ok { if before, after, ok := strings.Cut(tag, "-"); ok {
lo, err = strconv.Atoi(before) lo, err = strconv.Atoi(before)
+3 -3
View File
@@ -29,13 +29,13 @@ func Bytes2Size(num int64) string {
numStr = fmt.Sprintf("%.2f", float64(num)/float64(sizeGB)) numStr = fmt.Sprintf("%.2f", float64(num)/float64(sizeGB))
unit = "GB" unit = "GB"
case num/int64(sizeMB) >= 1: case num/int64(sizeMB) >= 1:
numStr = fmt.Sprintf("%d", int(float64(num)/float64(sizeMB))) numStr = strconv.Itoa(int(float64(num) / float64(sizeMB)))
unit = "MB" unit = "MB"
case num/int64(sizeKB) >= 1: case num/int64(sizeKB) >= 1:
numStr = fmt.Sprintf("%d", int(float64(num)/float64(sizeKB))) numStr = strconv.Itoa(int(float64(num) / float64(sizeKB)))
unit = "KB" unit = "KB"
default: default:
numStr = fmt.Sprintf("%d", num) numStr = strconv.FormatInt(num, 10)
} }
return numStr + " " + unit return numStr + " " + unit
} }
+5 -2
View File
@@ -1,6 +1,9 @@
package util package util
import "fmt" import (
"fmt"
"strconv"
)
// Interface2String converts a string, int, or float64 value to its string representation. // Interface2String converts a string, int, or float64 value to its string representation.
func Interface2String(inter any) string { func Interface2String(inter any) string {
@@ -8,7 +11,7 @@ func Interface2String(inter any) string {
case string: case string:
return v return v
case int: case int:
return fmt.Sprintf("%d", v) return strconv.Itoa(v)
case float64: case float64:
return fmt.Sprintf("%f", v) return fmt.Sprintf("%f", v)
} }
+3 -2
View File
@@ -6,6 +6,7 @@ package main
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"os" "os"
"time" "time"
@@ -32,7 +33,7 @@ func main() {
func run() error { func run() error {
if !db.ChConnReady() { if !db.ChConnReady() {
return fmt.Errorf("ChConn not ready — check config.yaml clickhouse.enabled") return errors.New("ChConn not ready — check config.yaml clickhouse.enabled")
} }
ctx := context.Background() ctx := context.Background()
chwriter.Init(ctx) chwriter.Init(ctx)
@@ -76,7 +77,7 @@ func waitForSnapshot(ctx context.Context, nodeID string, now time.Time) error {
} }
time.Sleep(pollInterval) time.Sleep(pollInterval)
} }
return fmt.Errorf("not flushed within timeout") return errors.New("not flushed within timeout")
} }
func assertLatestIncludes(ctx context.Context, nodeID string, now time.Time) error { func assertLatestIncludes(ctx context.Context, nodeID string, now time.Time) error {