Skip to content

Conversation

@Cryolitia
Copy link

@Cryolitia Cryolitia commented Oct 21, 2025

内核工程师没有使用任何AI一晚上速成go的故事

将 DN42 模式的默认行为更改为联网下载预构建的数据库,两个库都只有200kb左右所以直接把整个下载下来应该是比较合适的。

暂时用了我的GitHub Pages,或许可以把这个预构建仓库transfer到组织里然后用你们的GitHub Pages或可能存在的什么分发网络,建议不要直接用我的地址合并

图片

Summary by CodeRabbit

发布说明

  • 新功能

    • 添加从远程源加载地理位置反馈和PTR数据的支持
  • 改进

    • 默认配置源已更新为远程URL,而非本地文件路径
    • 简化缺失配置文件时的处理流程,不再自动生成默认配置文件
  • 配置变更

    • 本地配置文件中移除了地理位置反馈和PTR数据路径条目

@Cryolitia Cryolitia marked this pull request as draft October 21, 2025 19:46
@coderabbitai
Copy link

coderabbitai bot commented Oct 21, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

概览

本次更改将配置默认值从本地文件路径迁移至远程 URL,为地理位置反馈和 PTR 数据加载引入 HTTP 支持,并在缺失配置时改变错误处理行为,不再自动写入默认配置文件。

变更详情

内聚组 / 文件 变更摘要
配置默认值迁移
config/viper.go, nt_config.yaml
将 ptrPath 和 geoFeedPath 的默认值从本地路径("./ptr.csv"、"./geofeed.csv")改为远程 URL("https://cryolitia.github.io/Next-Trace-DN42-Feeder/ptr.csv" 及对应的 geofeed.csv);移除配置缺失时的默认文件写入逻辑,改为仅打印默认配置将被加载的提示信息
数据加载现代化
dn42/geofeed.go
引入使用 sync.OnceValues 的 getGeoFeed 缓存机制;支持从 HTTP(S) URL 或本地文件加载 CSV 数据;HTTP 请求超时设置为 10 秒;ReadGeoFeed 新增对多列格式的 CIDR 解析和按掩码长度排序的处理逻辑
指针记录加载优化
dn42/ptr.go
引入使用 sync.OnceValues 的 getPtr 缓存机制;支持从 HTTP(S) URL 或本地文件加载 PTR 数据;HTTP 请求超时设置为 10 秒;保持现有 FindPtrRecord 的城市名称和 IATA 码匹配逻辑,但操作于缓存的行数据上

时序图

sequenceDiagram
    participant User
    participant Config as config/viper.go
    participant GeoFeed as dn42/geofeed.go
    participant PTR as dn42/ptr.go
    participant Remote as Remote URL
    participant Local as Local File

    User->>Config: 初始化配置
    
    alt 配置文件存在
        Config->>Config: 读取 nt_config.yaml
    else 配置文件缺失
        Config->>Config: 使用远程 URL 默认值
        Note over Config: 仅打印提示,不写入文件
    end

    User->>GeoFeed: 读取地理数据
    
    alt 首次调用 getGeoFeed
        GeoFeed->>GeoFeed: 检查 geoFeedPath
        
        alt URL 路径
            GeoFeed->>Remote: HTTP GET (10s 超时)
            Remote-->>GeoFeed: CSV 响应
        else 本地路径
            GeoFeed->>Local: 读取本地文件
            Local-->>GeoFeed: CSV 数据
        end
        
        GeoFeed->>GeoFeed: 缓存结果 (sync.OnceValues)
    else 后续调用
        GeoFeed->>GeoFeed: 返回缓存数据
    end
    
    GeoFeed->>GeoFeed: 解析 CIDR,排序掩码
    GeoFeed-->>User: GeoFeedRow 切片

    User->>PTR: 查询 PTR 记录
    
    alt 首次调用 getPtr
        PTR->>PTR: 检查 ptrPath
        
        alt URL 路径
            PTR->>Remote: HTTP GET (10s 超时)
            Remote-->>PTR: CSV 响应
        else 本地路径
            PTR->>Local: 读取本地文件
            Local-->>PTR: CSV 数据
        end
        
        PTR->>PTR: 缓存结果 (sync.OnceValues)
    else 后续调用
        PTR->>PTR: 返回缓存数据
    end
    
    PTR->>PTR: 匹配城市名称或 IATA 码
    PTR-->>User: PTR 记录
Loading

代码审查工作量估计

