mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-28 05:46:36 +08:00
unparam 死代码清理 12→2(保留 2 处 objectstore 构造函数统一签名):移除 10 处恒 nil error / 从未使用的结果(getPoWConfigForRoute 的恒 nil *PoWConfig、getSQLiteOverview/getPostgresOverview/getStatus/loadKumaConfig/filterExpectedRoutes 的恒 nil error、rawJSONString/parsePositiveInt 的弃用 bool、buildProxyRoute 的弃用 []ZoneDomain、getLocked 的恒 nil error),同步简化 12+ 处调用方与死错误检查。9 个受影响包测试通过。metric 持平 8(改进在基准之外)。
Result: {"status":"keep","total_issues":8,"golint_canonicalheader":0,"golint_errname":0,"golint_errorlint":1,"golint_exhaustive":0,"golint_forcetypeassert":0,"golint_gosec":0,"golint_intrange":0,"golint_modernize":3,"golint_nilnil":3,"golint_perfsprint":0,"golint_prealloc":0,"golint_recvcheck":1,"golint_usestdlibvars":0,"golint_wastedassign":0,"golint_total":8,"golint_test_testifylint":0,"golint_test_thelper":0,"golint_test_usetesting":0,"golint_test_total":0,"golint_vetx_total":0,"eslint_problems":0,"eslint_errors":0,"eslint_warnings":0,"tsc_errors":0,"measure_s":43}
This commit is contained in:
@@ -13,3 +13,4 @@
|
|||||||
{"run":12,"commit":"e5f6b0a","metric":33,"metrics":{"golint_canonicalheader":0,"golint_errname":0,"golint_errorlint":1,"golint_forcetypeassert":0,"golint_gosec":0,"golint_intrange":0,"golint_modernize":3,"golint_nilnil":3,"golint_perfsprint":0,"golint_prealloc":0,"golint_recvcheck":1,"golint_usestdlibvars":0,"golint_wastedassign":0,"golint_total":8,"golint_test_testifylint":20,"golint_test_thelper":3,"golint_test_usetesting":2,"golint_test_total":25,"eslint_problems":0,"eslint_errors":0,"eslint_warnings":0,"tsc_errors":0,"measure_s":37},"status":"keep","description":"基准扩展(文档化):新增测试代码质量维度 25 处(testifylint 20 + thelper 3 + usetesting 2),生产代码 8 处刻意保留不变。新基线 total=33。","timestamp":1786874744438,"segment":0,"confidence":4.878048780487805,"asi":{"hypothesis":"扩展基准到测试代码质量维度(testifylint 20 + thelper 3 + usetesting 2 = 25)","insight":"刻意排除 paralleltest/tparallel(共享 DB/redis 状态 + 本环境无法跑测试,t.Parallel 有风险)—— 这是范围扩展(抬高门槛),不是 gaming;基准定义已写入 prompt.md","next_action_hint":"修 25 处测试问题:float-compare 3(InDelta)、require-error 3、encoded-compare 1(JSONEq)、empty 3、contains 1、error-is-as 3、len 3、go-require-in-handler 2、t.Helper 3、os.MkdirTemp→t.TempDir 2"}}
|
{"run":12,"commit":"e5f6b0a","metric":33,"metrics":{"golint_canonicalheader":0,"golint_errname":0,"golint_errorlint":1,"golint_forcetypeassert":0,"golint_gosec":0,"golint_intrange":0,"golint_modernize":3,"golint_nilnil":3,"golint_perfsprint":0,"golint_prealloc":0,"golint_recvcheck":1,"golint_usestdlibvars":0,"golint_wastedassign":0,"golint_total":8,"golint_test_testifylint":20,"golint_test_thelper":3,"golint_test_usetesting":2,"golint_test_total":25,"eslint_problems":0,"eslint_errors":0,"eslint_warnings":0,"tsc_errors":0,"measure_s":37},"status":"keep","description":"基准扩展(文档化):新增测试代码质量维度 25 处(testifylint 20 + thelper 3 + usetesting 2),生产代码 8 处刻意保留不变。新基线 total=33。","timestamp":1786874744438,"segment":0,"confidence":4.878048780487805,"asi":{"hypothesis":"扩展基准到测试代码质量维度(testifylint 20 + thelper 3 + usetesting 2 = 25)","insight":"刻意排除 paralleltest/tparallel(共享 DB/redis 状态 + 本环境无法跑测试,t.Parallel 有风险)—— 这是范围扩展(抬高门槛),不是 gaming;基准定义已写入 prompt.md","next_action_hint":"修 25 处测试问题:float-compare 3(InDelta)、require-error 3、encoded-compare 1(JSONEq)、empty 3、contains 1、error-is-as 3、len 3、go-require-in-handler 2、t.Helper 3、os.MkdirTemp→t.TempDir 2"}}
|
||||||
{"run":13,"commit":"63a24da","metric":8,"metrics":{"golint_canonicalheader":0,"golint_errname":0,"golint_errorlint":1,"golint_forcetypeassert":0,"golint_gosec":0,"golint_intrange":0,"golint_modernize":3,"golint_nilnil":3,"golint_perfsprint":0,"golint_prealloc":0,"golint_recvcheck":1,"golint_usestdlibvars":0,"golint_wastedassign":0,"golint_total":8,"golint_test_testifylint":0,"golint_test_thelper":0,"golint_test_usetesting":0,"golint_test_total":0,"eslint_problems":0,"eslint_errors":0,"eslint_warnings":0,"tsc_errors":0,"measure_s":39},"status":"keep","description":"测试代码质量 25→0:assert↔require 一致性(fail-fast)、float 精确比较→InDelta、Equal(\"\",x)→Empty、Equal(len)→Len、errors.Is/As→ErrorIs/ErrorAs、JSON 字符串→JSONEq、handler goroutine 内 require→assert(真健壮性修复)、t.Helper()、os.MkdirTemp→t.TempDir()(符合 repo AGENTS 约束)。","timestamp":1786875177918,"segment":0,"confidence":4.3478260869565215,"asi":{"hypothesis":"修完测试代码质量维度 25 处(testifylint 20 + thelper 3 + usetesting 2)","insight":"批量修复过程揭示 testifylint 默认 max-same-issues=3 会掩盖重复模式(len(entries) 出现 4+ 次、float64(3) 4 次),需 --max-issues-per-linter=0 反复收敛;全部修复语义中性(assert↔require 仅 fail-fast 差异,InDelta/JSONEq/Empty/Len/ErrorIs 等价断言,t.Helper/t.TempDir 纯改善)。go-require 类(handler 内 require→assert)是真正的健壮性修复","next_action_hint":"测试维度清零。生产代码剩余 8 全部刻意保留。可选深化:gocritic 更多子检查/staticcheck 更多(SA 系列)扫描、或 biome check 格式一次性提交、或前端 a11y(eslint jsx-a11y 已含于 next core-web-vitals 默认关闭项)"}}
|
{"run":13,"commit":"63a24da","metric":8,"metrics":{"golint_canonicalheader":0,"golint_errname":0,"golint_errorlint":1,"golint_forcetypeassert":0,"golint_gosec":0,"golint_intrange":0,"golint_modernize":3,"golint_nilnil":3,"golint_perfsprint":0,"golint_prealloc":0,"golint_recvcheck":1,"golint_usestdlibvars":0,"golint_wastedassign":0,"golint_total":8,"golint_test_testifylint":0,"golint_test_thelper":0,"golint_test_usetesting":0,"golint_test_total":0,"eslint_problems":0,"eslint_errors":0,"eslint_warnings":0,"tsc_errors":0,"measure_s":39},"status":"keep","description":"测试代码质量 25→0:assert↔require 一致性(fail-fast)、float 精确比较→InDelta、Equal(\"\",x)→Empty、Equal(len)→Len、errors.Is/As→ErrorIs/ErrorAs、JSON 字符串→JSONEq、handler goroutine 内 require→assert(真健壮性修复)、t.Helper()、os.MkdirTemp→t.TempDir()(符合 repo AGENTS 约束)。","timestamp":1786875177918,"segment":0,"confidence":4.3478260869565215,"asi":{"hypothesis":"修完测试代码质量维度 25 处(testifylint 20 + thelper 3 + usetesting 2)","insight":"批量修复过程揭示 testifylint 默认 max-same-issues=3 会掩盖重复模式(len(entries) 出现 4+ 次、float64(3) 4 次),需 --max-issues-per-linter=0 反复收敛;全部修复语义中性(assert↔require 仅 fail-fast 差异,InDelta/JSONEq/Empty/Len/ErrorIs 等价断言,t.Helper/t.TempDir 纯改善)。go-require 类(handler 内 require→assert)是真正的健壮性修复","next_action_hint":"测试维度清零。生产代码剩余 8 全部刻意保留。可选深化:gocritic 更多子检查/staticcheck 更多(SA 系列)扫描、或 biome check 格式一次性提交、或前端 a11y(eslint jsx-a11y 已含于 next core-web-vitals 默认关闭项)"}}
|
||||||
{"run":14,"commit":"65c02ef","metric":8,"metrics":{"golint_canonicalheader":0,"golint_errname":0,"golint_errorlint":1,"golint_exhaustive":0,"golint_forcetypeassert":0,"golint_gosec":0,"golint_intrange":0,"golint_modernize":3,"golint_nilnil":3,"golint_perfsprint":0,"golint_prealloc":0,"golint_recvcheck":1,"golint_usestdlibvars":0,"golint_wastedassign":0,"golint_total":8,"golint_test_testifylint":0,"golint_test_thelper":0,"golint_test_usetesting":0,"golint_test_total":0,"eslint_problems":0,"eslint_errors":0,"eslint_warnings":0,"tsc_errors":0,"measure_s":36},"status":"keep","description":"基准扩展 exhaustive(文档化)+ 12→0:枚举 switch 补显式 case(全部与现有 default 行为等价,fail-explicit 防未来枚举静默落入 default);source_tasks.go 为控制复杂度合并两个等价校验条件。","timestamp":1786875548060,"segment":0,"confidence":4.25531914893617,"asi":{"hypothesis":"基准扩展 exhaustive(12 处枚举 switch 显式化)+ 全量修复","insight":"12 处全部是 default 已正确处理、缺显式 case 的类型;补显式 case 仅为 fail-explicit(未来枚举新增不会静默落入 default)。source_tasks 补 case 后 Execute 复杂度 20→21 触发 cyclop,合并两个 ActionInvalid 条件(逻辑等价)降回 19。cyclop 与 exhaustive 的张力:显式 case 也计入复杂度","next_action_hint":"剩余 8 全为刻意保留。可再深化:sloglint 全量、govet 附加分析器、或前端 jsx-a11y/next 规则已有覆盖。也可将剩余 8 处文档化后收尾总结"}}
|
{"run":14,"commit":"65c02ef","metric":8,"metrics":{"golint_canonicalheader":0,"golint_errname":0,"golint_errorlint":1,"golint_exhaustive":0,"golint_forcetypeassert":0,"golint_gosec":0,"golint_intrange":0,"golint_modernize":3,"golint_nilnil":3,"golint_perfsprint":0,"golint_prealloc":0,"golint_recvcheck":1,"golint_usestdlibvars":0,"golint_wastedassign":0,"golint_total":8,"golint_test_testifylint":0,"golint_test_thelper":0,"golint_test_usetesting":0,"golint_test_total":0,"eslint_problems":0,"eslint_errors":0,"eslint_warnings":0,"tsc_errors":0,"measure_s":36},"status":"keep","description":"基准扩展 exhaustive(文档化)+ 12→0:枚举 switch 补显式 case(全部与现有 default 行为等价,fail-explicit 防未来枚举静默落入 default);source_tasks.go 为控制复杂度合并两个等价校验条件。","timestamp":1786875548060,"segment":0,"confidence":4.25531914893617,"asi":{"hypothesis":"基准扩展 exhaustive(12 处枚举 switch 显式化)+ 全量修复","insight":"12 处全部是 default 已正确处理、缺显式 case 的类型;补显式 case 仅为 fail-explicit(未来枚举新增不会静默落入 default)。source_tasks 补 case 后 Execute 复杂度 20→21 触发 cyclop,合并两个 ActionInvalid 条件(逻辑等价)降回 19。cyclop 与 exhaustive 的张力:显式 case 也计入复杂度","next_action_hint":"剩余 8 全为刻意保留。可再深化:sloglint 全量、govet 附加分析器、或前端 jsx-a11y/next 规则已有覆盖。也可将剩余 8 处文档化后收尾总结"}}
|
||||||
|
{"run":15,"commit":"d7b8f44","metric":8,"metrics":{"golint_canonicalheader":0,"golint_errname":0,"golint_errorlint":1,"golint_exhaustive":0,"golint_forcetypeassert":0,"golint_gosec":0,"golint_intrange":0,"golint_modernize":3,"golint_nilnil":3,"golint_perfsprint":0,"golint_prealloc":0,"golint_recvcheck":1,"golint_usestdlibvars":0,"golint_wastedassign":0,"golint_total":8,"golint_test_testifylint":0,"golint_test_thelper":0,"golint_test_usetesting":0,"golint_test_total":0,"golint_vetx_total":0,"eslint_problems":0,"eslint_errors":0,"eslint_warnings":0,"tsc_errors":0,"measure_s":37},"status":"keep","description":"修复 geoip/runtime.go 真死代码:ensureServerMMDB 的 os.Stat 错误被 if-init 遮蔽,`err != nil && !os.IsNotExist(err)` 恒为 false(外层 err 恒 nil),防御检查从未生效;改为显式捕获 statErr,stat 非 not-exist 错误现在正确返回。基准新增第 4 维度 govet nilness+unusedwrite(文档化扩展),当前 0。","timestamp":1786875949461,"segment":0,"confidence":4.166666666666667,"asi":{"hypothesis":"govet nilness 真实死代码 bug:ensureServerMMDB 的 stat 错误被 if-init 遮蔽,!os.IsNotExist(err) 恒为死条件(外层 err 恒 nil)","insight":"修复:显式捕获 statErr,使防御检查生效(stat 权限错误现在立即返回,不再静默吞掉后走 WriteFile 失败)。顺带基准扩展第 4 维度 govet nilness+unusedwrite(文档化,survey 过 fatcontext/containedctx/unparam/gocritic+29 检查:unparam 有 6+ 处真实死结果但需签名改动,留待下轮)","next_action_hint":"下轮候选:unparam(6+ 处 always-nil/never-used 结果,含 getSQLiteOverview/getPostgresOverview/getStatus 等,需改签名+调用方,churn 中等但都是真实死代码);或 fatcontext/containedctx(3+3 处,需逐处判断是否真反模式)"}}
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ func formatBytes(bytes uint64) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getSQLiteOverview 获取 SQLite 数据库概览信息
|
// getSQLiteOverview 获取 SQLite 数据库概览信息
|
||||||
func getSQLiteOverview(gormDB *gorm.DB) (DBOverviewResponse, error) {
|
func getSQLiteOverview(gormDB *gorm.DB) DBOverviewResponse {
|
||||||
name := config.Config.Database.SQLitePath
|
name := config.Config.Database.SQLitePath
|
||||||
if name == "" {
|
if name == "" {
|
||||||
name = "./data/openflare.db"
|
name = "./data/openflare.db"
|
||||||
@@ -144,11 +144,11 @@ func getSQLiteOverview(gormDB *gorm.DB) (DBOverviewResponse, error) {
|
|||||||
Size: sizeStr,
|
Size: sizeStr,
|
||||||
TableCount: tableCount,
|
TableCount: tableCount,
|
||||||
Connections: connCount,
|
Connections: connCount,
|
||||||
}, nil
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPostgresOverview 获取 PostgreSQL 数据库概览信息
|
// getPostgresOverview 获取 PostgreSQL 数据库概览信息
|
||||||
func getPostgresOverview(gormDB *gorm.DB) (DBOverviewResponse, error) {
|
func getPostgresOverview(gormDB *gorm.DB) DBOverviewResponse {
|
||||||
name := config.Config.Database.Database
|
name := config.Config.Database.Database
|
||||||
|
|
||||||
var version string
|
var version string
|
||||||
@@ -192,7 +192,7 @@ func getPostgresOverview(gormDB *gorm.DB) (DBOverviewResponse, error) {
|
|||||||
Size: sizeStr,
|
Size: sizeStr,
|
||||||
TableCount: tableCount,
|
TableCount: tableCount,
|
||||||
Connections: connCount,
|
Connections: connCount,
|
||||||
}, nil
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDBOverview 获取数据库运行概览
|
// GetDBOverview 获取数据库运行概览
|
||||||
@@ -214,17 +214,11 @@ func GetDBOverview(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var overview DBOverviewResponse
|
var overview DBOverviewResponse
|
||||||
var err error
|
|
||||||
|
|
||||||
if !config.Config.Database.Enabled {
|
if !config.Config.Database.Enabled {
|
||||||
overview, err = getSQLiteOverview(gormDB)
|
overview = getSQLiteOverview(gormDB)
|
||||||
} else {
|
} else {
|
||||||
overview, err = getPostgresOverview(gormDB)
|
overview = getPostgresOverview(gormDB)
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
response.AbortInternal(c, err.Error())
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, response.OK(overview))
|
c.JSON(http.StatusOK, response.OK(overview))
|
||||||
|
|||||||
@@ -56,11 +56,11 @@ func GetLogs(c *gin.Context) {
|
|||||||
limitStr := c.DefaultQuery("limit", "200")
|
limitStr := c.DefaultQuery("limit", "200")
|
||||||
|
|
||||||
var cursor, limit int
|
var cursor, limit int
|
||||||
if _, err := parsePositiveInt(cursorStr, &cursor); err != nil {
|
if err := parsePositiveInt(cursorStr, &cursor); err != nil {
|
||||||
response.AbortWithError(c, http.StatusBadRequest, admin.InvalidCursorParam)
|
response.AbortWithError(c, http.StatusBadRequest, admin.InvalidCursorParam)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if _, err := parsePositiveInt(limitStr, &limit); err != nil || limit <= 0 {
|
if err := parsePositiveInt(limitStr, &limit); err != nil || limit <= 0 {
|
||||||
limit = defaultLimit
|
limit = defaultLimit
|
||||||
}
|
}
|
||||||
if limit > maxLimit {
|
if limit > maxLimit {
|
||||||
|
|||||||
@@ -49,15 +49,15 @@ func getUpgrader() *websocket.Upgrader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parsePositiveInt 解析非负整数字符串
|
// parsePositiveInt 解析非负整数字符串
|
||||||
func parsePositiveInt(s string, result *int) (bool, error) {
|
func parsePositiveInt(s string, result *int) error {
|
||||||
if s == "" {
|
if s == "" {
|
||||||
*result = 0
|
*result = 0
|
||||||
return true, nil
|
return nil
|
||||||
}
|
}
|
||||||
n, err := strconv.Atoi(s)
|
n, err := strconv.Atoi(s)
|
||||||
if err != nil || n < 0 {
|
if err != nil || n < 0 {
|
||||||
return false, err
|
return err
|
||||||
}
|
}
|
||||||
*result = n
|
*result = n
|
||||||
return true, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ func updateOptions(ctx context.Context, options []model.OpenFlareOption) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getStatus(ctx context.Context, baseAPIPath string) (*statusView, error) {
|
func getStatus(ctx context.Context, baseAPIPath string) *statusView {
|
||||||
authSources, err := publicAuthSources(ctx, baseAPIPath)
|
authSources, err := publicAuthSources(ctx, baseAPIPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
authSources = []publicAuthSourceView{}
|
authSources = []publicAuthSourceView{}
|
||||||
@@ -128,7 +128,7 @@ func getStatus(ctx context.Context, baseAPIPath string) (*statusView, error) {
|
|||||||
PasswordRegisterEnabled: passwordRegisterEnabled,
|
PasswordRegisterEnabled: passwordRegisterEnabled,
|
||||||
CapLoginEnabled: capLoginEnabled,
|
CapLoginEnabled: capLoginEnabled,
|
||||||
AuthSources: authSources,
|
AuthSources: authSources,
|
||||||
}, nil
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func publicAuthSources(ctx context.Context, baseAPIPath string) ([]publicAuthSourceView, error) {
|
func publicAuthSources(ctx context.Context, baseAPIPath string) ([]publicAuthSourceView, error) {
|
||||||
|
|||||||
@@ -22,10 +22,7 @@ import (
|
|||||||
// @Failure 500 {object} response.Any "内部错误"
|
// @Failure 500 {object} response.Any "内部错误"
|
||||||
// @Router /api/v1/d/status [get]
|
// @Router /api/v1/d/status [get]
|
||||||
func GetStatusHandler(c *gin.Context) {
|
func GetStatusHandler(c *gin.Context) {
|
||||||
view, err := getStatus(c.Request.Context(), "/api/v1/d")
|
view := getStatus(c.Request.Context(), "/api/v1/d")
|
||||||
if apiutil.AbortBadRequestOnError(c, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.JSON(http.StatusOK, response.OK(view))
|
c.JSON(http.StatusOK, response.OK(view))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,11 +90,11 @@ func rebindPagesRouteMaps(ctx context.Context, routes []map[string]json.RawMessa
|
|||||||
if route == nil {
|
if route == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
upstreamType, _ := rawJSONString(route["upstream_type"])
|
upstreamType := rawJSONString(route["upstream_type"])
|
||||||
if !strings.EqualFold(strings.TrimSpace(upstreamType), "pages") {
|
if !strings.EqualFold(strings.TrimSpace(upstreamType), "pages") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
siteName, _ := rawJSONString(route["site_name"])
|
siteName := rawJSONString(route["site_name"])
|
||||||
projectID, err := resolveProjectIDFromRouteMap(route)
|
projectID, err := resolveProjectIDFromRouteMap(route)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if siteName == "" {
|
if siteName == "" {
|
||||||
@@ -225,15 +225,15 @@ func buildLivePagesDeployment(
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func rawJSONString(raw json.RawMessage) (string, bool) {
|
func rawJSONString(raw json.RawMessage) string {
|
||||||
if !isPresentJSON(raw) {
|
if !isPresentJSON(raw) {
|
||||||
return "", false
|
return ""
|
||||||
}
|
}
|
||||||
var value string
|
var value string
|
||||||
if err := json.Unmarshal(raw, &value); err != nil {
|
if err := json.Unmarshal(raw, &value); err != nil {
|
||||||
return "", false
|
return ""
|
||||||
}
|
}
|
||||||
return value, true
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
func putJSON(route map[string]json.RawMessage, key string, value any) error {
|
func putJSON(route map[string]json.RawMessage, key string, value any) error {
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ func GetProxyRoute(ctx context.Context, id uint) (*View, error) {
|
|||||||
|
|
||||||
// CreateProxyRoute 创建代理规则。
|
// CreateProxyRoute 创建代理规则。
|
||||||
func CreateProxyRoute(ctx context.Context, input Input) (*View, error) {
|
func CreateProxyRoute(ctx context.Context, input Input) (*View, error) {
|
||||||
route, _, err := buildProxyRoute(ctx, nil, input)
|
route, err := buildProxyRoute(ctx, nil, input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -148,7 +148,7 @@ func UpdateProxyRoute(ctx context.Context, id uint, input Input) (*View, error)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
previousPagesProjectID := pagesProjectIDForRoute(route)
|
previousPagesProjectID := pagesProjectIDForRoute(route)
|
||||||
route, _, err = buildProxyRoute(ctx, route, input)
|
route, err = buildProxyRoute(ctx, route, input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -244,67 +244,67 @@ func DeleteProxyRoute(ctx context.Context, id uint) error {
|
|||||||
return repository.DeleteProxyRouteAndUnbind(ctx, id)
|
return repository.DeleteProxyRouteAndUnbind(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildProxyRoute(ctx context.Context, route *model.ProxyRoute, input Input) (*model.ProxyRoute, []model.ZoneDomain, error) {
|
func buildProxyRoute(ctx context.Context, route *model.ProxyRoute, input Input) (*model.ProxyRoute, error) {
|
||||||
domains, err := loadProxyRouteZoneDomains(ctx, input.ZoneDomainIDs)
|
domains, err := loadProxyRouteZoneDomains(ctx, input.ZoneDomainIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
siteName := strings.TrimSpace(input.SiteName)
|
siteName := strings.TrimSpace(input.SiteName)
|
||||||
|
|
||||||
upstreamType := normalizeUpstreamType(input.UpstreamType)
|
upstreamType := normalizeUpstreamType(input.UpstreamType)
|
||||||
_, originID, upstreams, err := resolveProxyRouteUpstreams(ctx, upstreamType, input)
|
_, originID, upstreams, err := resolveProxyRouteUpstreams(ctx, upstreamType, input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
originHost := strings.TrimSpace(input.OriginHost)
|
originHost := strings.TrimSpace(input.OriginHost)
|
||||||
cachePolicy := strings.TrimSpace(input.CachePolicy)
|
cachePolicy := strings.TrimSpace(input.CachePolicy)
|
||||||
cacheRules, err := normalizeCacheRules(input.CacheEnabled, cachePolicy, input.CacheRules)
|
cacheRules, err := normalizeCacheRules(input.CacheEnabled, cachePolicy, input.CacheRules)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
customHeaders, err := normalizeCustomHeaders(input.CustomHeaders)
|
customHeaders, err := normalizeCustomHeaders(input.CustomHeaders)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
limitConnPerServer, err := normalizeProxyRouteLimitConnValue(input.LimitConnPerServer, "limit_conn_per_server")
|
limitConnPerServer, err := normalizeProxyRouteLimitConnValue(input.LimitConnPerServer, "limit_conn_per_server")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
limitConnPerIP, err := normalizeProxyRouteLimitConnValue(input.LimitConnPerIP, "limit_conn_per_ip")
|
limitConnPerIP, err := normalizeProxyRouteLimitConnValue(input.LimitConnPerIP, "limit_conn_per_ip")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
limitRate, err := normalizeProxyRouteLimitRate(input.LimitRate)
|
limitRate, err := normalizeProxyRouteLimitRate(input.LimitRate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
limitReqPerIP, err := normalizeProxyRouteLimitReqPerIP(input.LimitReqPerIP)
|
limitReqPerIP, err := normalizeProxyRouteLimitReqPerIP(input.LimitReqPerIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := validateProxyRouteZoneDomainCertificates(ctx, domains, input.EnableHTTPS); err != nil {
|
if err := validateProxyRouteZoneDomainCertificates(ctx, domains, input.EnableHTTPS); err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
jsonFields, err := marshalProxyRouteJSONFields(upstreams, cacheRules, customHeaders)
|
jsonFields, err := marshalProxyRouteJSONFields(upstreams, cacheRules, customHeaders)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateProxyRouteSiteName(siteName); err != nil {
|
if err := validateProxyRouteSiteName(siteName); err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := validateProxyRouteSiteNameUniqueness(ctx, route, siteName); err != nil {
|
if err := validateProxyRouteSiteNameUniqueness(ctx, route, siteName); err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := validateOriginHost(originHost); err != nil {
|
if err := validateOriginHost(originHost); err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if input.RedirectHTTP && !input.EnableHTTPS {
|
if input.RedirectHTTP && !input.EnableHTTPS {
|
||||||
return nil, nil, errors.New(errProxyRouteRedirectHTTP)
|
return nil, errors.New(errProxyRouteRedirectHTTP)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := normalizeProxyRouteBasicAuth(&input); err != nil {
|
if err := normalizeProxyRouteBasicAuth(&input); err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if route == nil {
|
if route == nil {
|
||||||
@@ -326,9 +326,9 @@ func buildProxyRoute(ctx context.Context, route *model.ProxyRoute, input Input)
|
|||||||
upstreamType,
|
upstreamType,
|
||||||
)
|
)
|
||||||
if err := applyProxyRouteUpstreamType(ctx, route, upstreamType, input); err != nil {
|
if err := applyProxyRouteUpstreamType(ctx, route, upstreamType, input); err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return route, domains, nil
|
return route, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildProxyRouteViews(ctx context.Context, routes []*model.ProxyRoute) ([]*View, error) {
|
func buildProxyRouteViews(ctx context.Context, routes []*model.ProxyRoute) ([]*View, error) {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ type kumaConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// loadKumaConfig 从 SystemConfig 加载 UptimeKuma 配置
|
// loadKumaConfig 从 SystemConfig 加载 UptimeKuma 配置
|
||||||
func loadKumaConfig(ctx context.Context) (*kumaConfig, error) {
|
func loadKumaConfig(ctx context.Context) *kumaConfig {
|
||||||
url, _ := repository.GetSystemConfigByKey(ctx, model.ConfigKeyUptimeKumaURL)
|
url, _ := repository.GetSystemConfigByKey(ctx, model.ConfigKeyUptimeKumaURL)
|
||||||
username, _ := repository.GetSystemConfigByKey(ctx, model.ConfigKeyUptimeKumaUsername)
|
username, _ := repository.GetSystemConfigByKey(ctx, model.ConfigKeyUptimeKumaUsername)
|
||||||
password, _ := repository.GetSystemConfigByKey(ctx, model.ConfigKeyUptimeKumaPassword)
|
password, _ := repository.GetSystemConfigByKey(ctx, model.ConfigKeyUptimeKumaPassword)
|
||||||
@@ -68,7 +68,7 @@ func loadKumaConfig(ctx context.Context) (*kumaConfig, error) {
|
|||||||
Retry: retry,
|
Retry: retry,
|
||||||
RetryInterval: retryInterval,
|
RetryInterval: retryInterval,
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
}, nil
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SyncToUptimeKuma synchronizes enabled proxy routes to Uptime Kuma monitors.
|
// SyncToUptimeKuma synchronizes enabled proxy routes to Uptime Kuma monitors.
|
||||||
@@ -85,10 +85,7 @@ func SyncToUptimeKuma(ctx context.Context) error {
|
|||||||
defer isSyncing.Store(false)
|
defer isSyncing.Store(false)
|
||||||
|
|
||||||
// 加载配置
|
// 加载配置
|
||||||
config, err := loadKumaConfig(ctx)
|
config := loadKumaConfig(ctx)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证配置
|
// 验证配置
|
||||||
if err := validateKumaConfig(config); err != nil {
|
if err := validateKumaConfig(config); err != nil {
|
||||||
@@ -106,10 +103,7 @@ func SyncToUptimeKuma(ctx context.Context) error {
|
|||||||
return fmt.Errorf("failed to list local proxy routes: %w", err)
|
return fmt.Errorf("failed to list local proxy routes: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedRoutes, err := filterExpectedRoutes(allRoutes, config)
|
expectedRoutes := filterExpectedRoutes(allRoutes, config)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
client, err := connectAndLoginUptimeKuma(config.URL, config.Username, config.Password)
|
client, err := connectAndLoginUptimeKuma(config.URL, config.Username, config.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -129,7 +123,7 @@ func SyncToUptimeKuma(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterExpectedRoutes(allRoutes []*model.ProxyRoute, config *kumaConfig) ([]*model.ProxyRoute, error) {
|
func filterExpectedRoutes(allRoutes []*model.ProxyRoute, config *kumaConfig) []*model.ProxyRoute {
|
||||||
scope := config.MonitorScope
|
scope := config.MonitorScope
|
||||||
if scope == "selected" {
|
if scope == "selected" {
|
||||||
selectedList := strings.Split(config.SelectedSites, ",")
|
selectedList := strings.Split(config.SelectedSites, ",")
|
||||||
@@ -146,7 +140,7 @@ func filterExpectedRoutes(allRoutes []*model.ProxyRoute, config *kumaConfig) ([]
|
|||||||
expectedRoutes = append(expectedRoutes, route)
|
expectedRoutes = append(expectedRoutes, route)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return expectedRoutes, nil
|
return expectedRoutes
|
||||||
}
|
}
|
||||||
|
|
||||||
var expectedRoutes []*model.ProxyRoute
|
var expectedRoutes []*model.ProxyRoute
|
||||||
@@ -155,7 +149,7 @@ func filterExpectedRoutes(allRoutes []*model.ProxyRoute, config *kumaConfig) ([]
|
|||||||
expectedRoutes = append(expectedRoutes, route)
|
expectedRoutes = append(expectedRoutes, route)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return expectedRoutes, nil
|
return expectedRoutes
|
||||||
}
|
}
|
||||||
|
|
||||||
func ensureOpenFlareTag(client *SocketIOClient) (int, error) {
|
func ensureOpenFlareTag(client *SocketIOClient) (int, error) {
|
||||||
|
|||||||
+10
-9
@@ -52,20 +52,21 @@ func NewMemoryStore(cleanupInterval time.Duration) *MemoryStore {
|
|||||||
func (s *MemoryStore) Get(_ context.Context, key string) (string, bool, error) {
|
func (s *MemoryStore) Get(_ context.Context, key string) (string, bool, error) {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
return s.getLocked(key)
|
val, ok := s.getLocked(key)
|
||||||
|
return val, ok, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getLocked is the internal helper – caller must hold s.mu.
|
// getLocked is the internal helper – caller must hold s.mu.
|
||||||
func (s *MemoryStore) getLocked(key string) (string, bool, error) {
|
func (s *MemoryStore) getLocked(key string) (string, bool) {
|
||||||
item, found := s.items[key]
|
item, found := s.items[key]
|
||||||
if !found {
|
if !found {
|
||||||
return "", false, nil
|
return "", false
|
||||||
}
|
}
|
||||||
if time.Now().After(item.expiresAt) {
|
if time.Now().After(item.expiresAt) {
|
||||||
delete(s.items, key)
|
delete(s.items, key)
|
||||||
return "", false, nil
|
return "", false
|
||||||
}
|
}
|
||||||
return item.value, true, nil
|
return item.value, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set 向 MemoryStore 写入指定 key 的值
|
// Set 向 MemoryStore 写入指定 key 的值
|
||||||
@@ -93,7 +94,7 @@ func (s *MemoryStore) SetNX(_ context.Context, key string, val string, ttl time.
|
|||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
_, exists, _ := s.getLocked(key)
|
_, exists := s.getLocked(key)
|
||||||
if exists {
|
if exists {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
@@ -109,9 +110,9 @@ func (s *MemoryStore) GetAndDelete(_ context.Context, key string) (string, bool,
|
|||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
val, exists, err := s.getLocked(key)
|
val, exists := s.getLocked(key)
|
||||||
if err != nil || !exists {
|
if !exists {
|
||||||
return "", false, err
|
return "", false, nil
|
||||||
}
|
}
|
||||||
delete(s.items, key)
|
delete(s.items, key)
|
||||||
return val, true, nil
|
return val, true, nil
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ func RenderRouteConfig(doc Document, certificateFiles []SupportFile) (string, er
|
|||||||
displayName := resolveRouteSiteName(route)
|
displayName := resolveRouteSiteName(route)
|
||||||
cacheConfig := routeCacheConfig{Enabled: route.CacheEnabled, Policy: route.CachePolicy, Rules: route.CacheRules}
|
cacheConfig := routeCacheConfig{Enabled: route.CacheEnabled, Policy: route.CachePolicy, Rules: route.CacheRules}
|
||||||
limitConfig := mergeRouteLimitConfig(route, doc.OpenRestyConfig)
|
limitConfig := mergeRouteLimitConfig(route, doc.OpenRestyConfig)
|
||||||
powEnabled, _ := getPoWConfigForRoute(route.ID, doc.WAF)
|
powEnabled := getPoWConfigForRoute(route.ID, doc.WAF)
|
||||||
if normalizeRouteUpstreamType(route.UpstreamType) == routeUpstreamTypePages {
|
if normalizeRouteUpstreamType(route.UpstreamType) == routeUpstreamTypePages {
|
||||||
if err := renderPagesRoute(&builder, route, displayName, serverNames, certificates, limitConfig, powEnabled, doc.OpenRestyConfig); err != nil {
|
if err := renderPagesRoute(&builder, route, displayName, serverNames, certificates, limitConfig, powEnabled, doc.OpenRestyConfig); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -827,7 +827,7 @@ func validateCertificateCoverage(certPEM string, domains []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPoWConfigForRoute(routeID uint, snapshot WAFDocument) (bool, *PoWConfig) {
|
func getPoWConfigForRoute(routeID uint, snapshot WAFDocument) bool {
|
||||||
enabledGroups := make(map[uint]WAFRuleGroup, len(snapshot.RuleGroups))
|
enabledGroups := make(map[uint]WAFRuleGroup, len(snapshot.RuleGroups))
|
||||||
globalGroupIDs := make([]uint, 0)
|
globalGroupIDs := make([]uint, 0)
|
||||||
for _, group := range snapshot.RuleGroups {
|
for _, group := range snapshot.RuleGroups {
|
||||||
@@ -857,10 +857,10 @@ func getPoWConfigForRoute(routeID uint, snapshot WAFDocument) (bool, *PoWConfig)
|
|||||||
for _, groupID := range activeGroupIDs {
|
for _, groupID := range activeGroupIDs {
|
||||||
group := enabledGroups[groupID]
|
group := enabledGroups[groupID]
|
||||||
if graphContainsNodeType(group.Graph, "pow") {
|
if graphContainsNodeType(group.Graph, "pow") {
|
||||||
return true, nil
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false, nil
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func graphContainsNodeType(graph WAFRuleGraph, nodeType string) bool {
|
func graphContainsNodeType(graph WAFRuleGraph, nodeType string) bool {
|
||||||
|
|||||||
@@ -108,13 +108,10 @@ func TestGetPoWConfigForRouteUsesGlobalGroupWithoutExplicitBinding(t *testing.T)
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
enabled, config := getPoWConfigForRoute(42, snapshot)
|
enabled := getPoWConfigForRoute(42, snapshot)
|
||||||
if !enabled {
|
if !enabled {
|
||||||
t.Fatal("expected pow to be enabled via global rule group")
|
t.Fatal("expected pow to be enabled via global rule group")
|
||||||
}
|
}
|
||||||
if config != nil {
|
|
||||||
t.Fatalf("expected node config to stay in runtime graph, got legacy config %#v", config)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRenderRouteConfigEnablesPoWLocationsFromRuntimeGraph(t *testing.T) {
|
func TestRenderRouteConfigEnablesPoWLocationsFromRuntimeGraph(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user