🎯 4 (复杂) | ⏱️ ~45 分钟

本次变更涉及多个文件的异构逻辑修改,包括:HTTP 网络请求处理、超时机制、sync.OnceValues 缓存模式的引入、CIDR 解析和排序逻辑、多层次的错误处理以及控制流的调整。虽然各个修改点相对独立,但每个模块都需要单独的逻辑推导和测试路径分析。

🐰 远方的 URL 来敲门,
十秒超时网络纵横,
缓存魔法 OnceValues 施,
本地旧路终告别,
云端之数从此驶!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed 该拉取请求的标题"feat: fetch built DN42 data online by default"准确地反映了变更集的主要目标。变更涉及多个文件(config/viper.go、dn42/geofeed.go、dn42/ptr.go、nt_config.yaml),这些文件的修改共同实现了一个核心目标:将 DN42 模式的默认行为从加载本地文件改为从在线获取预构建的数据库。标题既简洁明确,又充分传达了这一主要改变的含义。
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Cryolitia
Copy link
Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Oct 21, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
dn42/ptr.go (2)

63-105: 避免 CSV 越界与保持返回字段原貌;PTR 匹配需更稳健

  • 多处直接访问 row[3]/row[1] 等,遇到短行会越界 panic。
  • IATA 为匹配而转小写,但返回值应保留数据源原貌。
  • 结合远端数据源,不可信输入需更稳健处理。
 func FindPtrRecord(ptr string) (PtrRow, error) {
 	rows, err := getPtr()
 	if err != nil {
 		return PtrRow{}, err
 	}
 
 	// 转小写
 	ptr = strings.ToLower(ptr)
 	// 先查城市名
 	for _, row := range rows {
-		city := row[3]
+		if len(row) < 4 {
+			continue
+		}
+		city := row[3]
 		if city == "" {
 			continue
 		}
 		city = strings.ReplaceAll(city, " ", "")
 		city = strings.ToLower(city)
 
 		if matchesPattern(city, ptr) {
 			return PtrRow{
-				LtdCode: row[1],
-				Region:  row[2],
-				City:    row[3],
+				LtdCode: row[1],
+				Region:  row[2],
+				City:    row[3],
 			}, nil
 		}
 	}
 	// 查 IATA Code
 	for _, row := range rows {
-		iata := row[0]
+		if len(row) < 4 {
+			continue
+		}
+		iata := row[0]
 		if iata == "" {
 			continue
 		}
-		iata = strings.ToLower(iata)
-		if matchesPattern(iata, ptr) {
+		if matchesPattern(strings.ToLower(iata), ptr) {
 			return PtrRow{
-				IATACode: iata,
-				LtdCode:  row[1],
-				Region:   row[2],
-				City:     row[3],
+				IATACode: iata,     // 保留原始大小写
+				LtdCode:  row[1],
+				Region:   row[2],
+				City:     row[3],
 			}, nil
 		}
 	}
 
 	return PtrRow{}, errors.New("ptr not found")
 }

25-35: 正则拼接未转义,城市名等含特殊字符时匹配不可靠

建议使用 regexp.QuoteMeta 转义 prefix,避免正则注入/误匹配。

 func matchesPattern(prefix string, s string) bool {
-	pattern := fmt.Sprintf(`^(.*[-.\d]|^)%s[-.\d].*$`, prefix)
+	safe := regexp.QuoteMeta(prefix)
+	pattern := fmt.Sprintf(`^(.*[-.\d]|^)%s[-.\d].*$`, safe)
 	r, err := regexp.Compile(pattern)
 	if err != nil {
 		fmt.Println("Invalid regular expression:", err)
 		return false
 	}
 	return r.MatchString(s)
 }
dn42/geofeed.go (2)

66-108: CSV 行访问未做长度校验,存在越界风险;读取失败不应 panic

  • 直接访问 row[1..5],短行会 panic。
  • GetGeoFeed 读取失败时 panic,不利于作为库函数复用。
 func GetGeoFeed(ip string) (GeoFeedRow, bool) {
 	rows, err := ReadGeoFeed()
 	if err != nil {
-		// 处理错误
-		panic(err)
+		log.Printf("读取 GeoFeed 失败:%v", err)
+		return GeoFeedRow{}, false
 	}
@@
 func ReadGeoFeed() ([]GeoFeedRow, error) {
 	rows, err := getGeoFeed()
 	if err != nil {
 		return nil, err
 	}
 	// 将 CSV 中的每一行转换为 GeoFeedRow 类型,并保存到 rowsSlice 中
 	var rowsSlice []GeoFeedRow
 	for _, row := range rows {
-		cidr := row[0] // 假设第一列是 CIDR 字段
+		if len(row) < 1 {
+			continue
+		}
+		cidr := row[0] // 第一列是 CIDR
 		_, ipnet, err := net.ParseCIDR(cidr)
 		if err != nil {
 			// 如果解析 CIDR 失败,跳过这一行
 			continue
 		}
-		if len(row) == 4 {
+		switch {
+		case len(row) >= 6:
+			rowsSlice = append(rowsSlice, GeoFeedRow{
+				IPNet:   ipnet,
+				CIDR:    cidr,
+				LtdCode: row[1],
+				ISO3166: row[2],
+				City:    row[3],
+				ASN:     row[4],
+				IPWhois: row[5],
+			})
+		case len(row) >= 4:
 			rowsSlice = append(rowsSlice, GeoFeedRow{
 				IPNet:   ipnet,
 				CIDR:    cidr,
 				LtdCode: row[1],
 				ISO3166: row[2],
 				City:    row[3],
 			})
-		} else {
-			rowsSlice = append(rowsSlice, GeoFeedRow{
-				IPNet:   ipnet,
-				CIDR:    cidr,
-				LtdCode: row[1],
-				ISO3166: row[2],
-				City:    row[3],
-				ASN:     row[4],
-				IPWhois: row[5],
-			})
+		default:
+			continue
 		}
 	}

103-105: 排序依据不正确,可能导致未按最长前缀匹配(LPM)返回

使用 Mask.String() 的字典序无法保证前缀长度从大到小;应按掩码比特位数排序,优先匹配更具体的网段。

-	// 根据 CIDR 范围从小到大排序,方便后面查找
-	sort.Slice(rowsSlice, func(i, j int) bool {
-		return rowsSlice[i].IPNet.Mask.String() > rowsSlice[j].IPNet.Mask.String()
-	})
+	// 按前缀长度从大到小排序(LPM 优先)
+	sort.Slice(rowsSlice, func(i, j int) bool {
+		oi, _ := rowsSlice[i].IPNet.Mask.Size()
+		oj, _ := rowsSlice[j].IPNet.Mask.Size()
+		if oi != oj {
+			return oi > oj
+		}
+		// 次级稳定排序
+		return rowsSlice[i].CIDR < rowsSlice[j].CIDR
+	})
🧹 Nitpick comments (1)
config/viper.go (1)

31-33: 统一日志体系并明确动作建议

使用 fmt.Println 可能与全局日志风格不一致;建议使用统一 logger,并在找不到配置时同时输出配置查找路径与下一步建议(例如如何创建/覆盖)。

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f2bd1a and b66f9f5.

📒 Files selected for processing (4)
  • config/viper.go (1 hunks)
  • dn42/geofeed.go (2 hunks)
  • dn42/ptr.go (2 hunks)
  • nt_config.yaml (0 hunks)
💤 Files with no reviewable changes (1)
  • nt_config.yaml

Comment on lines +26 to +27
viper.SetDefault("ptrPath", "https://cryolitia.github.io/Next-Trace-DN42-Feeder/ptr.csv")
viper.SetDefault("geoFeedPath", "https://cryolitia.github.io/Next-Trace-DN42-Feeder/geofeed.csv")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

默认数据源指向个人 GitHub Pages,不宜合并;请改为组织托管并提供回退

  • PR 说明已提及“不要使用作者的 GitHub Pages 地址合并”。当前默认值与此相冲突。
  • 运行时强依赖外网,离线/限网环境将直接失败;建议提供本地回退路径或内置数据。

建议:

  • 迁移到组织域(如 org 的 GitHub Pages 或 Release 资产/CDN),并进行版本化与校验(ETag/If-None-Match 或校验和)。
  • 支持环境变量/配置覆盖基础 URL(如 DN42_FEED_BASE_URL),默认使用组织源。
  • 实现“远端失败→本地路径回退”的双路策略,提升可用性与隐私。
  • 为后续灰度与回滚,建议在日志中标记所用数据源与版本。

Comment on lines 39 to 64
var getGeoFeed = sync.OnceValues(func() ([][]string, error) {
path := viper.Get("geoFeedPath").(string)
f, err := os.Open(path)
if err != nil {
return nil, err
var r *csv.Reader
log.Println(path)
if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") {
client := &http.Client{
// 10 秒超时
Timeout: time.Duration(10) * time.Second,
}
req, _ := http.NewRequest("GET", path, nil)
content, err := client.Do(req)
if err != nil {
log.Println("DN42数据请求超时,请更换其他数据源或使用本地数据")
return nil, err
}
r = csv.NewReader(content.Body)
} else {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
r = csv.NewReader(f)
}
defer f.Close()
return r.ReadAll()
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

HTTP 获取存在与 PTR 相同的问题;请修复错误处理、关闭 Body、校验状态码并限制读取

  • 忽略 NewRequest 错误、未 Close()、未检查 StatusCode、无读取上限、viper.Get(...).(string) 有 panic 风险。
  • log.Println(path) 可能噪声过大;建议降级为 debug 或移除。
@@
-import (
+import (
 	"encoding/csv"
-	"log"
+	"io"
+	"log"
 	"net"
 	"net/http"
 	"os"
 	"sort"
 	"strings"
 	"sync"
 	"time"
@@
-var getGeoFeed = sync.OnceValues(func() ([][]string, error) {
-	path := viper.Get("geoFeedPath").(string)
+var getGeoFeed = sync.OnceValues(func() ([][]string, error) {
+	path := viper.GetString("geoFeedPath")
 	var r *csv.Reader
-	log.Println(path)
 	if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") {
 		client := &http.Client{
 			// 10 秒超时
 			Timeout: time.Duration(10) * time.Second,
 		}
-		req, _ := http.NewRequest("GET", path, nil)
-		content, err := client.Do(req)
-		if err != nil {
-			log.Println("DN42数据请求超时,请更换其他数据源或使用本地数据")
-			return nil, err
-		}
-		r = csv.NewReader(content.Body)
+		req, err := http.NewRequest("GET", path, nil)
+		if err != nil {
+			return nil, err
+		}
+		resp, err := client.Do(req)
+		if err != nil {
+			log.Printf("DN42数据请求失败:%v", err)
+			return nil, err
+		}
+		defer resp.Body.Close()
+		if resp.StatusCode != http.StatusOK {
+			return nil, fmt.Errorf("DN42数据请求失败:%s", resp.Status)
+		}
+		r = csv.NewReader(io.LimitReader(resp.Body, 1<<20))
 	} else {
 		f, err := os.Open(path)
 		if err != nil {
 			return nil, err
 		}
 		defer f.Close()
 		r = csv.NewReader(f)
 	}
 	return r.ReadAll()
 })

同 PTR:sync.OnceValues 缓存首次错误,建议后续引入 TTL/重试策略以提升健壮性。

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In dn42/geofeed.go around lines 39 to 64, the HTTP path handling ignores
NewRequest errors, doesn't close the response body, fails to check HTTP status
codes, reads without size limits, and unsafely type-asserts viper.Get to string
(and prints the path noisily); fix by validating viper.Get("geoFeedPath") is a
non-empty string before use, handle and return any error from http.NewRequest,
use http.Client.Do and immediately defer resp.Body.Close() after a nil error,
check resp.StatusCode and treat non-2xx as an error, wrap the body reader with a
size limiter (e.g., io.LimitReader or io.ReadAll with an imposed max) before
creating csv.NewReader, and remove or lower log.Println(path) to debug level;
also consider not caching first error forever by adding TTL/retry logic to the
sync.OnceValues usage.

Comment on lines 37 to 62
var getPtr = sync.OnceValues(func() ([][]string, error) {
path := viper.Get("ptrPath").(string)
f, err := os.Open(path)
if err != nil {
return PtrRow{}, err
var r *csv.Reader
if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") {
client := &http.Client{
// 10 秒超时
Timeout: time.Duration(10) * time.Second,
}
req, _ := http.NewRequest("GET", path, nil)
content, err := client.Do(req)
if err != nil {
log.Println("DN42数据请求超时,请更换其他数据源或使用本地数据")
return nil, err
}
r = csv.NewReader(content.Body)
} else {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
r = csv.NewReader(f)
}
defer f.Close()
return r.ReadAll()
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

HTTP 获取存在资源泄漏与潜在 panic;建议完善错误处理与健壮性

  • 忽略 http.NewRequest 的错误,req 可能为 nil → 触发 panic。
  • 未检查 StatusCode
  • Close() 响应体 → 资源泄漏。
  • 仅打印“超时”,掩盖其他错误类型。
  • 直接断言 viper.Get(...).(string) 有 panic 风险;建议用 viper.GetString.
  • 建议对远端响应加大小上限,避免异常大响应耗内存。

可按此最小变更修复:

@@
-import (
+import (
 	"encoding/csv"
 	"errors"
 	"fmt"
-	"log"
+	"io"
+	"log"
 	"net/http"
 	"os"
 	"regexp"
 	"strings"
 	"sync"
 	"time"
@@
-var getPtr = sync.OnceValues(func() ([][]string, error) {
-	path := viper.Get("ptrPath").(string)
+var getPtr = sync.OnceValues(func() ([][]string, error) {
+	path := viper.GetString("ptrPath")
 	var r *csv.Reader
 	if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") {
 		client := &http.Client{
 			// 10 秒超时
 			Timeout: time.Duration(10) * time.Second,
 		}
-		req, _ := http.NewRequest("GET", path, nil)
-		content, err := client.Do(req)
-		if err != nil {
-			log.Println("DN42数据请求超时,请更换其他数据源或使用本地数据")
-			return nil, err
-		}
-		r = csv.NewReader(content.Body)
+		req, err := http.NewRequest("GET", path, nil)
+		if err != nil {
+			return nil, err
+		}
+		resp, err := client.Do(req)
+		if err != nil {
+			log.Printf("DN42数据请求失败:%v", err)
+			return nil, err
+		}
+		defer resp.Body.Close()
+		if resp.StatusCode != http.StatusOK {
+			return nil, fmt.Errorf("DN42数据请求失败:%s", resp.Status)
+		}
+		// 限制读取大小(1 MiB)
+		r = csv.NewReader(io.LimitReader(resp.Body, 1<<20))
 	} else {
 		f, err := os.Open(path)
 		if err != nil {
 			return nil, err
 		}
 		defer f.Close()
 		r = csv.NewReader(f)
 	}
 	return r.ReadAll()
 })

另外:sync.OnceValues 会把“失败结果”也永久缓存,首次网络抖动后将一直返回错误直至进程重启。建议改为带重试/TTL 的可刷新缓存或 singleflight+TTL(可后续提交实现)。


🏁 Script executed:

#!/bin/bash
# 核查 Go 版本是否兼容 sync.OnceValues(Go 1.22+)
rg -n '^(go|toolchain)\s+go1\.(\d+)' go.mod
echo '查找 OnceValues 用法:'
rg -n 'sync\.OnceValues' -g '!**/vendor/**'
# 粗查是否存在未 Close 的 Body 用法
echo '可能未关闭 Body 的位置:'
rg -n -C1 'csv\.NewReader\(.+\.Body\)' -g '!**/vendor/**'

Length of output: 388


🏁 Script executed:

#!/bin/bash
cat go.mod | head -5
echo "---"
echo "dn42/ptr.go lines 30-70:"
sed -n '30,70p' dn42/ptr.go
echo "---"
echo "dn42/geofeed.go lines 30-70:"
sed -n '30,70p' dn42/geofeed.go

Length of output: 1849


🏁 Script executed:

#!/bin/bash
echo "dn42/ptr.go lines 61-100 (FindPtrRecord):"
sed -n '61,100p' dn42/ptr.go
echo "---"
echo "dn42/ptr.go lines 100-130 (rest):"
sed -n '100,130p' dn42/ptr.go

Length of output: 881


HTTP 获取存在资源泄漏与多个潜在 panic;必须完善错误处理与数据校验

验证结果确认以下问题均存在:

  • 忽略 http.NewRequest 的错误,req 可能为 nil → client.Do(req) 触发 panic
  • 未检查 StatusCode
  • Close() 响应体 → 资源泄漏
  • 直接断言 viper.Get(...).(string) 有 panic 风险;建议用 viper.GetString
  • 访问 row[0]row[1]row[2]row[3] 无边界检查 → 若 CSV 行不足 4 列则 index out of range panic
  • 返回结果中 IATA 被转小写(第 95 行),改变了原始数据
  • sync.OnceValues 会把失败结果也永久缓存,首次网络抖动后将一直返回错误直至进程重启

建议修改(同时适用于 dn42/geofeed.go 中的相同模式):

@@
+import (
 	"encoding/csv"
 	"errors"
 	"fmt"
+	"io"
 	"log"
 	"net/http"
 	"os"
 	"regexp"
 	"strings"
 	"sync"
 	"time"
@@
 var getPtr = sync.OnceValues(func() ([][]string, error) {
-	path := viper.Get("ptrPath").(string)
+	path := viper.GetString("ptrPath")
 	var r *csv.Reader
 	if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") {
 		client := &http.Client{
 			Timeout: time.Duration(10) * time.Second,
 		}
-		req, _ := http.NewRequest("GET", path, nil)
-		content, err := client.Do(req)
+		req, err := http.NewRequest("GET", path, nil)
+		if err != nil {
+			return nil, err
+		}
+		resp, err := client.Do(req)
 		if err != nil {
-			log.Println("DN42数据请求超时,请更换其他数据源或使用本地数据")
+			log.Printf("DN42数据请求失败:%v", err)
 			return nil, err
 		}
-		r = csv.NewReader(content.Body)
+		defer resp.Body.Close()
+		if resp.StatusCode != http.StatusOK {
+			return nil, fmt.Errorf("DN42数据请求失败:%s", resp.Status)
+		}
+		r = csv.NewReader(io.LimitReader(resp.Body, 1<<20))
 	} else {
 		f, err := os.Open(path)
 		if err != nil {
 			return nil, err
 		}
 		defer f.Close()
 		r = csv.NewReader(f)
 	}
 	return r.ReadAll()
 })
@@
-	// 查 IATA Code
 	for _, row := range rows {
+		if len(row) < 4 {
+			continue
+		}
 		iata := row[0]
 		if iata == "" {
 			continue
 		}
-		iata = strings.ToLower(iata)
 		if matchesPattern(iata, ptr) {
 			return PtrRow{
-				IATACode: iata,
+				IATACode: row[0],
 				LtdCode:  row[1],
 				Region:   row[2],
 				City:     row[3],
 			}, nil
 		}
 	}

另外在 city 循环处也应加边界检查(第 73 行之后):

 	for _, row := range rows {
+		if len(row) < 4 {
+			continue
+		}
 		city := row[3]

Committable suggestion skipped: line range outside the PR's diff.

@tsosunchia
Copy link
Member

https://cryolitia.github.io/Next-Trace-DN42-Feeder/ptr.csv
https://cryolitia.github.io/Next-Trace-DN42-Feeder/geofeed.csv

我不懂DN42,这两个文件的出处在哪里,适用于所有人吗?

@Cryolitia
Copy link
Author

https://cryolitia.github.io/Next-Trace-DN42-Feeder/ptr.csv https://cryolitia.github.io/Next-Trace-DN42-Feeder/geofeed.csv

我不懂DN42,这两个文件的出处在哪里,适用于所有人吗?

这两个文件根据 https://github.com/Cryolitia/Next-Trace-DN42-Feeder 中的规则每日构建

其中 geofeed.csv 来自DN42 registry,而DN42 registry又导入了NeoNetwork。用这个构建出来的数据对这两个网络的玩家都是开箱即用的。ptr.csv 使用了三个不同来源的IATA数据库,构建了世界上绝大部分机场(现实意义上的机场)和他们所在的城市国家的结果。

对于极个别不适用这些预构建数据的人,他们只需要自己写个配置文件指向自己的数据库就可以了,这只是增加了默认选项,并没有削弱原本的能力。

@tsosunchia
Copy link
Member

tsosunchia commented Oct 23, 2025

@tsosunchia
Copy link
Member

需要确认一下

https://git.lantian.pub/backup/dn42-registry.git
https://github.com/lxndrblz/Airports

出处是?维护者?是否能保证安全?

@Cryolitia
Copy link
Author

需要确认一下

https://git.lantian.pub/backup/dn42-registry.git https://github.com/lxndrblz/Airports

出处是?维护者?是否能保证安全?

对于IATA数据库,如果你们拉黑了ip2location的话还有其他两个公开数据集,这几个都只是我在网上搜索得到的,都有与NextTrace兼容的开源协议,而且在积极维护。

对于DN42注册表,官方注册表无法匿名访问,我找的是蓝天的镜像,蓝天是DN42社区比较有知名度的成员和比较大的网络节点,应当是可以信任的。

不确定您指的保证安全是什么意思,csv怎么看也不像是能注入木马病毒的东西。

@tsosunchia
Copy link
Member

还在沟通专业人士来review这份PR,因为我实在不懂DN42,可能需要再等等了

@tsosunchia tsosunchia closed this Oct 29, 2025
@Cryolitia
Copy link
Author

为什么关闭了这个PR

@tsosunchia
Copy link
Member

暂无合适的人员review,当有的时候会重启merge进程

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants