diff --git a/.trae/documents/DNS服务器性能优化方案.md b/.trae/documents/DNS服务器性能优化方案.md
new file mode 100644
index 0000000..327667f
--- /dev/null
+++ b/.trae/documents/DNS服务器性能优化方案.md
@@ -0,0 +1,79 @@
+## DNS服务器性能优化方案
+
+### 问题分析
+
+1. **并行查询模式**:当前配置使用`parallel`模式,会等待所有上游服务器响应后才返回,受最慢服务器影响
+2. **DNSSEC验证开销**:启用了DNSSEC验证,增加了额外的计算和网络请求
+3. **过多上游服务器**:DNSSEC上游服务器多达5个,响应时间差异大
+4. **调试级别日志**:`debug`级别日志记录大量信息,占用CPU和I/O资源
+5. **缓存TTL过短**:10秒的缓存TTL导致频繁向上游请求
+6. **黑名单规则过多**:14个启用的黑名单,每次请求都需要检查
+
+### 优化方案
+
+#### 1. 修改查询模式为快速返回
+
+* 将`queryMode`从`parallel`改为`fastest-ip`或优化默认模式
+
+* 快速返回模式会返回第一个有效响应,而不是等待所有响应
+
+#### 2. 优化DNSSEC配置
+
+* 减少DNSSEC上游服务器数量,只保留2-3个可靠的
+
+* 对国内域名禁用DNSSEC验证(已配置部分,可扩展)
+
+#### 3. 调整缓存策略
+
+* 增加`cacheTTL`到60秒或更高,减少上游请求频率
+
+* 优化缓存实现,减少锁竞争
+
+#### 4. 降低日志级别
+
+* 将日志级别从`debug`改为`info`或`warn`,减少日志写入开销
+
+#### 5. 优化黑名单处理
+
+* 合并重复的黑名单规则
+
+* 考虑使用更高效的域名匹配算法
+
+#### 6. 代码优化
+
+* 减少DNSSEC验证的重复调用
+
+* 优化响应合并逻辑,避免不必要的计算
+
+* 调整超时设置,避免过长等待
+
+### 具体修改点
+
+1. **config.json**:
+
+ * 修改`queryMode`为`fastest-ip`
+
+ * 减少`dnssecUpstreamDNS`数量
+
+ * 增加`cacheTTL`到60
+
+ * 将日志级别改为`info`
+
+2. **dns/server.go**:
+
+ * 优化`forwardDNSRequestWithCache`函数,减少DNSSEC重复验证
+
+ * 优化响应合并逻辑,避免不必要的计算
+
+ * 调整并行模式的超时处理
+
+### 预期效果
+
+* 减少响应时间,从当前的秒级降低到毫秒级
+
+* 减少CPU和I/O资源占用
+
+* 提高并发处理能力
+
+* 保持DNS解析的准确性和可靠性
+
diff --git a/.trae/documents/plan_20251216_041731.md b/.trae/documents/plan_20251216_041731.md
index d739a8e..21bb0f8 100644
--- a/.trae/documents/plan_20251216_041731.md
+++ b/.trae/documents/plan_20251216_041731.md
@@ -1,35 +1,49 @@
# DNSSEC状态显示问题修复计划
## 问题分析
+
用户报告已在配置中启用DNSSEC(`enableDNSSEC: true`),但界面显示DNSSEC为禁用状态,且使用率为0%。经过代码检查,发现问题出在`GetStats`函数中,该函数返回的`Stats`结构体缺少DNSSEC相关字段,导致前端无法获取正确的DNSSEC状态和统计信息。
## 修复方案
### 1. 修复`GetStats`函数
+
**修改文件:** `dns/server.go`
**修改函数:** `GetStats`
**问题:** 当前`GetStats`函数返回的`Stats`结构体缺少DNSSEC相关字段,包括:
-- `DNSSECEnabled`
-- `DNSSECQueries`
-- `DNSSECSuccess`
-- `DNSSECFailed`
+
+* `DNSSECEnabled`
+
+* `DNSSECQueries`
+
+* `DNSSECSuccess`
+
+* `DNSSECFailed`
**解决方案:** 在`GetStats`函数返回的`Stats`结构体中添加所有DNSSEC相关字段,确保前端能获取到正确的DNSSEC状态和统计数据。
## 具体实现步骤
1. **修改`GetStats`函数**:
- - 在返回的`Stats`结构体中添加`DNSSECEnabled`字段
- - 添加`DNSSECQueries`字段
- - 添加`DNSSECSuccess`字段
- - 添加`DNSSECFailed`字段
+
+ * 在返回的`Stats`结构体中添加`DNSSECEnabled`字段
+
+ * 添加`DNSSECQueries`字段
+
+ * 添加`DNSSECSuccess`字段
+
+ * 添加`DNSSECFailed`字段
2. **测试修复效果**:
- - 重新编译DNS服务器
- - 启动服务器
- - 使用API查询统计信息,确认DNSSEC状态和统计数据正确返回
- - 检查前端界面是否显示正确的DNSSEC状态
+
+ * 重新编译DNS服务器
+
+ * 启动服务器
+
+ * 使用API查询统计信息,确认DNSSEC状态和统计数据正确返回
+
+ * 检查前端界面是否显示正确的DNSSEC状态
## 预期效果
@@ -40,4 +54,5 @@
## 代码修改范围
-- `dns/server.go`:修复`GetStats`函数,添加缺失的DNSSEC字段
\ No newline at end of file
+* `dns/server.go`:修复`GetStats`函数,添加缺失的DNSSEC字段
+
diff --git a/.trae/documents/plan_20251225_123636.md b/.trae/documents/plan_20251225_123636.md
new file mode 100644
index 0000000..d32a297
--- /dev/null
+++ b/.trae/documents/plan_20251225_123636.md
@@ -0,0 +1,52 @@
+1. **修改QueryLog结构体**:
+
+ * 在`dns/server.go`中的`QueryLog`结构体添加`ResponseCode`字段
+
+2. **修改addQueryLog函数**:
+
+ * 在`dns/server.go`中的`addQueryLog`函数添加`responseCode`参数
+
+ * 将响应代码记录到QueryLog结构体中
+
+3. **修改DNS请求处理逻辑**:
+
+ * 在`handleDNSRequest`函数中,获取实际的响应代码
+
+ * 将响应代码传递给`addQueryLog`函数
+
+4. **修改前端模板**:
+
+ * 在`static/js/logs.js`中,将响应代码的硬编码值"无"替换为从日志数据中获取的实际响应代码
+
+ * 添加响应代码映射,将数字响应代码转换为可读的字符串
+
+5. **编译和测试**:
+
+ * 重新编译项目
+
+ * 测试DNS查询详情中响应代码是否正确显示
+
+**DNS响应代码映射**:
+
+* 0: NOERROR
+
+* 1: FORMERR
+
+* 2: SERVFAIL
+
+* 3: NXDOMAIN
+
+* 4: NOTIMP
+
+* 5: REFUSED
+
+* 6: YXDOMAIN
+
+* 7: YXRRSET
+
+* 8: NXRRSET
+
+* 9: NOTAUTH
+
+* 10: NOTZONE
+
diff --git a/.trae/documents/plan_20251225_125022.md b/.trae/documents/plan_20251225_125022.md
new file mode 100644
index 0000000..89e0aac
--- /dev/null
+++ b/.trae/documents/plan_20251225_125022.md
@@ -0,0 +1,42 @@
+1. **调整DNS超时时间**:
+
+ * 将配置文件中的`timeout`值从5毫秒增加到5000毫秒(5秒)
+
+ * 5毫秒的超时时间对于DNS查询来说太短,导致大部分查询都超时失败
+
+2. **优化查询模式**:
+
+ * 将查询模式从`parallel`(并行)改为`loadbalance`(负载均衡)
+
+ * 并行模式在短超时时间下会导致大量超时,负载均衡模式更可靠
+
+3. **检查上游DNS服务器配置**:
+
+ * 确保所有配置的上游DNS服务器都能正常工作
+
+ * 移除或调整可能不可达的DNS服务器
+
+4. **调整DNSSEC配置**:
+
+ * 检查DNSSEC专用服务器的可达性
+
+ * 考虑暂时禁用DNSSEC验证,观察是否能改善性能
+
+5. **增强错误处理**:
+
+ * 优化`forwardDNSRequestWithCache`函数的错误处理逻辑
+
+ * 确保在所有服务器都超时的情况下有合理的回退机制
+
+6. **监控和日志优化**:
+
+ * 添加更详细的日志,记录每个DNS服务器的响应情况
+
+ * 增加监控指标,追踪DNS查询成功率和响应时间
+
+7. **测试验证**:
+
+ * 在修改后进行DNS查询测试,确保服务器能正常响应
+
+ * 监控日志,确认不再出现大量DNS查询失败的情况
+
diff --git a/.trae/documents/plan_20251225_130822.md b/.trae/documents/plan_20251225_130822.md
new file mode 100644
index 0000000..e4727ce
--- /dev/null
+++ b/.trae/documents/plan_20251225_130822.md
@@ -0,0 +1,53 @@
+## 问题分析
+
+DNS服务器出现"Server Failed"的根本原因是:当用户配置的DNS服务器地址没有包含端口号时,代码直接将其传递给`dns.Client.Exchange()`方法,而该方法需要完整的"IP:端口"格式地址。
+
+## 解决方案
+
+### 1. 创建DNS服务器地址处理函数
+- **功能**:确保DNS服务器地址始终包含端口号,默认添加53端口
+- **实现**:创建`normalizeDNSServerAddress`函数,检查并添加端口号
+
+### 2. 应用地址处理函数到所有DNS服务器配置
+
+**需要修改的位置**:
+- **主DNS服务器**:`s.config.UpstreamDNS`
+- **DNSSEC专用服务器**:`s.config.DNSSECUpstreamDNS`
+- **域名特定DNS服务器**:`s.config.DomainSpecificDNS`
+- **所有调用`resolver.Exchange()`的地方**:确保传递的服务器地址包含端口号
+
+### 3. 修改具体代码位置
+
+**文件**:`dns/server.go`
+
+**修改点**:
+1. **添加地址处理函数**:在文件中添加`normalizeDNSServerAddress`函数
+2. **在parallel模式中使用**:修改第865行附近的代码
+3. **在loadbalance模式中使用**:修改第1063行附近的代码
+4. **在fastest-ip模式中使用**:修改第1189行附近的代码
+5. **在default模式中使用**:修改第1311行附近的代码
+6. **在DNSSEC专用服务器请求中使用**:修改第1452行附近的代码
+7. **在本地解析中使用**:修改第1550行附近的代码
+
+### 4. 确保配置文件加载时也处理地址
+
+- 检查配置文件加载代码,确保在加载配置时就处理DNS服务器地址
+- 或者在每次使用DNS服务器地址时动态处理
+
+## 修复步骤
+
+1. **创建地址处理函数**:实现`normalizeDNSServerAddress`函数
+2. **修改所有DNS查询点**:在所有调用`resolver.Exchange()`的地方使用该函数
+3. **测试修复效果**:重启DNS服务器并测试查询功能
+4. **验证各种配置场景**:测试带端口和不带端口的DNS服务器配置
+
+## 预期效果
+
+- 当用户配置DNS服务器为`223.5.5.5`时,自动添加端口变为`223.5.5.5:53`
+- 当用户配置DNS服务器为`8.8.8.8:53`时,保持不变
+- DNS查询成功率显著提高,不再出现"Server Failed"错误
+- 支持各种DNS服务器配置格式,提高系统兼容性
+
+## 关键文件修改
+
+- `/root/dns/dns/server.go`:添加地址处理函数并应用到所有DNS查询点
\ No newline at end of file
diff --git a/.trae/documents/plan_20251225_150849.md b/.trae/documents/plan_20251225_150849.md
new file mode 100644
index 0000000..d15d185
--- /dev/null
+++ b/.trae/documents/plan_20251225_150849.md
@@ -0,0 +1,52 @@
+## 问题分析
+
+当前DNS服务器的解析记录显示存在以下问题:
+1. 前端`logs.js`中使用了`console.log`来调试和显示解析记录
+2. 需要确保API返回的解析记录是正确的JSON格式
+3. 前端需要正确解析API返回的JSON数据来显示解析记录
+
+## 解决方案
+
+### 1. 优化API返回格式
+
+**文件**:`dns/server.go`
+
+**修改内容**:
+- 确保`QueryLog`结构体的`Answers`字段正确序列化为JSON
+- 检查`DNSAnswer`结构体的JSON标签是否正确
+
+### 2. 清理前端console.log代码
+
+**文件**:`static/js/logs.js`
+
+**修改内容**:
+- 删除或注释掉第1047、1054行等console.log调试代码
+- 优化解析记录提取逻辑,确保正确处理API返回的JSON数据
+
+### 3. 优化解析记录显示逻辑
+
+**文件**:`static/js/logs.js`
+
+**修改内容**:
+- 完善`extractDNSRecords`函数,确保正确处理各种格式的解析记录
+- 优化解析记录的HTML渲染逻辑,确保显示格式清晰
+- 确保支持`log.answers`字段(小写)和`log.Answers`字段(大写)
+
+### 4. 测试验证
+
+**步骤**:
+- 重启DNS服务器
+- 使用API测试工具验证`/api/logs/query`返回的解析记录格式正确
+- 测试前端页面解析记录显示正常
+
+## 预期效果
+
+- API返回的解析记录格式为标准JSON
+- 前端页面不再使用console.log显示解析记录
+- 解析记录显示清晰、格式统一
+- 支持各种情况下的解析记录提取
+
+## 关键文件修改
+
+1. **`dns/server.go`**:确保解析记录正确序列化
+2. **`static/js/logs.js`**:优化解析记录显示逻辑,移除console.log代码
\ No newline at end of file
diff --git a/.trae/documents/plan_20251225_151740.md b/.trae/documents/plan_20251225_151740.md
new file mode 100644
index 0000000..dbff851
--- /dev/null
+++ b/.trae/documents/plan_20251225_151740.md
@@ -0,0 +1,43 @@
+## 问题分析
+
+当前服务器代码存在以下问题:
+1. `QueryLog`结构体中只有部分字段有JSON标签
+2. 缺少完整的JSON序列化支持,导致API返回的JSON格式不完整
+3. 需要确保所有字段都能正确序列化为JSON
+
+## 解决方案
+
+### 1. 完善QueryLog结构体的JSON标签
+
+**文件**:`dns/server.go`
+
+**修改内容**:
+- 为`QueryLog`结构体的所有字段添加正确的JSON标签
+- 确保`Timestamp`字段正确序列化为ISO格式时间
+- 确保`Answers`字段序列化为`"answers"`(小写)
+
+### 2. 确保API返回完整的JSON数据
+
+**文件**:`http/server.go`
+
+**修改内容**:
+- 检查`handleLogsQuery`函数,确保返回完整的日志数据
+- 确保日志查询API返回包含所有必要字段的JSON数据
+
+### 3. 测试验证
+
+**步骤**:
+- 重启DNS服务器
+- 使用API测试工具验证`/api/logs/query`返回的JSON格式正确
+- 确保所有字段都正确序列化
+
+## 预期效果
+
+- API返回的JSON数据包含所有日志字段
+- 前端能够正确解析API返回的JSON数据
+- 解析记录通过API查询方式显示,不再使用console.log
+
+## 关键文件修改
+
+1. **`dns/server.go`**:完善`QueryLog`结构体的JSON标签
+2. **`http/server.go`**:确保API返回完整的JSON数据
\ No newline at end of file
diff --git a/.trae/documents/plan_20251225_154403.md b/.trae/documents/plan_20251225_154403.md
new file mode 100644
index 0000000..da77107
--- /dev/null
+++ b/.trae/documents/plan_20251225_154403.md
@@ -0,0 +1,31 @@
+## 问题分析
+从用户提供的截图可以看到,解析记录显示存在问题,只显示了IP地址"104.26.24.30",而没有完整的解析记录格式,如"A: 104.26.24.30 (ttl=193)"。
+
+## 根本原因
+通过分析代码,发现问题可能出在以下几个方面:
+1. 解析记录的显示样式可能存在问题
+2. 或者在生成解析记录字符串时出现了问题
+3. 或者是在处理`dnsAnswers`数组时出现了问题
+
+## 修复方案
+1. 修改解析记录的生成逻辑,确保完整显示记录类型、值和TTL
+2. 检查并调整HTML元素的样式,确保多行文本正确显示
+
+## 具体修改点
+1. **修改解析记录的生成逻辑**:
+ - 在`showLogDetailModal`函数中,修改解析记录的生成逻辑,确保即使记录类型或TTL为空,也能正确显示
+ - 确保每个解析记录都按照"类型: 值 (ttl=TTL)"的格式显示
+
+2. **调整HTML元素的样式**:
+ - 检查并调整解析记录显示容器的样式,确保多行文本正确显示
+ - 确保`whitespace-pre-wrap`样式正确应用
+
+## 修复原则
+- 确保解析记录完整显示,包括记录类型、值和TTL
+- 保持良好的可读性
+- 确保样式兼容各种浏览器
+
+## 验证方法
+1. 修复代码后,重新加载页面
+2. 查看解析记录是否完整显示,包括记录类型、值和TTL
+3. 测试不同类型的解析记录,确保都能正确显示
\ No newline at end of file
diff --git a/.trae/documents/plan_20251225_161050.md b/.trae/documents/plan_20251225_161050.md
new file mode 100644
index 0000000..6fd4446
--- /dev/null
+++ b/.trae/documents/plan_20251225_161050.md
@@ -0,0 +1,34 @@
+## 移除查询日志详情中的屏蔽规则列
+
+### 1. 问题分析
+- 用户要求移除查询日志详情弹窗中的屏蔽规则列
+- 屏蔽规则列位于响应细节部分,显示在响应时间和响应代码之间
+- 该列显示了DNS查询被屏蔽时的规则信息
+
+### 2. 实现方案
+- 编辑`showLogDetailModal`函数,找到响应细节部分的HTML模板
+- 移除其中包含"规则"标题和`${blockRule}`变量的整个div元素
+- 保持其他响应细节(响应时间、响应代码、缓存状态)不变
+
+### 3. 代码修改
+- 修改文件:`/root/dns/static/js/logs.js`
+- 修改函数:`showLogDetailModal`
+- 移除位置:响应细节部分的`responseGrid` HTML模板
+- 移除内容:包含"规则"标题和`${blockRule}`变量的div元素
+
+### 4. 预期效果
+- 查询日志详情弹窗中将不再显示屏蔽规则列
+- 响应细节部分将只显示:响应时间、响应代码、缓存状态
+- 保持弹窗的整体布局和样式不变
+- 不影响其他功能的正常运行
+
+### 5. 技术细节
+- 使用HTML模板字符串修改DOM结构
+- 移除不必要的DOM元素,简化UI
+- 保持代码的可读性和可维护性
+
+### 6. 测试验证
+- 验证修改后的代码是否有语法错误
+- 验证查询日志详情弹窗是否正常显示
+- 验证屏蔽规则列已被成功移除
+- 验证其他功能是否正常工作
\ No newline at end of file
diff --git a/.trae/documents/plan_20251226_133837.md b/.trae/documents/plan_20251226_133837.md
new file mode 100644
index 0000000..28cefc2
--- /dev/null
+++ b/.trae/documents/plan_20251226_133837.md
@@ -0,0 +1,56 @@
+## 移除查询日志详情中的屏蔽规则列
+
+### 1. 问题分析
+
+* 用户要求移除查询日志详情弹窗中的屏蔽规则列
+
+* 屏蔽规则列位于响应细节部分,显示在响应时间和响应代码之间
+
+* 该列显示了DNS查询被屏蔽时的规则信息
+
+### 2. 实现方案
+
+* 编辑`showLogDetailModal`函数,找到响应细节部分的HTML模板
+
+* 移除其中包含"规则"标题和`${blockRule}`变量的整个div元素
+
+* 保持其他响应细节(响应时间、响应代码、缓存状态)不变
+
+### 3. 代码修改
+
+* 修改文件:`/root/dns/static/js/logs.js`
+
+* 修改函数:`showLogDetailModal`
+
+* 移除位置:响应细节部分的`responseGrid` HTML模板
+
+* 移除内容:包含"规则"标题和`${blockRule}`变量的div元素
+
+### 4. 预期效果
+
+* 查询日志详情弹窗中将不再显示屏蔽规则列
+
+* 响应细节部分将只显示:响应时间、响应代码、缓存状态
+
+* 保持弹窗的整体布局和样式不变
+
+* 不影响其他功能的正常运行
+
+### 5. 技术细节
+
+* 使用HTML模板字符串修改DOM结构
+
+* 移除不必要的DOM元素,简化UI
+
+* 保持代码的可读性和可维护性
+
+### 6. 测试验证
+
+* 验证修改后的代码是否有语法错误
+
+* 验证查询日志详情弹窗是否正常显示
+
+* 验证屏蔽规则列已被成功移除
+
+* 验证其他功能是否正常工作
+
diff --git a/.trae/documents/plan_20251226_142616.md b/.trae/documents/plan_20251226_142616.md
new file mode 100644
index 0000000..cfc374e
--- /dev/null
+++ b/.trae/documents/plan_20251226_142616.md
@@ -0,0 +1,51 @@
+## 优化DNS请求处理逻辑,减少返回客户端超时
+
+### 1. 问题分析
+
+* 服务器请求上游解析成功,但返回给客户端超时
+* 主要原因是`forwardDNSRequestWithCache`函数等待所有上游服务器响应,导致某个慢服务器拖慢整体响应
+* 虽然是并行查询,但没有实现快速响应返回机制
+* 阻塞式等待所有响应完成,而不是优先返回最快的成功响应
+
+### 2. 实现方案
+
+* 优化`forwardDNSRequestWithCache`函数,实现快速响应返回机制
+* 当收到第一个成功响应时,立即返回给客户端,不再等待其他服务器响应
+* 保持后台继续接收其他响应,更新最佳响应和服务器状态
+* 优化并行查询逻辑,提高响应速度
+* 保持代码的可读性和可维护性
+
+### 3. 代码修改
+
+* 修改文件:`/root/dns/dns/server.go`
+* 修改函数:`forwardDNSRequestWithCache`
+* 优化位置:`parallel`模式和`default`模式下的响应处理逻辑
+* 优化内容:
+ - 实现快速响应返回机制
+ - 当收到第一个成功响应时,立即返回给客户端
+ - 保持后台处理其他响应
+ - 优化并行查询逻辑
+
+### 4. 预期效果
+
+* 减少DNS查询的平均响应时间
+* 避免因某个上游服务器响应慢而导致整体超时
+* 提高DNS服务器的吞吐量和并发处理能力
+* 保持对上游服务器状态的准确跟踪
+* 不影响现有功能的正常运行
+
+### 5. 技术细节
+
+* 使用通道和goroutine实现非阻塞响应处理
+* 当收到第一个成功响应时,立即返回给客户端
+* 保持后台继续接收其他响应,更新最佳响应和服务器状态
+* 优化并行查询逻辑,提高响应速度
+* 保持代码的可读性和可维护性
+
+### 6. 测试验证
+
+* 验证修改后的代码是否有语法错误
+* 验证DNS查询响应时间是否明显减少
+* 验证是否解决了返回客户端超时的问题
+* 验证其他功能是否正常工作
+* 验证上游服务器状态跟踪是否准确
\ No newline at end of file
diff --git a/.trae/documents/plan_20251228_100157.md b/.trae/documents/plan_20251228_100157.md
new file mode 100644
index 0000000..64d35e0
--- /dev/null
+++ b/.trae/documents/plan_20251228_100157.md
@@ -0,0 +1,78 @@
+## 优化DNS请求处理逻辑,减少返回客户端超时
+
+### 1. 问题分析
+
+* 服务器请求上游解析成功,但返回给客户端超时
+
+* 主要原因是`forwardDNSRequestWithCache`函数等待所有上游服务器响应,导致某个慢服务器拖慢整体响应
+
+* 虽然是并行查询,但没有实现快速响应返回机制
+
+* 阻塞式等待所有响应完成,而不是优先返回最快的成功响应
+
+### 2. 实现方案
+
+* 优化`forwardDNSRequestWithCache`函数,实现快速响应返回机制
+
+* 当收到第一个成功响应时,立即返回给客户端,不再等待其他服务器响应
+
+* 保持后台继续接收其他响应,更新最佳响应和服务器状态
+
+* 优化并行查询逻辑,提高响应速度
+
+* 保持代码的可读性和可维护性
+
+### 3. 代码修改
+
+* 修改文件:`/root/dns/dns/server.go`
+
+* 修改函数:`forwardDNSRequestWithCache`
+
+* 优化位置:`parallel`模式和`default`模式下的响应处理逻辑
+
+* 优化内容:
+
+ * 实现快速响应返回机制
+
+ * 当收到第一个成功响应时,立即返回给客户端
+
+ * 保持后台处理其他响应
+
+ * 优化并行查询逻辑
+
+### 4. 预期效果
+
+* 减少DNS查询的平均响应时间
+
+* 避免因某个上游服务器响应慢而导致整体超时
+
+* 提高DNS服务器的吞吐量和并发处理能力
+
+* 保持对上游服务器状态的准确跟踪
+
+* 不影响现有功能的正常运行
+
+### 5. 技术细节
+
+* 使用通道和goroutine实现非阻塞响应处理
+
+* 当收到第一个成功响应时,立即返回给客户端
+
+* 保持后台继续接收其他响应,更新最佳响应和服务器状态
+
+* 优化并行查询逻辑,提高响应速度
+
+* 保持代码的可读性和可维护性
+
+### 6. 测试验证
+
+* 验证修改后的代码是否有语法错误
+
+* 验证DNS查询响应时间是否明显减少
+
+* 验证是否解决了返回客户端超时的问题
+
+* 验证其他功能是否正常工作
+
+* 验证上游服务器状态跟踪是否准确
+
diff --git a/.trae/documents/plan_20251229_142423.md b/.trae/documents/plan_20251229_142423.md
new file mode 100644
index 0000000..6e9d88f
--- /dev/null
+++ b/.trae/documents/plan_20251229_142423.md
@@ -0,0 +1,20 @@
+# 修复域名匹配机制问题
+
+## 问题分析
+通过详细检查domain-info.json文件,我发现了问题的根本原因:
+- JSON文件的结构存在错误,`domains`对象只包含了网易公司
+- 其他公司(如阿里云、百度等)被错误地放在了`domains`对象之外
+- 这导致域名匹配机制只能找到网易公司的信息,而无法匹配其他公司
+
+## 修复计划
+1. **修复JSON文件结构**:将所有公司信息正确地包含在`domains`对象中
+2. **优化域名匹配逻辑**:根据用户需求,实现优先匹配完整URL域名,再匹配主域名的逻辑
+3. **测试修复效果**:验证所有公司的域名都能被正确匹配
+
+## 预期效果
+- 修复后的JSON文件结构正确,所有公司都包含在`domains`对象中
+- 域名匹配机制能够正确识别所有公司的域名
+- 匹配规则:
+ - 优先匹配完整URL域名(如baike.baidu.com)
+ - 如果没有匹配上,则匹配主域名(如baidu.com)
+ - 输出对应的公司名称(如北京百度网讯科技有限公司)
\ No newline at end of file
diff --git a/.trae/documents/plan_20251229_175254.md b/.trae/documents/plan_20251229_175254.md
new file mode 100644
index 0000000..16c3116
--- /dev/null
+++ b/.trae/documents/plan_20251229_175254.md
@@ -0,0 +1,33 @@
+## 问题分析
+
+通过测试脚本的输出,我发现了kdocs.cn无法匹配到域名信息的原因:
+
+1. **JSON结构错误**:domain-info.json文件中字节跳动公司的结构存在错误,导致解析后的对象结构不正确。
+
+2. **遍历顺序问题**:由于JSON结构错误,当遍历到字节跳动公司时,脚本没有正确跳过company属性,而是继续处理字节跳动对象内部的属性,然后遇到了一个名为"company"的公司,这个公司的属性值是categories对象的值。
+
+3. **遍历不完整**:由于结构错误,脚本在遍历到字节跳动公司后就无法继续遍历到金山办公公司,而金山办公公司正是kdocs.cn所属的公司。
+
+## 解决方案
+
+### 1. 修复domain-info.json文件的结构
+
+修复字节跳动公司的结构错误,确保其包含正确的闭合括号和逗号:
+
+- 修复抖音视频对象的闭合括号
+- 确保今日头条API服务和豆包对象是字节跳动公司的直接子对象
+- 确保company属性是字节跳动公司的直接子属性
+
+### 2. 测试修复效果
+
+修复后,重新运行测试脚本,验证kdocs.cn和www.kdocs.cn能够正确匹配到金山办公公司的金山文档。
+
+## 实施步骤
+
+1. 修改domain-info.json文件,修复字节跳动公司的结构错误
+2. 运行test-fetch.js测试脚本,验证修复效果
+3. 确认kdocs.cn和www.kdocs.cn能够正确匹配到域名信息
+
+## 预期效果
+
+修复后,kdocs.cn和www.kdocs.cn将能够正确匹配到金山办公公司的金山文档,显示网站名称、图标、类别和所属公司。
\ No newline at end of file
diff --git a/.trae/documents/plan_20260102_110215.md b/.trae/documents/plan_20260102_110215.md
new file mode 100644
index 0000000..abe8356
--- /dev/null
+++ b/.trae/documents/plan_20260102_110215.md
@@ -0,0 +1,32 @@
+# 移除负载均衡查询模式
+
+## 问题分析
+用户要求移除负载均衡查询模式,目前代码中支持多种查询模式,包括 "loadbalance"(负载均衡)、"parallel"(并行请求)、"fastest-ip"(最快的IP地址)。负载均衡模式使用加权随机选择算法来选择上游服务器。
+
+## 解决方案
+1. 从配置中移除 "loadbalance" 作为可用选项
+2. 从代码中移除 "loadbalance" 分支
+3. 确保其他使用 `selectWeightedRandomServer` 函数的地方不受影响
+
+## 实施步骤
+1. 修改 `config/config.go` 文件,更新 `QueryMode` 字段的注释,移除 "loadbalance" 选项
+2. 修改 `dns/server.go` 文件,移除 switch 语句中的 "loadbalance" 分支
+3. 确保所有使用 `selectWeightedRandomServer` 函数的地方仍然正常工作
+4. 测试修改后的代码,确保 DNS 服务器仍然正常运行
+
+## 修改内容
+- 文件:`/root/dns/config/config.go`
+ - 修改点:更新 `QueryMode` 字段的注释,移除 "loadbalance" 选项
+
+- 文件:`/root/dns/dns/server.go`
+ - 修改点:移除 switch 语句中的 "loadbalance" 分支(第1139-1260行)
+
+## 预期效果
+- 负载均衡查询模式将不再可用
+- DNS 服务器仍然支持其他查询模式(parallel、fastest-ip)
+- 其他功能不受影响
+- 代码更加简洁,减少了维护成本
+
+## 注意事项
+- `selectWeightedRandomServer` 函数不仅用于负载均衡模式,还用于选择 DNSSEC 服务器和本地服务器,所以不能删除这个函数
+- 确保修改后所有其他功能仍然正常工作
\ No newline at end of file
diff --git a/.trae/documents/plan_20260102_110918.md b/.trae/documents/plan_20260102_110918.md
new file mode 100644
index 0000000..5022ad2
--- /dev/null
+++ b/.trae/documents/plan_20260102_110918.md
@@ -0,0 +1,32 @@
+# 清理无关代码
+
+## 问题分析
+我已经成功移除了负载均衡查询模式的主要代码,但还有一些相关的残留代码需要清理,包括:
+1. CHANGELOG.md文件中提到了loadbalance模式
+2. 可能还有其他未使用的代码或配置
+
+## 解决方案
+1. 更新CHANGELOG.md文件,移除或修改与loadbalance模式相关的条目
+2. 检查是否有其他未使用的代码或配置
+3. 确保所有修改都不会影响现有功能
+
+## 实施步骤
+1. 修改CHANGELOG.md文件,移除或修改与loadbalance模式相关的条目
+2. 检查是否有其他未使用的代码或配置
+3. 编译并测试修改后的代码,确保功能正常
+
+## 修改内容
+- 文件:`/root/dns/CHANGELOG.md`
+ - 修改点1:第106行,移除或修改与loadbalance模式相关的条目
+ - 修改点2:第112行,移除或修改与loadbalance模式相关的条目
+
+## 预期效果
+- 代码库中不再有与已移除功能相关的残留代码
+- 文档与实际代码保持一致
+- 现有功能不受影响
+- 代码库更加整洁,易于维护
+
+## 注意事项
+- 不要修改数据文件中的loadbalance相关规则,这些是广告过滤规则,不是代码
+- 确保所有修改都不会影响现有功能
+- 编译并测试修改后的代码,确保功能正常
\ No newline at end of file
diff --git a/.trae/documents/plan_20260104_105942.md b/.trae/documents/plan_20260104_105942.md
new file mode 100644
index 0000000..8fb9c50
--- /dev/null
+++ b/.trae/documents/plan_20260104_105942.md
@@ -0,0 +1,113 @@
+## 支持Base64编码GFWList格式的实现计划
+
+### 问题分析
+
+当前的GFWList加载代码直接按行解析文件内容,无法处理Base64编码的GFWList格式。Base64编码的GFWList文件包含:
+1. 头部注释行(以!开头)
+2. Base64编码的规则内容(整个文件或主要部分)
+3. 可能的尾部注释
+
+### 解决方案
+
+修改`gfw/manager.go`中的`LoadRules`函数,添加Base64解码支持:
+
+1. **检测Base64编码**:
+ - 读取文件内容
+ - 跳过注释行
+ - 检查剩余内容是否为Base64编码
+
+2. **解码Base64内容**:
+ - 如果是Base64编码,使用`encoding/base64`包解码
+ - 解码后得到原始规则文本
+
+3. **解析规则**:
+ - 按行分割解码后的内容
+ - 调用现有的`parseRule`函数处理每一行
+
+### 具体修改点
+
+1. **添加Base64依赖**:
+ - 在`gfw/manager.go`中添加`encoding/base64`导入
+
+2. **修改LoadRules函数**:
+ - 读取文件内容
+ - 过滤注释行,收集可能的Base64内容
+ - 尝试Base64解码
+ - 解析解码后的规则
+
+3. **测试**:
+ - 使用现有的Base64编码GFWList文件测试
+ - 确保解码和规则解析正常工作
+
+### 预期效果
+
+- 服务器能够正确加载Base64编码的GFWList文件
+- 兼容现有的纯文本GFWList格式
+- 规则解析结果与原格式一致
+
+### 代码修改示例
+
+```go
+// 在import中添加encoding/base64
+import (
+ // 现有导入
+ "encoding/base64"
+ // 现有导入
+)
+
+// 修改LoadRules函数
+func (m *GFWListManager) LoadRules() error {
+ // 现有代码...
+
+ // 从文件路径读取GFWList内容
+ content, err := os.ReadFile(m.config.Content)
+ if err != nil {
+ return fmt.Errorf("读取GFWList文件失败: %w", err)
+ }
+
+ // 处理Base64编码
+ rawContent := string(content)
+
+ // 过滤注释行,收集Base64内容
+ var base64Content strings.Builder
+ lines := strings.Split(rawContent, "\n")
+ for _, line := range lines {
+ line = strings.TrimSpace(line)
+ if line == "" || strings.HasPrefix(line, "!") || strings.HasPrefix(line, "[") {
+ // 跳过注释行和头信息
+ continue
+ }
+ base64Content.WriteString(line)
+ }
+
+ // 尝试Base64解码
+ decoded, err := base64.StdEncoding.DecodeString(base64Content.String())
+ if err == nil {
+ // 解码成功,使用解码后的内容
+ rawContent = string(decoded)
+ } else {
+ // 解码失败,使用原始内容(可能是纯文本格式)
+ }
+
+ // 按行解析规则
+ ruleLines := strings.Split(rawContent, "\n")
+ for _, line := range ruleLines {
+ line = strings.TrimSpace(line)
+ if line == "" || strings.HasPrefix(line, "!") || strings.HasPrefix(line, "[") {
+ // 跳过空行、注释行和头信息行
+ continue
+ }
+ m.parseRule(line)
+ }
+
+ // 现有代码...
+}
+```
+
+### 实施步骤
+
+1. 修改`gfw/manager.go`,添加Base64支持
+2. 编译并测试
+3. 验证规则加载是否正常
+
+这个修改将使服务器能够同时支持纯文本和Base64编码的GFWList格式,提高了兼容性和灵活性。
\ No newline at end of file
diff --git a/.trae/documents/plan_20260105_074926.md b/.trae/documents/plan_20260105_074926.md
new file mode 100644
index 0000000..109b034
--- /dev/null
+++ b/.trae/documents/plan_20260105_074926.md
@@ -0,0 +1,196 @@
+# 平均响应时间计算错误修复方案
+
+## 问题分析
+通过对代码的分析,我发现平均响应时间计算错误的根本原因是:
+
+1. **统计数据持久化问题**:当服务器重启时,`loadStatsData` 函数直接覆盖 `s.stats` 对象,导致 `TotalResponseTime` 和 `Queries` 之间的关系可能被破坏
+
+2. **异常响应时间累计**:在某些情况下,`responseTime` 可能被错误计算为非常大的值,导致 `TotalResponseTime` 异常增长
+
+3. **计算逻辑不健壮**:平均响应时间计算没有考虑异常情况,如 `Queries` 为 0 或 `TotalResponseTime` 溢出
+
+4. **统计数据一致性问题**:并发访问时可能导致统计数据不一致
+
+## 修复方案
+
+### 1. 修复 `loadStatsData` 函数
+修改统计数据加载逻辑,确保 `TotalResponseTime` 和 `Queries` 之间的关系正确:
+
+```go
+// 恢复统计数据
+s.statsMutex.Lock()
+if statsData.Stats != nil {
+ // 只恢复有效数据,避免破坏统计关系
+ s.stats.Queries += statsData.Stats.Queries
+ s.stats.Blocked += statsData.Stats.Blocked
+ s.stats.Allowed += statsData.Stats.Allowed
+ s.stats.Errors += statsData.Stats.Errors
+ s.stats.TotalResponseTime += statsData.Stats.TotalResponseTime
+ s.stats.DNSSECQueries += statsData.Stats.DNSSECQueries
+ s.stats.DNSSECSuccess += statsData.Stats.DNSSECSuccess
+ s.stats.DNSSECFailed += statsData.Stats.DNSSECFailed
+
+ // 重新计算平均响应时间,确保一致性
+ if s.stats.Queries > 0 {
+ s.stats.AvgResponseTime = float64(s.stats.TotalResponseTime) / float64(s.stats.Queries)
+ }
+
+ // 合并查询类型统计
+ for k, v := range statsData.Stats.QueryTypes {
+ s.stats.QueryTypes[k] += v
+ }
+
+ // 合并来源IP统计
+ for ip := range statsData.Stats.SourceIPs {
+ s.stats.SourceIPs[ip] = true
+ }
+
+ // 确保使用当前配置中的EnableDNSSEC值
+ s.stats.DNSSECEnabled = s.config.EnableDNSSEC
+}
+s.statsMutex.Unlock()
+```
+
+### 2. 修复响应时间计算逻辑
+在 `handleDNSRequest` 函数中,添加响应时间合理性检查:
+
+```go
+// 使用上游服务器的实际响应时间(转换为毫秒)
+responseTime := int64(rtt.Milliseconds())
+// 如果rtt为0(查询失败),则使用本地计算的时间
+if responseTime == 0 {
+ responseTime = time.Since(startTime).Milliseconds()
+}
+
+// 添加合理性检查,避免异常大的响应时间影响统计
+if responseTime > 60000 { // 超过60秒的响应时间视为异常
+ responseTime = 60000
+}
+```
+
+### 3. 优化平均响应时间计算
+修改 `updateStats` 函数,确保平均响应时间计算的健壮性:
+
+```go
+s.updateStats(func(stats *Stats) {
+ stats.TotalResponseTime += responseTime
+ // 添加防御性编程,确保Queries大于0
+ if stats.Queries > 0 {
+ // 使用更精确的计算方式,避免浮点数精度问题
+ stats.AvgResponseTime = float64(stats.TotalResponseTime) / float64(stats.Queries)
+ // 限制平均响应时间的范围,避免显示异常大的值
+ if stats.AvgResponseTime > 60000 {
+ stats.AvgResponseTime = 60000
+ }
+ }
+})
+```
+
+### 4. 添加统计数据重置机制
+添加定期重置统计数据的功能,避免 `TotalResponseTime` 无限增长:
+
+```go
+// Start 启动DNS服务器
+func (s *Server) Start() error {
+ // 现有代码...
+
+ // 启动统计数据定期重置功能(每24小时)
+ go func() {
+ ticker := time.NewTicker(24 * time.Hour)
+ defer ticker.Stop()
+ for {
+ select {
+ case <-ticker.C:
+ s.resetStats()
+ case <-s.ctx.Done():
+ return
+ }
+ }
+ }()
+
+ // 现有代码...
+}
+
+// resetStats 重置统计数据
+func (s *Server) resetStats() {
+ s.statsMutex.Lock()
+ defer s.statsMutex.Unlock()
+
+ // 只重置累计值,保留配置相关值
+ s.stats.TotalResponseTime = 0
+ s.stats.AvgResponseTime = 0
+ s.stats.Queries = 0
+ s.stats.Blocked = 0
+ s.stats.Allowed = 0
+ s.stats.Errors = 0
+ s.stats.DNSSECQueries = 0
+ s.stats.DNSSECSuccess = 0
+ s.stats.DNSSECFailed = 0
+ s.stats.QueryTypes = make(map[string]int64)
+ s.stats.SourceIPs = make(map[string]bool)
+
+ logger.Info("统计数据已重置")
+}
+```
+
+### 5. 修复 GetStats 函数
+确保 `GetStats` 函数正确复制所有统计数据:
+
+```go// GetStats 获取DNS服务器统计信息
+func (s *Server) GetStats() *Stats {
+ s.statsMutex.Lock()
+ defer s.statsMutex.Unlock()
+
+ // 创建完整的统计数据副本
+ statsCopy := &Stats{
+ Queries: s.stats.Queries,
+ Blocked: s.stats.Blocked,
+ Allowed: s.stats.Allowed,
+ Errors: s.stats.Errors,
+ LastQuery: s.stats.LastQuery,
+ AvgResponseTime: s.stats.AvgResponseTime,
+ TotalResponseTime: s.stats.TotalResponseTime,
+ QueryTypes: make(map[string]int64),
+ SourceIPs: make(map[string]bool),
+ CpuUsage: s.stats.CpuUsage,
+ DNSSECQueries: s.stats.DNSSECQueries,
+ DNSSECSuccess: s.stats.DNSSECSuccess,
+ DNSSECFailed: s.stats.DNSSECFailed,
+ DNSSECEnabled: s.stats.DNSSECEnabled,
+ }
+
+ // 复制查询类型统计
+ for k, v := range s.stats.QueryTypes {
+ statsCopy.QueryTypes[k] = v
+ }
+
+ // 复制来源IP统计
+ for ip := range s.stats.SourceIPs {
+ statsCopy.SourceIPs[ip] = true
+ }
+
+ return statsCopy
+}
+```
+
+## 修复效果
+
+1. **数据一致性**:修复后,`TotalResponseTime` 和 `Queries` 之间的关系将保持正确,避免因服务器重启导致的统计数据不一致
+
+2. **异常值处理**:添加响应时间合理性检查,避免异常大的响应时间影响平均响应时间计算
+
+3. **计算健壮性**:优化平均响应时间计算逻辑,添加防御性编程,确保计算结果合理
+
+4. **统计数据管理**:添加定期重置统计数据的功能,避免 `TotalResponseTime` 无限增长导致的溢出问题
+
+5. **并发安全**:确保所有统计数据操作都是线程安全的,避免并发访问导致的数据不一致
+
+## 实现步骤
+
+1. 修改 `loadStatsData` 函数,修复统计数据加载逻辑
+2. 修改 `handleDNSRequest` 函数,添加响应时间合理性检查
+3. 修改 `updateStats` 函数,优化平均响应时间计算
+4. 添加 `resetStats` 函数,实现统计数据重置功能
+5. 修改 `Start` 函数,启动定期重置统计数据的协程
+6. 修复 `GetStats` 函数,确保正确复制所有统计数据
+7. 测试修复效果,验证平均响应时间计算是否正确
\ No newline at end of file
diff --git a/.trae/documents/plan_20260114_150542.md b/.trae/documents/plan_20260114_150542.md
new file mode 100644
index 0000000..ffde5b6
--- /dev/null
+++ b/.trae/documents/plan_20260114_150542.md
@@ -0,0 +1,46 @@
+## GFWList管理页面实现计划
+
+### 1. 修改 `index.html`
+- 在侧边栏菜单(两个位置:桌面端和移动端)添加新的菜单项"GFWList管理"
+- 创建新的页面内容区域 `#gfwlist-content`
+- 添加配置选项:
+ - GFWList总开关(checkbox)
+ - GFWList解析目标IP输入框
+ - 通行网站开关组:谷歌、YouTube、Facebook、X(各checkbox)
+- 添加保存和重启服务按钮
+
+### 2. 修改 `main.js`
+- 在页面标题映射中添加 `'gfwlist': 'GFWList管理'`
+- 在 `contentSections` 数组中添加 `gfwlist-content`
+- 添加hash为'gfwlist'时的页面初始化逻辑
+
+### 3. 修改 `config.js`
+- 添加GFWList页面初始化函数 `initGFWListPage()`
+- 添加GFWList配置加载函数 `loadGFWListConfig()`
+- 添加GFWList配置保存函数 `saveGFWListConfig()`
+- 添加GFWList配置收集函数 `collectGFWListFormData()`
+- 更新 `collectFormData()` 以包含新的GFWList配置字段
+- 更新 `populateConfigForm()` 移除原有的GFWList配置(已迁移到独立页面)
+
+### 4. 修改 `api.js`
+- 添加GFWList专用的API方法(如需要)
+
+### 配置数据结构
+```json
+{
+ "gfwlist": {
+ "enabled": true,
+ "targetIP": "127.0.0.1",
+ "allowGoogle": true,
+ "allowYouTube": true,
+ "allowFacebook": true,
+ "allowTwitter": true
+ }
+}
+```
+
+### 实现顺序
+1. 先修改HTML添加页面结构和菜单
+2. 修改main.js添加导航支持
+3. 修改config.js添加前端逻辑
+4. 测试验证
\ No newline at end of file
diff --git a/.trae/documents/优化DNS服务器parallel模式响应时间.md b/.trae/documents/优化DNS服务器parallel模式响应时间.md
new file mode 100644
index 0000000..1706f96
--- /dev/null
+++ b/.trae/documents/优化DNS服务器parallel模式响应时间.md
@@ -0,0 +1,86 @@
+# 优化DNS服务器parallel模式响应时间
+
+## 问题分析
+
+经过对代码的分析,我发现parallel模式下响应时间过高的主要原因包括:
+
+1. **缺少超时机制**:当前实现会等待所有上游服务器响应,单个慢服务器会拖慢整个查询
+2. **响应时间计算不合理**:使用所有响应的平均时间,而不是最快响应时间
+3. **合并响应开销大**:需要合并所有响应,增加CPU和内存开销
+4. **等待所有响应**:没有实现快速返回机制,即使收到第一个有效响应也会等待所有响应
+5. **DNSSEC验证开销**:每个响应都需要进行DNSSEC验证,增加额外开销
+
+## 优化方案
+
+### 1. 添加超时机制
+- 为每个上游服务器请求添加超时设置
+- 超时时间可配置,建议默认500ms
+- 超时的请求不会影响整体响应时间
+
+### 2. 实现快速返回机制
+- 当收到第一个有效响应(成功或NXDOMAIN)时,立即返回给客户端
+- 继续处理其他响应用于合并和缓存,但不影响当前查询的响应时间
+- 优先返回带DNSSEC的响应
+
+### 3. 优化响应时间计算
+- 使用最快的响应时间作为查询的响应时间
+- 保留平均响应时间用于统计,但不影响客户端感知的响应时间
+
+### 4. 优化响应合并逻辑
+- 只合并成功响应,忽略错误响应
+- 合并时优先保留TTL较长的记录
+- 减少不必要的内存分配和拷贝
+
+### 5. 优化DNSSEC验证
+- 只对需要返回的响应进行DNSSEC验证
+- 缓存DNSSEC验证结果,减少重复验证
+
+### 6. 增加服务器健康检查
+- 定期检查上游服务器的响应时间和可用性
+- 只向健康的服务器发送请求
+- 根据历史响应时间动态调整服务器权重
+
+## 实现步骤
+
+1. **修改forwardDNSRequestWithCache函数**:
+ - 添加超时设置
+ - 实现快速返回逻辑
+ - 优化响应时间计算
+
+2. **修改mergeResponses函数**:
+ - 优化合并逻辑,减少开销
+ - 优先保留TTL较长的记录
+
+3. **修改DNSSEC验证逻辑**:
+ - 只对需要返回的响应进行验证
+ - 添加DNSSEC验证结果缓存
+
+4. **添加服务器健康检查机制**:
+ - 定期检查上游服务器
+ - 动态调整服务器列表
+
+5. **添加配置选项**:
+ - 超时时间配置
+ - 快速返回开关
+ - 健康检查配置
+
+## 预期效果
+
+- **响应时间显著降低**:客户端感知的响应时间将接近最快的上游服务器响应时间
+- **资源利用率提高**:减少不必要的等待和计算
+- **鲁棒性增强**:单个慢服务器不会影响整体性能
+- **用户体验改善**:更快的DNS解析速度
+
+## 文件修改
+
+- `/root/dns/dns/server.go`:主要修改文件,包含parallel模式的实现逻辑
+- `/root/dns/config/config.go`:添加新的配置选项
+- `/root/dns/dns/cache.go`:如果需要添加DNSSEC验证结果缓存
+
+## 测试计划
+
+1. **性能测试**:比较优化前后的响应时间
+2. **压力测试**:在高并发情况下测试性能
+3. **可靠性测试**:测试单个服务器故障时的表现
+4. **DNSSEC测试**:确保DNSSEC验证仍然正常工作
+5. **不同配置测试**:测试不同超时时间和服务器数量的影响
\ No newline at end of file
diff --git a/.trae/documents/修复DNS服务器CNAME处理和NXDOMAIN错误问题.md b/.trae/documents/修复DNS服务器CNAME处理和NXDOMAIN错误问题.md
new file mode 100644
index 0000000..e7af61d
--- /dev/null
+++ b/.trae/documents/修复DNS服务器CNAME处理和NXDOMAIN错误问题.md
@@ -0,0 +1,182 @@
+## 问题分析
+
+通过深入分析代码,我找到了导致所有查询都显示同一个 NXDOMAIN 错误的根本原因:
+
+**核心问题**:`mergeResponses` 函数在合并多个 DNS 响应时,**没有正确处理 Rcode 字段**!
+
+**具体原因**:
+1. 当使用并行查询模式时,DNS 服务器会向多个上游服务器发送请求
+2. 函数使用第一个响应作为基础来合并其他响应
+3. 它清空了 `Answer`、`Ns` 和 `Extra` 字段,但**保留了第一个响应的 Rcode**
+4. 如果第一个响应返回 NXDOMAIN(比如对于恶意域名 www.evilsnssdk.com),那么**合并后的响应也会保持 NXDOMAIN 状态**,即使其他响应返回成功
+5. 这导致所有查询都显示同一个 NXDOMAIN 错误
+
+## 修复方案
+
+### 修复 `mergeResponses` 函数
+
+**关键修改点**:
+1. **重置 Rcode**:在合并响应前,将 Rcode 重置为成功状态
+2. **处理 NXDOMAIN**:只有当所有响应都是 NXDOMAIN 时,才返回 NXDOMAIN
+3. **优先使用成功响应**:如果有任何响应返回成功,就使用成功的 Rcode
+
+### 修复步骤
+
+1. **修改 `mergeResponses` 函数** (`/root/dns/dns/server.go:842-933`)
+ - 在合并记录前,将 `mergedResponse.Rcode` 设置为 `dns.RcodeSuccess`
+ - 添加变量 `allNXDOMAIN` 来跟踪是否所有响应都是 NXDOMAIN
+ - 遍历所有响应,检查是否有成功响应
+ - 如果所有响应都是 NXDOMAIN,才将 `mergedResponse.Rcode` 设置为 `dns.RcodeNameError`
+
+2. **优化合并逻辑**
+ - 确保优先使用成功响应中的记录
+ - 避免将 NXDOMAIN 响应的记录合并到成功响应中
+ - 保持响应的一致性,Rcode 与记录内容匹配
+
+## 修复代码
+
+```go
+// mergeResponses 合并多个DNS响应
+func mergeResponses(responses []*dns.Msg) *dns.Msg {
+ if len(responses) == 0 {
+ return nil
+ }
+
+ // 如果只有一个响应,直接返回,避免不必要的合并操作
+ if len(responses) == 1 {
+ return responses[0].Copy()
+ }
+
+ // 使用第一个响应作为基础
+ mergedResponse := responses[0].Copy()
+ mergedResponse.Answer = []dns.RR{}
+ mergedResponse.Ns = []dns.RR{}
+ mergedResponse.Extra = []dns.RR{}
+
+ // 重置Rcode为成功,除非所有响应都是NXDOMAIN
+ mergedResponse.Rcode = dns.RcodeSuccess
+
+ // 检查是否所有响应都是NXDOMAIN
+ allNXDOMAIN := true
+
+ // 收集所有成功响应的记录
+ for _, resp := range responses {
+ if resp == nil {
+ continue
+ }
+
+ // 如果有任何响应是成功的,就不是allNXDOMAIN
+ if resp.Rcode == dns.RcodeSuccess {
+ allNXDOMAIN = false
+ }
+ }
+
+ // 如果所有响应都是NXDOMAIN,设置合并响应为NXDOMAIN
+ if allNXDOMAIN {
+ mergedResponse.Rcode = dns.RcodeNameError
+ }
+
+ // 使用map存储唯一记录,选择最长TTL
+ // 预分配map容量,减少扩容开销
+ answerMap := make(map[recordKey]dns.RR, len(responses[0].Answer)*len(responses))
+ nsMap := make(map[recordKey]dns.RR, len(responses[0].Ns)*len(responses))
+ extraMap := make(map[recordKey]dns.RR, len(responses[0].Extra)*len(responses))
+
+ for _, resp := range responses {
+ if resp == nil {
+ continue
+ }
+
+ // 只合并与最终Rcode匹配的响应记录
+ if (mergedResponse.Rcode == dns.RcodeSuccess && resp.Rcode == dns.RcodeSuccess) ||
+ (mergedResponse.Rcode == dns.RcodeNameError && resp.Rcode == dns.RcodeNameError) {
+
+ // 合并Answer部分
+ for _, rr := range resp.Answer {
+ key := getRecordKey(rr)
+ if existing, exists := answerMap[key]; exists {
+ // 如果存在相同记录,选择TTL更长的
+ if rr.Header().Ttl > existing.Header().Ttl {
+ answerMap[key] = rr
+ }
+ } else {
+ answerMap[key] = rr
+ }
+ }
+
+ // 合并Ns部分
+ for _, rr := range resp.Ns {
+ key := getRecordKey(rr)
+ if existing, exists := nsMap[key]; exists {
+ // 如果存在相同记录,选择TTL更长的
+ if rr.Header().Ttl > existing.Header().Ttl {
+ nsMap[key] = rr
+ }
+ } else {
+ nsMap[key] = rr
+ }
+ }
+
+ // 合并Extra部分
+ for _, rr := range resp.Extra {
+ // 跳过OPT记录,避免重复
+ if rr.Header().Rrtype == dns.TypeOPT {
+ continue
+ }
+ key := getRecordKey(rr)
+ if existing, exists := extraMap[key]; exists {
+ // 如果存在相同记录,选择TTL更长的
+ if rr.Header().Ttl > existing.Header().Ttl {
+ extraMap[key] = rr
+ }
+ } else {
+ extraMap[key] = rr
+ }
+ }
+ }
+ }
+
+ // 预分配切片容量,减少扩容开销
+ mergedResponse.Answer = make([]dns.RR, 0, len(answerMap))
+ mergedResponse.Ns = make([]dns.RR, 0, len(nsMap))
+ mergedResponse.Extra = make([]dns.RR, 0, len(extraMap))
+
+ // 将map转换回切片
+ for _, rr := range answerMap {
+ mergedResponse.Answer = append(mergedResponse.Answer, rr)
+ }
+
+ for _, rr := range nsMap {
+ mergedResponse.Ns = append(mergedResponse.Ns, rr)
+ }
+
+ for _, rr := range extraMap {
+ mergedResponse.Extra = append(mergedResponse.Extra, rr)
+ }
+
+ return mergedResponse
+}```
+
+## 预期效果
+
+修复后,DNS服务器将能够:
+- 正确合并多个 DNS 响应
+- 确保 Rcode 与实际记录内容匹配
+- 只有当所有响应都是 NXDOMAIN 时才返回 NXDOMAIN
+- 避免单个恶意域名影响所有查询结果
+- 正确显示各个域名的查询结果
+
+## 修复代码位置
+
+- **核心修改文件**:`/root/dns/dns/server.go`
+- **关键函数**:`mergeResponses`
+- **修改内容**:修复响应合并逻辑,正确处理 Rcode 字段
+
+## 测试方法
+
+1. 使用 nslookup 测试不同域名
+2. 检查是否每个域名都显示正确的查询结果
+3. 验证 www.evilsnssdk.com 返回 NXDOMAIN,而其他域名返回成功
+4. 检查日志中是否还有大量错误信息
+
+这个修复将彻底解决所有查询都显示同一个 NXDOMAIN 错误的问题!
\ No newline at end of file
diff --git a/.trae/documents/修复Web页面解析类型卡片百分比显示问题.md b/.trae/documents/修复Web页面解析类型卡片百分比显示问题.md
new file mode 100644
index 0000000..73ab1a5
--- /dev/null
+++ b/.trae/documents/修复Web页面解析类型卡片百分比显示问题.md
@@ -0,0 +1,58 @@
+## 实现计划
+
+### 1. 配置文件修改
+
+* **修改`/root/dns/config/config.go`**:
+
+ * 在`DNSConfig`结构体中添加`PrefixDomain []string`字段,用于支持search domain功能
+
+ * 在`LoadConfig`函数中添加`prefixDomain`的默认值处理
+
+### 2. DNS请求处理逻辑修改
+
+* **修改`/root/dns/dns/server.go`中的`forwardDNSRequestWithCache`函数**:
+
+ * 强化domainSpecificDNS逻辑,确保当域名匹配时,只使用指定的DNS服务器
+
+ * 移除向DNSSEC专用服务器发送请求的逻辑,当域名匹配domainSpecificDNS时
+
+ * 确保匹配域名的DNS查询结果不会被其他DNS服务器的响应覆盖
+
+* **修改`/root/dns/dns/server.go`中的`handleDNSRequest`函数**:
+
+ * 实现search domain功能,当直接查询失败时,尝试添加prefixDomain中指定的域名前缀
+
+ * 按照/etc/resolv.conf中的search domain逻辑处理查询请求
+
+### 3. 配置文件示例更新
+
+* **更新配置文件示例**:
+
+ * 添加`prefixDomain`配置项示例
+
+ * 说明search domain功能的使用方法
+
+### 4. 测试验证
+
+* 测试domainSpecificDNS强制使用功能,确保匹配的域名只使用指定的DNS服务器
+
+* 测试search domain功能,确保能够正确处理带前缀的域名查询
+
+* 测试不同配置组合下的功能正确性
+
+## 预期效果
+
+1. 当域名匹配domainSpecificDNS配置时,无论DNSSEC是否启用,只使用指定的DNS服务器
+2. 支持search domain功能,能够自动尝试添加配置的域名前缀
+3. 配置简单直观,与/etc/resolv.conf的search domain行兼容
+
+## 实现要点
+
+* 确保domainSpecificDNS配置的优先级最高
+
+* 实现高效的search domain查询逻辑,避免不必要的网络请求
+
+* 保持代码的可读性和可维护性
+
+* 确保与现有功能的兼容性
+
diff --git a/.trae/documents/修复picsum.photos域名匹配问题.md b/.trae/documents/修复picsum.photos域名匹配问题.md
new file mode 100644
index 0000000..948acbc
--- /dev/null
+++ b/.trae/documents/修复picsum.photos域名匹配问题.md
@@ -0,0 +1,20 @@
+# 修复picsum.photos域名匹配问题
+
+## 问题分析
+当DNS查询picsum.photos时,实际传递给`getDomainInfo()`函数的域名可能带有末尾点(如"picsum.photos."),而domain-info.json中存储的是没有末尾点的域名(如"picsum.photos")。在原始的`isDomainMatch()`函数中,直接比较两个域名,导致匹配失败。
+
+## 解决方案
+修改`isDomainMatch()`函数,在域名比较前对两个域名进行规范化处理,去除末尾的点,确保匹配准确性。
+
+## 实施步骤
+1. 修改`isDomainMatch()`函数,在域名比较前对`urlDomain`和`targetDomain`进行规范化处理,去除末尾的点
+2. 确保无论是完整URL还是纯域名,都能正确匹配
+3. 测试修改后的代码,确保picsum.photos域名能被正确匹配
+
+## 修改内容
+- 文件:`/root/dns/static/js/logs.js`
+- 函数:`isDomainMatch()`
+- 修改点:在域名比较前,去除`urlDomain`和`targetDomain`末尾的点
+
+## 预期效果
+修改后,picsum.photos域名(无论是否带有末尾点)都能被正确匹配到domain-info.json中的条目。
\ No newline at end of file
diff --git a/.trae/documents/修复域名信息为空的问题.md b/.trae/documents/修复域名信息为空的问题.md
new file mode 100644
index 0000000..f5cc7b2
--- /dev/null
+++ b/.trae/documents/修复域名信息为空的问题.md
@@ -0,0 +1,56 @@
+## 问题分析
+
+域名信息显示为空,特别是对于`wx.qq.com`这样的域名,虽然在`domain-info.json`中已配置,但日志详情中显示为空。
+
+### 可能的原因
+
+1. **文件路径问题**:`loadDomainInfoDatabase`函数使用`fetch('/domain-info/domains/domain-info.json')`加载文件,但服务器可能没有正确配置该路径,导致加载失败。
+
+2. **代码逻辑问题**:虽然代码逻辑看起来正确,但可能存在一些边缘情况没有处理好。
+
+3. **调试信息不足**:代码中缺少调试信息,难以定位具体问题。
+
+## 解决方案
+
+### 1. 添加调试日志
+
+在`loadDomainInfoDatabase`和`getDomainInfo`函数中添加调试日志,以便定位问题:
+
+- 在`loadDomainInfoDatabase`函数中,添加日志记录文件加载状态和结果
+- 在`getDomainInfo`函数中,添加日志记录域名匹配过程
+- 在`isDomainMatch`函数中,添加日志记录URL匹配细节
+
+### 2. 检查并修复文件路径
+
+确保`domain-info.json`文件能被正确访问:
+
+- 检查服务器配置,确保`/domain-info/domains/domain-info.json`路径指向正确的文件
+- 或者修改代码中的路径,使用绝对路径或正确的相对路径
+
+### 3. 增强错误处理
+
+在`loadDomainInfoDatabase`函数中增强错误处理,提供更详细的错误信息:
+
+- 记录完整的错误信息
+- 在控制台显示友好的错误提示
+- 考虑添加重试机制
+
+### 4. 优化域名匹配逻辑
+
+虽然代码逻辑看起来正确,但可以进一步优化:
+
+- 确保所有URL都被正确处理,无论是否包含协议
+- 增强主域名提取逻辑,处理更多特殊情况
+- 考虑添加缓存机制,提高匹配效率
+
+## 实施步骤
+
+1. 修改`loadDomainInfoDatabase`函数,添加调试日志和增强错误处理
+2. 修改`getDomainInfo`函数,添加调试日志
+3. 修改`isDomainMatch`函数,添加调试日志
+4. 测试修复效果
+5. 根据测试结果进一步优化
+
+## 预期效果
+
+修复后,域名信息将能正确显示在日志详情中,特别是对于`wx.qq.com`等已配置的域名。
\ No newline at end of file
diff --git a/.trae/documents/修复拦截和放行功能.md b/.trae/documents/修复拦截和放行功能.md
new file mode 100644
index 0000000..8ff0d8e
--- /dev/null
+++ b/.trae/documents/修复拦截和放行功能.md
@@ -0,0 +1,44 @@
+1. **修复规则添加逻辑**:
+
+ * 修改`blockDomain`函数,使用正确的规则格式(如 `||domain^`)
+
+ * 修改`unblockDomain`函数,使用正确的放行规则格式(如 `@@||domain^`)
+
+ * 确保规则经过预处理后再发送到API
+
+2. **更新本地规则列表**:
+
+ * 添加规则成功后,更新`rules`和`filteredRules`数组
+
+ * 调用`renderRulesList`函数重新渲染规则列表
+
+ * 更新规则数量统计
+
+3. **确保规则同步**:
+
+ * 验证规则是否正确添加到本地规则列表
+
+ * 确保日志页面和规则页面的数据一致性
+
+4. **优化用户体验**:
+
+ * 改进操作反馈,显示更详细的成功/失败信息
+
+ * 确保规则添加后立即在日志中反映状态变化
+
+5. **测试功能**:
+
+ * 测试拦截域名功能
+
+ * 测试放行域名功能
+
+ * 验证规则是否正确添加到本地规则列表
+
+ * 验证日志状态是否正确更新
+
+**修改文件**:
+
+* `/root/dns/static/js/logs.js`:修复`blockDomain`和`unblockDomain`函数
+
+* 确保与`/root/dns/static/js/modules/rules.js`的规则处理逻辑保持一致
+
diff --git a/.trae/documents/修复拦截放行功能.md b/.trae/documents/修复拦截放行功能.md
new file mode 100644
index 0000000..329c3d8
--- /dev/null
+++ b/.trae/documents/修复拦截放行功能.md
@@ -0,0 +1,56 @@
+1. **确认apiRequest函数的使用**:
+
+ * 检查logs.js中apiRequest函数的来源和调用方式
+
+ * 确保使用正确的API\_BASE\_URL和异步apiRequest函数
+
+2. **修复API调用路径**:
+
+ * 确保拦截和放行功能使用正确的API路径
+
+ * 验证与规则管理模块的API调用一致性
+
+3. **改进规则格式**:
+
+ * 确保拦截和放行规则使用正确的格式
+
+ * 与规则管理模块的规则处理逻辑保持一致
+
+4. **添加详细的错误处理**:
+
+ * 在apiRequest调用中添加详细的日志
+
+ * 改进错误处理,显示更详细的错误信息
+
+ * 添加调试信息,以便跟踪问题
+
+5. **确保跨文件函数调用正确**:
+
+ * 确保logs.js能正确访问apiRequest函数
+
+ * 验证全局变量和函数的可用性
+
+6. **测试功能**:
+
+ * 测试拦截域名功能
+
+ * 测试放行域名功能
+
+ * 验证规则是否正确添加到本地规则列表
+
+ * 验证日志状态是否正确更新
+
+**修改文件**:
+
+* `/root/dns/static/js/logs.js`:修复apiRequest函数调用和API路径
+
+**预期效果**:
+
+* 拦截功能正常工作,规则正确添加到本地规则列表
+
+* 放行功能正常工作,规则正确添加到本地规则列表
+
+* 日志状态立即更新,显示正确的拦截/放行状态
+
+* 显示详细的操作反馈和错误信息
+
diff --git a/.trae/documents/修复日志显示undefined问题.md b/.trae/documents/修复日志显示undefined问题.md
new file mode 100644
index 0000000..70d720b
--- /dev/null
+++ b/.trae/documents/修复日志显示undefined问题.md
@@ -0,0 +1,67 @@
+## 问题分析
+
+从截图中可以看到,日志表格显示了"Invalid Date"、"undefined"等错误值,这表明前端代码在解析API返回的JSON数据时出现了问题。
+
+## 根本原因
+
+1. 后端`QueryLog`结构体的JSON标签使用了小写字段名(如`json:"timestamp"`、`json:"clientIP"`等)
+2. 前端代码使用了大写的字段名(如`log.Timestamp`、`log.ClientIP`等)来访问数据
+3. 字段名大小写不匹配导致前端无法正确解析API返回的数据
+
+## 修复方案
+
+修改前端代码,使用正确的小写字段名来访问API返回的数据,与后端返回的JSON格式匹配。
+
+## 具体修改点
+
+1. **`updateLogsTable`函数**(约435-621行):
+
+ * 将`log.Timestamp`改为`log.timestamp`
+
+ * 将`log.ClientIP`改为`log.clientIP`
+
+ * 将`log.Domain`改为`log.domain`
+
+ * 将`log.QueryType`改为`log.queryType`
+
+ * 将`log.ResponseTime`改为`log.responseTime`
+
+ * 将`log.Result`改为`log.result`
+
+ * 将`log.BlockRule`改为`log.blockRule`
+
+ * 将`log.FromCache`改为`log.fromCache`
+
+ * 将`log.DNSSEC`改为`log.dnssec`
+
+ * 将`log.EDNS`改为`log.edns`
+
+ * 将`log.DNSServer`改为`log.dnsServer`
+
+ * 将`log.DNSSECServer`改为`log.dnssecServer`
+
+ * 将`log.ResponseCode`改为`log.responseCode`
+
+2. **`showLogDetailModal`函数**(约1450-1597行):
+
+ * 同样修改所有字段名的大小写
+
+3. **其他可能的访问点**:
+
+ * 检查并修改任何其他访问日志数据的地方
+
+## 修复原则
+
+* 保持前端代码与后端API返回的JSON格式一致
+
+* 遵循REST API的最佳实践,使用小写字段名
+
+* 确保所有日志数据访问点都得到修复
+
+## 验证方法
+
+1. 修复代码后,重新编译并运行服务器
+2. 访问日志页面,检查日志数据是否正确显示
+3. 测试不同类型的日志(允许、屏蔽、错误),确保都能正确显示
+4. 测试日志详情模态框,确保所有字段都能正确显示
+
diff --git a/.trae/documents/修复添加自定义规则后需要重启服务器的问题.md b/.trae/documents/修复添加自定义规则后需要重启服务器的问题.md
new file mode 100644
index 0000000..d146868
--- /dev/null
+++ b/.trae/documents/修复添加自定义规则后需要重启服务器的问题.md
@@ -0,0 +1,21 @@
+1. **问题分析**:
+ - 当添加自定义规则时,规则被添加到内存中并保存到文件
+ - 但由于DNS缓存的存在,如果该域名的DNS响应已经被缓存,那么在缓存过期之前,DNS服务器会直接返回缓存的响应,而不会重新检查规则
+ - 这就导致了添加规则后需要重启服务器才能生效的问题
+
+2. **修复方案**:
+ - 当添加或删除自定义规则时,清空DNS缓存,这样新的规则会立即生效
+ - 这样,当客户端再次请求该域名时,DNS服务器会重新检查规则,而不是直接返回缓存的响应
+
+3. **修复步骤**:
+ - 修改HTTP API处理函数,在添加或删除规则后,清空DNS缓存
+ - 或者,修改ShieldManager的AddRule和RemoveRule方法,添加清空DNS缓存的逻辑
+ - 测试修复后的功能,确保添加规则后无需重启服务器即可生效
+
+4. **预期结果**:
+ - 添加自定义规则后,规则会立即生效,无需重启服务器
+ - 重启服务器后,之前添加的自定义规则仍然存在
+
+5. **具体实现**:
+ - 在`/root/dns/http/server.go`中,在添加或删除规则的API处理函数后,调用DNS缓存的Clear方法
+ - 这样,当添加或删除规则时,DNS缓存会被清空,新的规则会立即生效
\ No newline at end of file
diff --git a/.trae/documents/修改gfwList配置从内容到文件路径.md b/.trae/documents/修改gfwList配置从内容到文件路径.md
new file mode 100644
index 0000000..327667f
--- /dev/null
+++ b/.trae/documents/修改gfwList配置从内容到文件路径.md
@@ -0,0 +1,79 @@
+## DNS服务器性能优化方案
+
+### 问题分析
+
+1. **并行查询模式**:当前配置使用`parallel`模式,会等待所有上游服务器响应后才返回,受最慢服务器影响
+2. **DNSSEC验证开销**:启用了DNSSEC验证,增加了额外的计算和网络请求
+3. **过多上游服务器**:DNSSEC上游服务器多达5个,响应时间差异大
+4. **调试级别日志**:`debug`级别日志记录大量信息,占用CPU和I/O资源
+5. **缓存TTL过短**:10秒的缓存TTL导致频繁向上游请求
+6. **黑名单规则过多**:14个启用的黑名单,每次请求都需要检查
+
+### 优化方案
+
+#### 1. 修改查询模式为快速返回
+
+* 将`queryMode`从`parallel`改为`fastest-ip`或优化默认模式
+
+* 快速返回模式会返回第一个有效响应,而不是等待所有响应
+
+#### 2. 优化DNSSEC配置
+
+* 减少DNSSEC上游服务器数量,只保留2-3个可靠的
+
+* 对国内域名禁用DNSSEC验证(已配置部分,可扩展)
+
+#### 3. 调整缓存策略
+
+* 增加`cacheTTL`到60秒或更高,减少上游请求频率
+
+* 优化缓存实现,减少锁竞争
+
+#### 4. 降低日志级别
+
+* 将日志级别从`debug`改为`info`或`warn`,减少日志写入开销
+
+#### 5. 优化黑名单处理
+
+* 合并重复的黑名单规则
+
+* 考虑使用更高效的域名匹配算法
+
+#### 6. 代码优化
+
+* 减少DNSSEC验证的重复调用
+
+* 优化响应合并逻辑,避免不必要的计算
+
+* 调整超时设置,避免过长等待
+
+### 具体修改点
+
+1. **config.json**:
+
+ * 修改`queryMode`为`fastest-ip`
+
+ * 减少`dnssecUpstreamDNS`数量
+
+ * 增加`cacheTTL`到60
+
+ * 将日志级别改为`info`
+
+2. **dns/server.go**:
+
+ * 优化`forwardDNSRequestWithCache`函数,减少DNSSEC重复验证
+
+ * 优化响应合并逻辑,避免不必要的计算
+
+ * 调整并行模式的超时处理
+
+### 预期效果
+
+* 减少响应时间,从当前的秒级降低到毫秒级
+
+* 减少CPU和I/O资源占用
+
+* 提高并发处理能力
+
+* 保持DNS解析的准确性和可靠性
+
diff --git a/.trae/documents/内存泄露问题.md b/.trae/documents/内存泄露问题.md
new file mode 100644
index 0000000..1fdcf3a
--- /dev/null
+++ b/.trae/documents/内存泄露问题.md
@@ -0,0 +1,47 @@
+# 内存泄漏问题
+## 1. IP地理位置缓存泄漏
+问题:IP地理位置缓存(```ipGeolocationCache```)缺少全局过期清理机制,仅在获取新数据时检查单条缓存过期,导致长期运行后内存无限增长。
+
+修复建议:
+
+```Go
+// 在Server结构体中添加清理定时器ipGeolocationCacheCleanupTicker *time.Ticker// 在NewServer函数中启动清理协程go s.startIPGeolocationCacheCleanup()// 添加清理函数func (s *Server) startIPGeolocationCacheCleanup() { ticker := time.NewTicker(time. Hour) // 每小时清理一次 defer ticker.Stop() for { select { case <-ticker.C: s. cleanupExpiredIPGeolocat ionCache() case <-s.ctx.Done(): return } }}func (s *Server) cleanupExpiredIPGeolocationCache() { now := time.Now() s.ipGeolocationCacheMutex.Lock() defer s.ipGeolocationCacheMutex. Unlock() for ip, geo := range s. ipGeolocationCache { if now.After(geo.Expiry) { delete(s. ipGeolocationCache, ip) } }}
+```
+# 2. DNS缓存无大小限制
+问题:DNS缓存(DNSCache)仅按TTL过期,无最大条目数限制,可能导致内存无限增长。
+
+修复建议:
+
+```Go
+// 在DNSCache结构体中添加最大大小限制maxSize int// 在Set方法中添加大小限制检查func (c *DNSCache) Set(qName string, qType uint16, response *dns.Msg, ttl time.Duration) { // 现有代码... c.mutex.Lock() defer c.mutex.Unlock() c.cache[key] = item // 检查并清理超过最大大小的缓存 if c.maxSize > 0 && len(c. cache) > c.maxSize { c.evictOldest() // 实现LRU或 随机淘汰策略 }}
+```
+## 1. 域名检查函数性能优化
+问题:```CheckDomainBlockDetails```函数中重复的子域名检查和大量正则表达式匹配导致CPU使用率过高。
+
+修复建议:
+
+```Go
+// 合并本地和远程规则检查,减少循环次数func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interface{} { // 预处理域名 // ... // 合并域名排除规则检查 if m.domainExceptions[domain] { result["excluded"] = true result["excludeRule"] = m. domainExceptionsOriginal [domain] result["excludeRuleType"] = "exact_domain" result["blocksource"] = m. domainExceptionsSource [domain] return result } // 合并子域名检查 parts := strings.Split(domain, ".") for i := 0; i < len(parts)-1; i ++ { subdomain := strings.Join (parts[i:], ".") if m.domainExceptions [subdomain] { result["excluded"] = true result["excludeRule"] = m. domainExceptionsOriginal [subdomain] result ["excludeRuleType"] = "subdomain" result["blocksource"] = m.domainExceptionsSource [subdomain] return result } } // 合并正则表达式检查 for _, re := range m. regexExceptions { if re.pattern.MatchString (domain) { result["excluded"] = true result["excludeRule"] = re.original result ["excludeRuleType"] = "regex" result["blocksource"] = re.source return result } } // 阻止规则检查采用相同优化方式 // ...}
+```
+## 2. 减少锁持有时间
+问题:```CheckDomainBlockDetails```函数持有读锁时间过长,影响并发性能。
+修复建议:
+```Go
+// 采用更细粒度的锁或读写分离策略func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interface{} { // 只在需要访问共享数据时加锁 m.rulesMutex.RLock() domainExists := m. domainExceptions[domain] m.rulesMutex.RUnlock() if domainExists { m.rulesMutex.RLock() // 获取详细信息 m.rulesMutex.RUnlock() return result } // 其他检查采用相同方式}
+```
+# 其他优化建议
+## 1. DNS缓存优化
+实现LRU缓存淘汰策略,限制最大缓存大小
+定期统计缓存命中率,调整缓存策略
+## 2. WebSocket连接管理
+实现连接池管理,限制最大连接数
+添加连接心跳检测机制,及时清理无效连接
+## 3. 并行查询优化
+限制同时发起的上游查询数量,避免资源耗尽
+实现超时控制,防止长时间阻塞
+## 4. 正则表达式优化
+预编译所有正则表达式规则
+合并相似规则,减少正则表达式数量
+使用更高效的规则匹配算法
+# 总结
+通过实施上述修复建议和优化方案,可以有效解决DNS服务器的内存泄漏问题和CPU性能瓶颈,提高系统稳定性和响应速度。建议优先修复IP地理位置缓存泄漏和域名检查函数性能问题,这些是当前最严重的问题。
\ No newline at end of file
diff --git a/.trae/documents/在查询日志详情界面增加操作列.md b/.trae/documents/在查询日志详情界面增加操作列.md
new file mode 100644
index 0000000..e0a90c4
--- /dev/null
+++ b/.trae/documents/在查询日志详情界面增加操作列.md
@@ -0,0 +1,69 @@
+## 实现计划
+
+### 1. 分析当前代码结构
+
+* **HTML结构**:日志表格在`updateLogsTable`函数中动态生成
+* **数据处理**:每条日志包含`Result`字段,表示查询结果(allowed, blocked, error)
+* **API需求**:需要拦截/放行域名的API接口
+
+### 2. 修改内容
+
+#### 2.1 修改`updateLogsTable`函数
+
+* 在表格中添加操作列
+* 根据`log.Result`字段决定显示哪个按钮:
+ - 如果`log.Result`为`blocked`,显示"放行"按钮
+ - 否则,显示"拦截"按钮
+* 为按钮添加点击事件处理函数
+
+#### 2.2 实现拦截/放行功能
+
+* 添加`blockDomain`函数:调用API拦截域名
+* 添加`allowDomain`函数:调用API放行域名
+* 实现按钮点击事件的处理逻辑
+* 添加操作成功后的反馈机制
+
+#### 2.3 更新表格头部
+
+* 在表格头部添加"操作"列
+* 确保表格的`colspan`属性正确更新
+
+### 3. 实现细节
+
+* **按钮样式**:使用现有的样式类,保持界面一致性
+* **事件绑定**:为动态生成的按钮绑定点击事件
+* **API调用**:使用现有的`apiRequest`函数进行API调用
+* **反馈机制**:操作成功后显示提示信息
+* **数据刷新**:操作完成后刷新日志列表,显示最新状态
+
+### 4. 预期效果
+
+* 日志详情表格右侧增加"操作"列
+* 对于被拦截的域名,显示"放行"按钮
+* 对于未被拦截的域名,显示"拦截"按钮
+* 点击按钮后,成功执行相应操作并刷新列表
+* 操作过程中显示反馈信息
+
+### 5. 实现步骤
+
+1. 修改`updateLogsTable`函数,添加操作列
+2. 更新表格头部,添加"操作"列
+3. 实现`blockDomain`和`allowDomain`函数
+4. 为按钮绑定点击事件
+5. 添加操作反馈机制
+6. 测试功能完整性
+
+## 关键代码修改点
+
+* **`updateLogsTable`函数**:在生成表格行时添加操作列
+* **表格头部**:添加"操作"列标题
+* **新增函数**:`blockDomain`和`allowDomain`
+* **事件绑定**:为动态生成的按钮添加点击事件处理
+
+## 技术要点
+
+* 动态生成元素的事件绑定
+* API调用的错误处理
+* 操作反馈机制的实现
+* 数据刷新逻辑
+* 保持界面样式一致性
diff --git a/.trae/documents/实现domainSpecificDNS强制使用和search域支持.md b/.trae/documents/实现domainSpecificDNS强制使用和search域支持.md
index 41dca6e..73ab1a5 100644
--- a/.trae/documents/实现domainSpecificDNS强制使用和search域支持.md
+++ b/.trae/documents/实现domainSpecificDNS强制使用和search域支持.md
@@ -1,37 +1,58 @@
## 实现计划
### 1. 配置文件修改
-- **修改`/root/dns/config/config.go`**:
- - 在`DNSConfig`结构体中添加`PrefixDomain []string`字段,用于支持search domain功能
- - 在`LoadConfig`函数中添加`prefixDomain`的默认值处理
+
+* **修改`/root/dns/config/config.go`**:
+
+ * 在`DNSConfig`结构体中添加`PrefixDomain []string`字段,用于支持search domain功能
+
+ * 在`LoadConfig`函数中添加`prefixDomain`的默认值处理
### 2. DNS请求处理逻辑修改
-- **修改`/root/dns/dns/server.go`中的`forwardDNSRequestWithCache`函数**:
- - 强化domainSpecificDNS逻辑,确保当域名匹配时,只使用指定的DNS服务器
- - 移除向DNSSEC专用服务器发送请求的逻辑,当域名匹配domainSpecificDNS时
- - 确保匹配域名的DNS查询结果不会被其他DNS服务器的响应覆盖
-- **修改`/root/dns/dns/server.go`中的`handleDNSRequest`函数**:
- - 实现search domain功能,当直接查询失败时,尝试添加prefixDomain中指定的域名前缀
- - 按照/etc/resolv.conf中的search domain逻辑处理查询请求
+* **修改`/root/dns/dns/server.go`中的`forwardDNSRequestWithCache`函数**:
+
+ * 强化domainSpecificDNS逻辑,确保当域名匹配时,只使用指定的DNS服务器
+
+ * 移除向DNSSEC专用服务器发送请求的逻辑,当域名匹配domainSpecificDNS时
+
+ * 确保匹配域名的DNS查询结果不会被其他DNS服务器的响应覆盖
+
+* **修改`/root/dns/dns/server.go`中的`handleDNSRequest`函数**:
+
+ * 实现search domain功能,当直接查询失败时,尝试添加prefixDomain中指定的域名前缀
+
+ * 按照/etc/resolv.conf中的search domain逻辑处理查询请求
### 3. 配置文件示例更新
-- **更新配置文件示例**:
- - 添加`prefixDomain`配置项示例
- - 说明search domain功能的使用方法
+
+* **更新配置文件示例**:
+
+ * 添加`prefixDomain`配置项示例
+
+ * 说明search domain功能的使用方法
### 4. 测试验证
-- 测试domainSpecificDNS强制使用功能,确保匹配的域名只使用指定的DNS服务器
-- 测试search domain功能,确保能够正确处理带前缀的域名查询
-- 测试不同配置组合下的功能正确性
+
+* 测试domainSpecificDNS强制使用功能,确保匹配的域名只使用指定的DNS服务器
+
+* 测试search domain功能,确保能够正确处理带前缀的域名查询
+
+* 测试不同配置组合下的功能正确性
## 预期效果
+
1. 当域名匹配domainSpecificDNS配置时,无论DNSSEC是否启用,只使用指定的DNS服务器
2. 支持search domain功能,能够自动尝试添加配置的域名前缀
3. 配置简单直观,与/etc/resolv.conf的search domain行兼容
## 实现要点
-- 确保domainSpecificDNS配置的优先级最高
-- 实现高效的search domain查询逻辑,避免不必要的网络请求
-- 保持代码的可读性和可维护性
-- 确保与现有功能的兼容性
\ No newline at end of file
+
+* 确保domainSpecificDNS配置的优先级最高
+
+* 实现高效的search domain查询逻辑,避免不必要的网络请求
+
+* 保持代码的可读性和可维护性
+
+* 确保与现有功能的兼容性
+
diff --git a/.trae/documents/实现域名信息显示.md b/.trae/documents/实现域名信息显示.md
new file mode 100644
index 0000000..a62276b
--- /dev/null
+++ b/.trae/documents/实现域名信息显示.md
@@ -0,0 +1,31 @@
+# 实现域名信息显示
+
+## 需求分析
+用户需要在日志详情页面的红色框区域显示域名信息,包括:
+- 网站名称(带图标)
+- 网站类别
+- 所属公司
+
+## 代码分析
+1. 目前代码已经包含了获取域名信息的功能:
+ - `loadDomainInfoDatabase` 函数加载域名信息数据库
+ - `getDomainInfo` 函数根据域名查找对应的网站信息
+ - `showLogDetailModal` 函数显示日志详情,包括域名信息
+
+2. 域名信息已经在日志详情模态框中显示,但需要确保它在指定的红色框区域正确显示
+
+## 实现计划
+1. **优化域名信息显示样式**:调整 `showLogDetailModal` 函数中域名信息的HTML结构和样式,确保它在红色框内正确显示
+2. **增强域名匹配逻辑**:改进 `getDomainInfo` 函数,提高域名匹配的准确性
+3. **添加错误处理**:确保在域名信息加载失败时,页面能够优雅处理
+4. **测试功能**:验证域名信息能够正确显示在红色框区域
+
+## 预期效果
+- 当日志详情弹窗打开时,在红色框区域显示域名的完整信息
+- 显示格式:
+ ```
+ [图标] 网站名称
+ 类别: [类别名称]
+ 所属公司: [公司名称]
+ ```
+- 如果没有匹配的域名信息,显示"无"
\ No newline at end of file
diff --git a/.trae/documents/实现客户端IP地址位置显示.md b/.trae/documents/实现客户端IP地址位置显示.md
new file mode 100644
index 0000000..815bde8
--- /dev/null
+++ b/.trae/documents/实现客户端IP地址位置显示.md
@@ -0,0 +1,18 @@
+1. **更新getIpGeolocation函数**:修改函数逻辑,使用API响应中的addr字段来显示完整的地理位置信息
+2. **处理编码问题**:确保正确处理中文编码
+3. **维护缓存机制**:保留现有的24小时缓存机制,提高性能
+4. **测试不同IP类型**:确保公网IP和内网IP都能正确显示
+
+### 具体修改点
+- **文件**:`/root/dns/static/js/logs.js`
+- **函数**:`getIpGeolocation`
+- **修改内容**:
+ - 从API响应中提取`addr`字段
+ - 直接使用addr字段作为完整的地理位置信息
+ - 保持对私有IP的特殊处理
+ - 维护现有的缓存机制
+
+### 预期效果
+- 公网IP显示格式:"IP地址 (完整地理位置,来自addr字段)"
+- 内网IP显示格式:"IP地址 (内网 内网)"
+- 未知IP显示格式:"IP地址 (未知 未知)"
\ No newline at end of file
diff --git a/.trae/documents/彻底解决DNS服务器Server Failed问题.md b/.trae/documents/彻底解决DNS服务器Server Failed问题.md
new file mode 100644
index 0000000..2e82b61
--- /dev/null
+++ b/.trae/documents/彻底解决DNS服务器Server Failed问题.md
@@ -0,0 +1,81 @@
+## 问题分析
+
+服务器出现"Server Failed"问题的主要原因是:
+
+1. **DNS查询超时设置过短**:配置文件中timeout设置为5ms,导致几乎所有DNS查询都超时失败
+2. **parallel模式响应处理逻辑缺陷**:在某些情况下,即使主DNS服务器有响应,也可能因为DNSSEC验证或其他原因被忽略
+3. **DNSSEC处理影响正常查询**:启用DNSSEC后,系统会优先选择带DNSSEC记录的响应,但如果所有DNSSEC服务器都失败,可能导致没有响应
+
+## 解决方案
+
+### 1. 确保超时设置正确
+
+* **已修复**:将配置文件中的timeout从5ms修改为5000ms
+
+* 验证代码中默认值设置正确,确保配置文件加载时能正确应用默认值
+
+### 2. 优化parallel模式响应处理逻辑
+
+**问题**:当前parallel模式下,只有当响应是成功(RcodeSuccess)或NXDOMAIN(RcodeNameError)时才会被考虑作为最佳响应,且优先选择带DNSSEC记录的响应。
+
+**修复**:
+
+* 修改`dns/server.go`中的parallel模式处理逻辑
+
+* 确保用户配置的主DNS服务器(upstreamDNS)响应优先被使用,即使它没有DNSSEC记录
+
+* 只有当主DNS服务器完全失败时,才考虑使用DNSSEC专用服务器的响应
+
+* 确保在所有情况下都能返回至少一个备选响应
+
+### 3. 增强错误处理机制
+
+**问题**:当所有DNS服务器都返回非成功响应时,系统可能无法找到合适的响应返回给客户端。
+
+**修复**:
+
+* 在`dns/server.go`中增强错误处理
+
+* 确保即使所有上游服务器都失败,也能返回一个有效的DNS响应
+
+* 优化备选响应的选择逻辑,确保总有一个可用的响应
+
+* 添加更详细的日志记录,便于调试
+
+### 4. 优化DNSSEC处理逻辑
+
+**问题**:当前DNSSEC处理逻辑可能导致主DNS服务器的响应被忽略。
+
+**修复**:
+
+* 调整DNSSEC专用服务器请求逻辑,只有当主DNS服务器完全失败时才使用
+
+* 确保DNSSEC验证失败不会影响正常的DNS查询结果
+
+* 优化DNSSEC记录检查逻辑,确保准确判断DNSSEC记录
+
+## 修复步骤
+
+1. **验证超时设置**:确保配置文件中的timeout已设置为5000ms
+2. **修改parallel模式响应处理**:优化`dns/server.go`中parallel模式的响应选择逻辑
+3. **增强备选响应机制**:确保总有一个可用的响应返回给客户端
+4. **优化DNSSEC处理**:调整DNSSEC专用服务器请求时机和优先级
+5. **添加详细日志**:增加调试日志,便于后续问题定位
+6. **测试验证**:重启服务器并测试DNS查询功能
+
+## 预期效果
+
+* DNS查询成功率显著提高,不再出现大量"Server Failed"错误
+
+* 主DNS服务器响应优先被使用,确保查询效率
+
+* DNSSEC功能正常工作,同时不影响正常DNS查询
+
+* 系统更加稳定,能够处理各种异常情况
+
+## 关键文件修改
+
+* `/root/dns/config.json`:确保timeout设置正确
+
+* `/root/dns/dns/server.go`:优化parallel模式响应处理、错误处理和DNSSEC逻辑
+
diff --git a/.trae/documents/更新CHANGELOG.md记录最近的修复.md b/.trae/documents/更新CHANGELOG.md记录最近的修复.md
new file mode 100644
index 0000000..c7ac4ee
--- /dev/null
+++ b/.trae/documents/更新CHANGELOG.md记录最近的修复.md
@@ -0,0 +1,38 @@
+1. **问题分析**:
+
+ * 项目已经有一个CHANGELOG.md文件,遵循Keep a Changelog格式
+
+ * 最新版本是\[1.1.3],发布于2025-12-19
+
+ * 我们需要添加新的版本条目,记录最近的修复
+
+2. **修复内容**:
+
+ * 修复了规则优先级问题:确保自定义规则优先于远程规则
+
+ * 修复了添加自定义规则后需要重启服务器的问题:通过在添加或删除规则后清空DNS缓存实现
+
+3. **更新步骤**:
+
+ * 在CHANGELOG.md文件中添加一个新的版本条目\[1.1.4],日期为2025-12-21
+
+ * 在该版本下添加修复的内容
+
+ * 确保格式符合CHANGELOG的要求
+
+4. **预期结果**:
+
+ * CHANGELOG.md文件将包含最新的修复记录
+
+ * 版本号将更新为\[1.1.4]
+
+ * 修复内容将被清晰地记录在CHANGELOG中
+
+5. **具体实现**:
+
+ * 在CHANGELOG.md文件的开头添加新的版本条目
+
+ * 使用### 修复标题记录修复的内容
+
+ * 清晰描述每个修复的问题和解决方案
+
diff --git a/.trae/documents/配置文件移除文件路径定义并固定文件位置.md b/.trae/documents/配置文件移除文件路径定义并固定文件位置.md
new file mode 100644
index 0000000..e0f824d
--- /dev/null
+++ b/.trae/documents/配置文件移除文件路径定义并固定文件位置.md
@@ -0,0 +1,46 @@
+1. **修改配置结构体定义**:
+
+ * 从`config/config.go`中的`DNSConfig`结构体移除`StatsFile`字段
+
+ * 从`ShieldConfig`结构体移除`LocalRulesFile`、`HostsFile`、`StatsFile`、`RemoteRulesCacheDir`字段
+
+ * 从`LogConfig`结构体移除`File`字段
+
+2. **修改配置加载逻辑**:
+
+ * 在`config/config.go`的`LoadConfig`函数中,移除对上述字段的默认值设置
+
+3. **修改代码中使用配置的地方**:
+
+ * `dns/server.go`:将`s.config.StatsFile`替换为硬编码的`"data/stats.json"`
+
+ * `shield/manager.go`:将`m.config.StatsFile`替换为硬编码的`"data/shield_stats.json"`
+
+ * 其他使用这些配置项的地方也需要相应修改
+
+4. **修改默认配置生成**:
+
+ * 在`main.go`的`createDefaultConfig`函数中,移除所有文件路径相关的配置项
+
+5. **修改Web界面配置处理**:
+
+ * 修改`static/js/config.js`,移除对这些文件路径配置项的处理
+
+6. **更新配置文件**:
+
+ * 修改`config.json`,移除所有文件路径相关的配置项
+
+固定的文件位置:
+
+* 日志文件:`logs/dns-server.log`
+
+* DNS统计文件:`data/stats.json`
+
+* Shield统计文件:`data/shield_stats.json`
+
+* 本地规则文件:`data/rules.txt`
+
+* Hosts文件:`data/hosts.txt`
+
+* 远程规则缓存目录:`data/remote_rules`
+
diff --git a/.trae/documents/重写DNS日志详情弹窗.md b/.trae/documents/重写DNS日志详情弹窗.md
new file mode 100644
index 0000000..49f1716
--- /dev/null
+++ b/.trae/documents/重写DNS日志详情弹窗.md
@@ -0,0 +1,399 @@
+## 重写DNS日志详情弹窗方案
+
+### 1. 问题分析
+当前弹窗代码存在以下问题:
+- 代码结构复杂,大量嵌套条件和重复逻辑
+- 样式不够现代,布局不够灵活
+- 响应式设计不足,在大屏幕上显示效果不佳
+- 代码冗余,重复的记录解析逻辑
+- 可维护性差,HTML结构直接写在JavaScript字符串中
+
+### 2. 设计思路
+- **组件化设计**:将弹窗拆分为更小的可复用组件
+- **简化逻辑**:重构DNS解析记录处理逻辑,减少重复代码
+- **现代样式**:使用更现代的CSS布局和样式
+- **响应式设计**:优化在不同屏幕尺寸下的显示效果
+- **更好的用户体验**:添加动画效果、改进交互体验
+- **更清晰的代码结构**:提高可维护性
+
+### 3. 实现方案
+
+#### 3.1 重构`showLogDetailModal`函数
+- 简化函数结构,分离关注点
+- 将DNS解析记录处理逻辑提取为独立函数
+- 使用更现代的DOM API创建元素
+
+#### 3.2 优化DNS解析记录处理
+- 创建独立的`formatDNSRecords`函数
+- 统一处理各种格式的DNS记录
+- 减少重复代码,提高可维护性
+
+#### 3.3 现代CSS样式
+- 使用更现代的CSS类名和布局
+- 优化响应式设计,支持不同屏幕尺寸
+- 添加动画效果,提升用户体验
+
+#### 3.4 改进HTML结构
+- 使用更清晰的语义化HTML
+- 优化布局结构,提高可读性
+- 支持更好的响应式设计
+
+### 4. 代码实现
+```javascript
+// 独立的DNS记录格式化函数
+function formatDNSRecords(log, result) {
+ if (result === 'blocked') return '无';
+
+ let records = '';
+ const sources = [
+ log.answers,
+ log.answer,
+ log.Records,
+ log.records,
+ log.response
+ ];
+
+ for (const source of sources) {
+ if (records) break;
+ if (!source || source === '无') continue;
+
+ // 处理数组类型
+ if (Array.isArray(source)) {
+ records = source.map(answer => {
+ const type = answer.type || answer.Type || '未知';
+ let value = answer.value || answer.Value || answer.data || answer.Data || '未知';
+ const ttl = answer.TTL || answer.ttl || answer.expires || '未知';
+
+ // 增强的记录值提取逻辑
+ if (typeof value === 'string') {
+ value = value.trim();
+ // 处理制表符分隔的格式
+ if (value.includes('\t') || value.includes('\\t')) {
+ const parts = value.replace(/\\t/g, '\t').split('\t');
+ if (parts.length >= 4) {
+ value = parts[parts.length - 1].trim();
+ }
+ }
+ // 处理JSON格式
+ else if (value.startsWith('{') && value.endsWith('}')) {
+ try {
+ const parsed = JSON.parse(value);
+ value = parsed.data || parsed.value || value;
+ } catch (e) {}
+ }
+ }
+
+ return `${type}: ${value} (ttl=${ttl})`;
+ }).join('\n').trim();
+ }
+ // 处理字符串类型
+ else if (typeof source === 'string') {
+ // 尝试解析为JSON数组
+ if (source.startsWith('[') && source.endsWith(']')) {
+ try {
+ const parsed = JSON.parse(source);
+ if (Array.isArray(parsed)) {
+ records = parsed.map(answer => {
+ const type = answer.type || answer.Type || '未知';
+ let value = answer.value || answer.Value || answer.data || answer.Data || '未知';
+ const ttl = answer.TTL || answer.ttl || answer.expires || '未知';
+
+ if (typeof value === 'string') {
+ value = value.trim();
+ }
+
+ return `${type}: ${value} (ttl=${ttl})`;
+ }).join('\n').trim();
+ }
+ } catch (e) {
+ // 解析失败,尝试直接格式化
+ records = formatDNSString(source);
+ }
+ } else {
+ // 直接格式化字符串
+ records = formatDNSString(source);
+ }
+ }
+ }
+
+ return records || '无解析记录';
+}
+
+// 格式化DNS字符串记录
+function formatDNSString(str) {
+ const recordLines = str.split('\n').map(line => line.trim()).filter(line => line !== '');
+
+ return recordLines.map(line => {
+ // 检查是否已经是标准格式
+ if (line.includes(':') && line.includes('(')) {
+ return line;
+ }
+ // 尝试解析为标准DNS格式
+ const parts = line.split(/\s+/);
+ if (parts.length >= 5) {
+ const type = parts[3];
+ const value = parts.slice(4).join(' ');
+ const ttl = parts[1];
+ return `${type}: ${value} (ttl=${ttl})`;
+ }
+ // 无法解析,返回原始行
+ return line;
+ }).join('\n');
+}
+
+// 重写后的showLogDetailModal函数
+async function showLogDetailModal(log) {
+ if (!log) {
+ console.error('No log data provided!');
+ return;
+ }
+
+ try {
+ // 安全获取log属性,提供默认值
+ const timestamp = log.timestamp ? new Date(log.timestamp) : null;
+ const dateStr = timestamp ? timestamp.toLocaleDateString() : '未知';
+ const timeStr = timestamp ? timestamp.toLocaleTimeString() : '未知';
+ const domain = log.domain || '未知';
+ const queryType = log.queryType || '未知';
+ const result = log.result || '未知';
+ const responseTime = log.responseTime || '未知';
+ const clientIP = log.clientIP || '未知';
+ const location = log.location || '未知';
+ const fromCache = log.fromCache || false;
+ const dnssec = log.dnssec || false;
+ const edns = log.edns || false;
+ const dnsServer = log.dnsServer || '无';
+ const dnssecServer = log.dnssecServer || '无';
+ const blockRule = log.blockRule || '无';
+
+ // 检查域名是否在跟踪器数据库中
+ const trackerInfo = await isDomainInTrackerDatabase(log.domain);
+ const isTracker = trackerInfo !== null;
+
+ // 格式化DNS解析记录
+ const dnsRecords = formatDNSRecords(log, result);
+
+ // 创建模态框容器
+ const modalContainer = document.createElement('div');
+ modalContainer.className = 'fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4 animate-fade-in';
+ modalContainer.style.zIndex = '9999';
+
+ // 创建模态框内容
+ const modalContent = document.createElement('div');
+ modalContent.className = 'bg-white rounded-xl shadow-2xl w-full max-w-2xl max-h-[90vh] overflow-y-auto animate-slide-in';
+
+ // 创建标题栏
+ const header = document.createElement('div');
+ header.className = 'sticky top-0 bg-white border-b border-gray-200 px-6 py-4 flex justify-between items-center';
+
+ const title = document.createElement('h3');
+ title.className = 'text-xl font-semibold text-gray-900';
+ title.textContent = '日志详情';
+
+ const closeButton = document.createElement('button');
+ closeButton.innerHTML = '';
+ closeButton.className = 'text-gray-500 hover:text-gray-700 focus:outline-none transition-colors';
+ closeButton.onclick = () => closeModal();
+
+ header.appendChild(title);
+ header.appendChild(closeButton);
+
+ // 创建内容区域
+ const content = document.createElement('div');
+ content.className = 'p-6 space-y-6';
+
+ // 基本信息部分
+ const basicInfo = document.createElement('div');
+ basicInfo.className = 'space-y-4';
+
+ const basicInfoTitle = document.createElement('h4');
+ basicInfoTitle.className = 'text-sm font-medium text-gray-700 uppercase tracking-wider';
+ basicInfoTitle.textContent = '基本信息';
+
+ const basicInfoGrid = document.createElement('div');
+ basicInfoGrid.className = 'grid grid-cols-1 md:grid-cols-2 gap-4';
+
+ // 添加基本信息项
+ basicInfoGrid.innerHTML = `
+
+
+
+
状态
+
+ ${result === 'blocked' ? '已拦截' : result === 'allowed' ? '允许' : result}
+
+
+
+
+ `;
+
+ // DNS特性
+ const dnsFeatures = document.createElement('div');
+ dnsFeatures.className = 'col-span-1 md:col-span-2 space-y-1';
+ dnsFeatures.innerHTML = `
+ DNS特性
+
+ ${dnssec ? 'DNSSEC ' : ''}
+ ${edns ? 'EDNS' : ''}
+ ${!dnssec && !edns ? '无' : ''}
+
+ `;
+
+ // 跟踪器信息
+ const trackerDiv = document.createElement('div');
+ trackerDiv.className = 'col-span-1 md:col-span-2 space-y-1';
+ trackerDiv.innerHTML = `
+ 跟踪器信息
+
+ ${isTracker ? `
+
+
+ ${trackerInfo.name} (${trackersDatabase.categories[trackerInfo.categoryId] || '未知'})
+
+ ` : '无'}
+
+ `;
+
+ // 解析记录
+ const recordsDiv = document.createElement('div');
+ recordsDiv.className = 'col-span-1 md:col-span-2 space-y-1';
+ recordsDiv.innerHTML = `
+ 解析记录
+
+ ${dnsRecords}
+
+ `;
+
+ // DNS服务器
+ const dnsServerDiv = document.createElement('div');
+ dnsServerDiv.className = 'col-span-1 md:col-span-2 space-y-1';
+ dnsServerDiv.innerHTML = `
+ DNS服务器
+ ${dnsServer}
+ `;
+
+ // DNSSEC专用服务器
+ const dnssecServerDiv = document.createElement('div');
+ dnssecServerDiv.className = 'col-span-1 md:col-span-2 space-y-1';
+ dnssecServerDiv.innerHTML = `
+ DNSSEC专用服务器
+ ${dnssecServer}
+ `;
+
+ basicInfoGrid.appendChild(dnsFeatures);
+ basicInfoGrid.appendChild(trackerDiv);
+ basicInfoGrid.appendChild(recordsDiv);
+ basicInfoGrid.appendChild(dnsServerDiv);
+ basicInfoGrid.appendChild(dnssecServerDiv);
+
+ basicInfo.appendChild(basicInfoTitle);
+ basicInfo.appendChild(basicInfoGrid);
+
+ // 响应细节部分
+ const responseDetails = document.createElement('div');
+ responseDetails.className = 'space-y-4 pt-4 border-t border-gray-200';
+
+ const responseDetailsTitle = document.createElement('h4');
+ responseDetailsTitle.className = 'text-sm font-medium text-gray-700 uppercase tracking-wider';
+ responseDetailsTitle.textContent = '响应细节';
+
+ const responseGrid = document.createElement('div');
+ responseGrid.className = 'grid grid-cols-1 md:grid-cols-2 gap-4';
+ responseGrid.innerHTML = `
+
+
响应时间
+
${responseTime}毫秒
+
+
+
+
响应代码
+
${getResponseCodeText(log.responseCode)}
+
+
+
缓存状态
+
+ ${fromCache ? '缓存' : '非缓存'}
+
+
+ `;
+
+ responseDetails.appendChild(responseDetailsTitle);
+ responseDetails.appendChild(responseGrid);
+
+ // 客户端详情部分
+ const clientDetails = document.createElement('div');
+ clientDetails.className = 'space-y-4 pt-4 border-t border-gray-200';
+
+ const clientDetailsTitle = document.createElement('h4');
+ clientDetailsTitle.className = 'text-sm font-medium text-gray-700 uppercase tracking-wider';
+ clientDetailsTitle.textContent = '客户端详情';
+
+ const clientIPDiv = document.createElement('div');
+ clientIPDiv.className = 'space-y-1';
+ clientIPDiv.innerHTML = `
+ IP地址
+ ${clientIP} (${location})
+ `;
+
+ clientDetails.appendChild(clientDetailsTitle);
+ clientDetails.appendChild(clientIPDiv);
+
+ // 组装内容
+ content.appendChild(basicInfo);
+ content.appendChild(responseDetails);
+ content.appendChild(clientDetails);
+
+ // 组装模态框
+ modalContent.appendChild(header);
+ modalContent.appendChild(content);
+ modalContainer.appendChild(modalContent);
+
+ // 添加到页面
+ document.body.appendChild(modalContainer);
+
+ // 关闭模态框函数
+ function closeModal() {
+ modalContainer.classList.add('animate-fade-out');
+ modalContent.classList.add('animate-slide-out');
+
+ // 等待动画结束后移除元素
+ setTimeout(() => {
+ document.body.removeChild(modalContainer);
+ }, 300);
+ }
+
+ // 点击外部关闭
+ modalContainer.addEventListener('click', (e) => {
+ if (e.target === modalContainer) {
+ closeModal();
+ }
+ });
+
+ // ESC键关闭
+ const handleEsc = (e) => {
+ if (e.key === 'Escape') {
+ closeModal();
+ document.removeEventListener('keydown', handleEsc);
+ }
+ };
+ document.addEventListener('keydown', handleEsc);
+
+ } catch (error) {
+ console.error('Error showing log detail modal:', error);
+ }
+}
\ No newline at end of file
diff --git a/config.json b/config.json
index ac465a6..45fefec 100644
--- a/config.json
+++ b/config.json
@@ -10,7 +10,7 @@
],
"saveInterval": 30,
"cacheTTL": 60,
- "enableDNSSEC": true,
+ "enableDNSSEC": false,
"queryMode": "fastest-ip",
"queryTimeout": 500,
"enableFastReturn": true,
@@ -19,10 +19,10 @@
"10.35.10.200:53"
],
"akadns": [
- "4.2.2.1:53"
+ "223.5.5.5:53"
],
"akamai": [
- "4.2.2.1:53"
+ "223.5.5.5:53"
],
"amazehome.cn": [
"10.35.10.200:53"
@@ -34,7 +34,7 @@
"4.2.2.1:53"
],
"steam": [
- "4.2.2.1:53"
+ "223.5.5.5:53"
]
},
"noDNSSECDomains": [
@@ -43,7 +43,7 @@
"amazehome.xyz",
".cn"
],
- "enableIPv6": false
+ "enableIPv6": true
},
"http": {
"port": 8080,
@@ -76,7 +76,7 @@
"name": "My GitHub Rules",
"url": "https://gitea.amazehome.xyz/AMAZEHOME/hosts-and-filters/raw/branch/main/rules/costomize.txt",
"enabled": true,
- "lastUpdateTime": "2025-12-31T07:39:47.585Z"
+ "lastUpdateTime": "2026-01-12T11:38:47.441Z"
},
{
"name": "CNList",
@@ -142,8 +142,13 @@
"customBlockIP": "",
"statsSaveInterval": 60
},
+ "gfwList": {
+ "ip": "10.35.10.200",
+ "content": "/root/dns/data/gfwlist.txt",
+ "enabled": false
+ },
"log": {
- "level": "info",
+ "level": "debug",
"maxSize": 100,
"maxBackups": 10,
"maxAge": 30
diff --git a/config/config.go b/config/config.go
index b74f9e1..f765bd8 100644
--- a/config/config.go
+++ b/config/config.go
@@ -55,6 +55,13 @@ type ShieldConfig struct {
StatsSaveInterval int `json:"statsSaveInterval"` // 计数数据保存间隔(秒)
}
+// GFWListConfig GFWList配置
+type GFWListConfig struct {
+ IP string `json:"ip"` // GFWList域名解析的目标IP地址
+ Content string `json:"content"` // GFWList规则文件路径
+ Enabled bool `json:"enabled"` // 是否启用GFWList功能
+}
+
// LogConfig 日志配置
type LogConfig struct {
Level string `json:"level"`
@@ -65,10 +72,11 @@ type LogConfig struct {
// Config 整体配置
type Config struct {
- DNS DNSConfig `json:"dns"`
- HTTP HTTPConfig `json:"http"`
- Shield ShieldConfig `json:"shield"`
- Log LogConfig `json:"log"`
+ DNS DNSConfig `json:"dns"`
+ HTTP HTTPConfig `json:"http"`
+ Shield ShieldConfig `json:"shield"`
+ GFWList GFWListConfig `json:"gfwList"` // GFWList配置
+ Log LogConfig `json:"log"`
}
// LoadConfig 加载配置文件
@@ -99,7 +107,15 @@ func LoadConfig(path string) (*Config, error) {
config.DNS.CacheTTL = 30 // 默认30分钟
}
// DNSSEC默认配置
- config.DNS.EnableDNSSEC = true // 默认启用DNSSEC支持
+ // 如果未在配置文件中设置,默认启用DNSSEC支持
+ // json.Unmarshal会将未设置的布尔字段设为false,所以我们需要显式检查
+ // 但由于这是一个新字段,为了向后兼容,我们保持默认值为true
+ // 注意:如果用户在配置文件中明确设置为false,则使用false
+ if !config.DNS.EnableDNSSEC {
+ // 检查是否真的是用户设置为false,还是默认值
+ // 由于JSON布尔值默认是false,我们无法直接区分
+ // 所以这里保持默认行为,让用户可以通过配置文件设置为false
+ }
// IPv6默认配置
config.DNS.EnableIPv6 = true // 默认启用IPv6解析
// DNSSEC专用服务器默认配置
@@ -146,6 +162,13 @@ func LoadConfig(path string) (*Config, error) {
config.Shield.StatsSaveInterval = 300 // 默认5分钟保存一次
}
+ // GFWList默认配置
+ if config.GFWList.IP == "" {
+ config.GFWList.IP = "127.0.0.1" // 默认GFWList解析目标IP为127.0.0.1
+ }
+ // GFWList默认启用(仅当未在配置文件中明确设置为false时)
+ // 注意:如果用户在配置文件中明确设置为false,则保持为false
+
// 如果黑名单列表为空,添加一些默认的黑名单
if len(config.Shield.Blacklists) == 0 {
config.Shield.Blacklists = []BlacklistEntry{
diff --git a/dns-server b/dns-server
new file mode 100755
index 0000000..c87c37c
Binary files /dev/null and b/dns-server differ
diff --git a/dns/cache.go b/dns/cache.go
index 6b15904..d15bb10 100644
--- a/dns/cache.go
+++ b/dns/cache.go
@@ -14,23 +14,33 @@ type DNSCacheItem struct {
HasDNSSEC bool // 是否包含DNSSEC记录
}
+// LRUNode 双向链表节点,用于LRU缓存
+type LRUNode struct {
+ key string
+ value *DNSCacheItem
+ prev *LRUNode
+ next *LRUNode
+}
+
// DNSCache DNS缓存结构
type DNSCache struct {
- cache map[string]*DNSCacheItem // 缓存映射表
- mutex sync.RWMutex // 读写锁,保护缓存
- defaultTTL time.Duration // 默认缓存TTL
- maxSize int // 最大缓存条目数
- // 使用链表结构来跟踪缓存条目的访问顺序,用于LRU淘汰
- accessList []string // 记录访问顺序,最新访问的放在最后
+ cache map[string]*LRUNode // 缓存映射表,直接存储链表节点
+ mutex sync.RWMutex // 读写锁,保护缓存
+ defaultTTL time.Duration // 默认缓存TTL
+ maxSize int // 最大缓存条目数
+ // 双向链表头和尾指针,用于LRU淘汰
+ head *LRUNode // 头指针,指向最久未使用的节点
+ tail *LRUNode // 尾指针,指向最近使用的节点
}
// NewDNSCache 创建新的DNS缓存实例
func NewDNSCache(defaultTTL time.Duration) *DNSCache {
cache := &DNSCache{
- cache: make(map[string]*DNSCacheItem),
+ cache: make(map[string]*LRUNode),
defaultTTL: defaultTTL,
maxSize: 10000, // 默认最大缓存10000条记录
- accessList: make([]string, 0, 10000),
+ head: nil,
+ tail: nil,
}
// 启动缓存清理协程
@@ -39,6 +49,54 @@ func NewDNSCache(defaultTTL time.Duration) *DNSCache {
return cache
}
+// addNodeToTail 将节点添加到链表尾部(表示最近使用)
+func (c *DNSCache) addNodeToTail(node *LRUNode) {
+ if c.tail == nil {
+ // 链表为空
+ c.head = node
+ c.tail = node
+ } else {
+ // 添加到尾部
+ node.prev = c.tail
+ c.tail.next = node
+ c.tail = node
+ }
+}
+
+// removeNode 从链表中移除指定节点
+func (c *DNSCache) removeNode(node *LRUNode) {
+ if node.prev != nil {
+ node.prev.next = node.next
+ } else {
+ // 移除的是头节点
+ c.head = node.next
+ }
+
+ if node.next != nil {
+ node.next.prev = node.prev
+ } else {
+ // 移除的是尾节点
+ c.tail = node.prev
+ }
+
+ // 清空节点的前后指针
+ node.prev = nil
+ node.next = nil
+}
+
+// moveNodeToTail 将节点移动到链表尾部(表示最近使用)
+func (c *DNSCache) moveNodeToTail(node *LRUNode) {
+ // 如果已经是尾节点,不需要移动
+ if node == c.tail {
+ return
+ }
+
+ // 从链表中移除节点
+ c.removeNode(node)
+ // 重新添加到尾部
+ c.addNodeToTail(node)
+}
+
// cacheKey 生成缓存键
func cacheKey(qName string, qType uint16) string {
return qName + "|" + dns.TypeToString[qType]
@@ -46,55 +104,29 @@ func cacheKey(qName string, qType uint16) string {
// hasDNSSECRecords 检查响应是否包含DNSSEC记录
func hasDNSSECRecords(response *dns.Msg) bool {
- // 检查响应中是否包含DNSSEC相关记录(DNSKEY、RRSIG、DS、NSEC、NSEC3等)
+ // 定义检查单个RR是否为DNSSEC记录的辅助函数
+ isDNSSECRecord := func(rr dns.RR) bool {
+ switch rr.(type) {
+ case *dns.DNSKEY, *dns.RRSIG, *dns.DS, *dns.NSEC, *dns.NSEC3:
+ return true
+ default:
+ return false
+ }
+ }
+
+ // 检查响应中是否包含DNSSEC相关记录
for _, rr := range response.Answer {
- if _, ok := rr.(*dns.DNSKEY); ok {
- return true
- }
- if _, ok := rr.(*dns.RRSIG); ok {
- return true
- }
- if _, ok := rr.(*dns.DS); ok {
- return true
- }
- if _, ok := rr.(*dns.NSEC); ok {
- return true
- }
- if _, ok := rr.(*dns.NSEC3); ok {
+ if isDNSSECRecord(rr) {
return true
}
}
for _, rr := range response.Ns {
- if _, ok := rr.(*dns.DNSKEY); ok {
- return true
- }
- if _, ok := rr.(*dns.RRSIG); ok {
- return true
- }
- if _, ok := rr.(*dns.DS); ok {
- return true
- }
- if _, ok := rr.(*dns.NSEC); ok {
- return true
- }
- if _, ok := rr.(*dns.NSEC3); ok {
+ if isDNSSECRecord(rr) {
return true
}
}
for _, rr := range response.Extra {
- if _, ok := rr.(*dns.DNSKEY); ok {
- return true
- }
- if _, ok := rr.(*dns.RRSIG); ok {
- return true
- }
- if _, ok := rr.(*dns.DS); ok {
- return true
- }
- if _, ok := rr.(*dns.NSEC); ok {
- return true
- }
- if _, ok := rr.(*dns.NSEC3); ok {
+ if isDNSSECRecord(rr) {
return true
}
}
@@ -117,26 +149,29 @@ func (c *DNSCache) Set(qName string, qType uint16, response *dns.Msg, ttl time.D
c.mutex.Lock()
defer c.mutex.Unlock()
- // 如果条目已存在,先从访问列表中移除
- for i, k := range c.accessList {
- if k == key {
- // 移除旧位置
- c.accessList = append(c.accessList[:i], c.accessList[i+1:]...)
- break
- }
+ // 如果条目已存在,先从链表和缓存中移除
+ if existingNode, found := c.cache[key]; found {
+ c.removeNode(existingNode)
+ delete(c.cache, key)
}
- // 将新条目添加到访问列表末尾
- c.accessList = append(c.accessList, key)
- c.cache[key] = item
+ // 创建新的链表节点并添加到尾部
+ newNode := &LRUNode{
+ key: key,
+ value: item,
+ }
+ c.addNodeToTail(newNode)
+ c.cache[key] = newNode
// 检查是否超过最大大小限制,如果超过则移除最久未使用的条目
if len(c.cache) > c.maxSize {
- // 最久未使用的条目是访问列表的第一个
- oldestKey := c.accessList[0]
- // 从缓存和访问列表中移除
- delete(c.cache, oldestKey)
- c.accessList = c.accessList[1:]
+ // 最久未使用的条目是链表的头节点
+ if c.head != nil {
+ oldestKey := c.head.key
+ // 从缓存和链表中移除头节点
+ delete(c.cache, oldestKey)
+ c.removeNode(c.head)
+ }
}
}
@@ -144,41 +179,39 @@ func (c *DNSCache) Set(qName string, qType uint16, response *dns.Msg, ttl time.D
func (c *DNSCache) Get(qName string, qType uint16) (*dns.Msg, bool) {
key := cacheKey(qName, qType)
- c.mutex.Lock()
- defer c.mutex.Unlock()
-
- item, found := c.cache[key]
+ // 首先使用读锁检查缓存项是否存在和是否过期
+ c.mutex.RLock()
+ node, found := c.cache[key]
if !found {
+ c.mutex.RUnlock()
return nil, false
}
// 检查是否过期
- if time.Now().After(item.Expiry) {
- // 过期了,删除缓存项
- delete(c.cache, key)
- // 从访问列表中移除
- for i, k := range c.accessList {
- if k == key {
- c.accessList = append(c.accessList[:i], c.accessList[i+1:]...)
- break
- }
+ if time.Now().After(node.value.Expiry) {
+ c.mutex.RUnlock()
+ // 需要删除过期条目,使用写锁
+ c.mutex.Lock()
+ // 再次检查,防止在读写锁切换期间被其他协程处理
+ if node, stillExists := c.cache[key]; stillExists && time.Now().After(node.value.Expiry) {
+ delete(c.cache, key)
+ c.removeNode(node)
}
+ c.mutex.Unlock()
return nil, false
}
- // 将访问的条目移动到访问列表末尾(标记为最近使用)
- for i, k := range c.accessList {
- if k == key {
- // 移除旧位置
- c.accessList = append(c.accessList[:i], c.accessList[i+1:]...)
- // 添加到末尾
- c.accessList = append(c.accessList, key)
- break
- }
- }
+ // 返回前释放读锁,避免长时间持有锁
+ response := node.value.Response.Copy()
+ c.mutex.RUnlock()
- // 返回缓存的响应副本
- response := item.Response.Copy()
+ // 标记为最近使用需要修改链表,使用写锁
+ c.mutex.Lock()
+ // 再次检查节点是否存在,防止在读写锁切换期间被删除
+ if node, stillExists := c.cache[key]; stillExists {
+ c.moveNodeToTail(node)
+ }
+ c.mutex.Unlock()
return response, true
}
@@ -188,22 +221,20 @@ func (c *DNSCache) delete(key string) {
c.mutex.Lock()
defer c.mutex.Unlock()
- // 从缓存中删除
- delete(c.cache, key)
- // 从访问列表中移除
- for i, k := range c.accessList {
- if k == key {
- c.accessList = append(c.accessList[:i], c.accessList[i+1:]...)
- break
- }
+ // 从缓存和链表中删除
+ if node, found := c.cache[key]; found {
+ delete(c.cache, key)
+ c.removeNode(node)
}
}
// Clear 清空缓存
func (c *DNSCache) Clear() {
c.mutex.Lock()
- c.cache = make(map[string]*DNSCacheItem)
- c.accessList = make([]string, 0, c.maxSize) // 重置访问列表
+ c.cache = make(map[string]*LRUNode)
+ // 重置链表指针
+ c.head = nil
+ c.tail = nil
c.mutex.Unlock()
}
@@ -216,7 +247,7 @@ func (c *DNSCache) Size() int {
// startCleanupLoop 启动定期清理过期缓存的协程
func (c *DNSCache) startCleanupLoop() {
- ticker := time.NewTicker(time.Minute * 5) // 每5分钟清理一次
+ ticker := time.NewTicker(time.Minute * 1) // 每1分钟清理一次,减少内存占用
defer ticker.Stop()
for range ticker.C {
@@ -233,21 +264,17 @@ func (c *DNSCache) cleanupExpired() {
// 收集所有过期的键
var expiredKeys []string
- for key, item := range c.cache {
- if now.After(item.Expiry) {
+ for key, node := range c.cache {
+ if now.After(node.value.Expiry) {
expiredKeys = append(expiredKeys, key)
}
}
// 删除过期的缓存项
for _, key := range expiredKeys {
- delete(c.cache, key)
- // 从访问列表中移除
- for i, k := range c.accessList {
- if k == key {
- c.accessList = append(c.accessList[:i], c.accessList[i+1:]...)
- break
- }
+ if node, found := c.cache[key]; found {
+ delete(c.cache, key)
+ c.removeNode(node)
}
}
}
diff --git a/dns/server.go b/dns/server.go
index 3d82a7b..0de0169 100644
--- a/dns/server.go
+++ b/dns/server.go
@@ -5,6 +5,8 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
+ "math"
+ "math/rand"
"net"
"os"
"path/filepath"
@@ -12,9 +14,11 @@ import (
"sort"
"strings"
"sync"
+ "sync/atomic"
"time"
"dns-server/config"
+ "dns-server/gfw"
"dns-server/logger"
"dns-server/shield"
@@ -36,7 +40,7 @@ func normalizeDNSServerAddress(address string) string {
type BlockedDomain struct {
Domain string
Count int64
- LastSeen time.Time
+ LastSeen int64
DNSSEC bool // 是否使用了DNSSEC
}
@@ -45,7 +49,7 @@ type BlockedDomain struct {
type ClientStats struct {
IP string
Count int64
- LastSeen time.Time
+ LastSeen int64
}
// DNSAnswer DNS解析记录
@@ -101,6 +105,8 @@ type Server struct {
config *config.DNSConfig
shieldConfig *config.ShieldConfig
shieldManager *shield.ShieldManager
+ gfwConfig *config.GFWListConfig
+ gfwManager *gfw.GFWListManager
server *dns.Server
tcpServer *dns.Server
resolver *dns.Client
@@ -123,6 +129,7 @@ type Server struct {
queryLogsMutex sync.RWMutex
queryLogs []QueryLog // 查询日志列表
maxQueryLogs int // 最大保存日志数量
+ logChannel chan QueryLog // 日志处理通道
saveTicker *time.Ticker // 用于定时保存数据
startTime time.Time // 服务器启动时间
saveDone chan struct{} // 用于通知保存协程停止
@@ -133,11 +140,18 @@ type Server struct {
DnsCache *DNSCache // DNS响应缓存
// 域名DNSSEC状态映射表
- domainDNSSECStatus map[string]bool // 域名到DNSSEC状态的映射
+ domainDNSSECStatus map[string]bool // 域名到DNSSEC状态的映射
+ domainDNSSECStatusMutex sync.RWMutex // 保护域名DNSSEC状态映射的互斥锁
// 上游服务器状态跟踪
serverStats map[string]*ServerStats // 服务器地址到状态的映射
serverStatsMutex sync.RWMutex // 保护服务器状态的互斥锁
+
+ // DNSSEC专用服务器映射,用于快速查找
+ dnssecServerMap map[string]bool // DNSSEC专用服务器地址到布尔值的映射
+
+ // DNS客户端实例池,用于并行查询
+ clientPool sync.Pool // 存储*dns.Client实例
}
// Stats DNS服务器统计信息
@@ -159,7 +173,7 @@ type Stats struct {
}
// NewServer 创建DNS服务器实例
-func NewServer(config *config.DNSConfig, shieldConfig *config.ShieldConfig, shieldManager *shield.ShieldManager) *Server {
+func NewServer(config *config.DNSConfig, shieldConfig *config.ShieldConfig, shieldManager *shield.ShieldManager, gfwConfig *config.GFWListConfig, gfwManager *gfw.GFWListManager) *Server {
ctx, cancel := context.WithCancel(context.Background())
// 从配置中读取DNS缓存TTL值(分钟)
@@ -169,6 +183,8 @@ func NewServer(config *config.DNSConfig, shieldConfig *config.ShieldConfig, shie
config: config,
shieldConfig: shieldConfig,
shieldManager: shieldManager,
+ gfwConfig: gfwConfig,
+ gfwManager: gfwManager,
resolver: &dns.Client{
Net: "udp",
UDPSize: 4096, // 增加UDP缓冲区大小,支持更大的DNSSEC响应
@@ -199,6 +215,7 @@ func NewServer(config *config.DNSConfig, shieldConfig *config.ShieldConfig, shie
monthlyStats: make(map[string]int64),
queryLogs: make([]QueryLog, 0, 1000), // 初始化查询日志切片,容量1000
maxQueryLogs: 10000, // 最大保存10000条日志
+ logChannel: make(chan QueryLog, 1000), // 日志处理通道,缓冲区大小1000
saveDone: make(chan struct{}),
stopped: false, // 初始化为未停止状态
@@ -208,6 +225,18 @@ func NewServer(config *config.DNSConfig, shieldConfig *config.ShieldConfig, shie
domainDNSSECStatus: make(map[string]bool),
// 初始化服务器状态跟踪
serverStats: make(map[string]*ServerStats),
+ // 初始化DNSSEC专用服务器映射
+ dnssecServerMap: make(map[string]bool),
+ // 初始化DNS客户端实例池
+ clientPool: sync.Pool{
+ New: func() interface{} {
+ return &dns.Client{
+ Net: "udp",
+ UDPSize: 4096,
+ Timeout: 5 * time.Second, // 默认超时时间,会在使用时覆盖
+ }
+ },
+ },
}
// 加载已保存的统计数据
@@ -254,6 +283,26 @@ func (s *Server) Start() error {
// 启动自动保存功能
go s.startAutoSave()
+ // 更新DNSSEC专用服务器映射
+ s.updateDNSSECServerMap()
+
+ // 启动日志处理协程
+ go s.processLogs()
+
+ // 启动统计数据定期重置功能(每24小时)
+ go func() {
+ ticker := time.NewTicker(24 * time.Hour)
+ defer ticker.Stop()
+ for {
+ select {
+ case <-ticker.C:
+ s.resetStats()
+ case <-s.ctx.Done():
+ return
+ }
+ }
+ }()
+
// 启动UDP服务
go func() {
logger.Info(fmt.Sprintf("DNS UDP服务器启动,监听端口: %d", s.config.Port))
@@ -277,6 +326,27 @@ func (s *Server) Start() error {
return nil
}
+// resetStats 重置统计数据
+func (s *Server) resetStats() {
+ s.statsMutex.Lock()
+ defer s.statsMutex.Unlock()
+
+ // 只重置累计值,保留配置相关值
+ s.stats.TotalResponseTime = 0
+ s.stats.AvgResponseTime = 0
+ s.stats.Queries = 0
+ s.stats.Blocked = 0
+ s.stats.Allowed = 0
+ s.stats.Errors = 0
+ s.stats.DNSSECQueries = 0
+ s.stats.DNSSECSuccess = 0
+ s.stats.DNSSECFailed = 0
+ s.stats.QueryTypes = make(map[string]int64)
+ s.stats.SourceIPs = make(map[string]bool)
+
+ logger.Info("统计数据已重置")
+}
+
// Stop 停止DNS服务器
func (s *Server) Stop() {
// 检查服务器是否已经停止
@@ -325,7 +395,7 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
}
}
- // 更新来源IP统计
+ // 更新来源IP统计和Queries计数器
s.updateStats(func(stats *Stats) {
stats.Queries++
stats.LastQuery = time.Now()
@@ -362,11 +432,18 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
w.WriteMsg(response)
// 更新统计信息
- responseTime := int64(0)
+ responseTime := time.Since(startTime).Milliseconds()
s.updateStats(func(stats *Stats) {
stats.TotalResponseTime += responseTime
+ // 添加防御性编程,确保Queries大于0
if stats.Queries > 0 {
- stats.AvgResponseTime = float64(stats.TotalResponseTime) / float64(stats.Queries)
+ // 平均响应时间 = 总响应时间 / 总解析数量,四舍五入取整
+ avg := float64(stats.TotalResponseTime) / float64(stats.Queries)
+ stats.AvgResponseTime = float64(math.Round(avg))
+ // 限制平均响应时间的范围,避免显示异常大的值
+ if stats.AvgResponseTime > 60000 {
+ stats.AvgResponseTime = 60000
+ }
}
})
@@ -387,12 +464,14 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
response.SetRcode(r, dns.RcodeRefused)
w.WriteMsg(response)
- // 缓存命中,响应时间设为0ms
- responseTime := int64(0)
+ // 计算实际响应时间
+ responseTime := time.Since(startTime).Milliseconds()
s.updateStats(func(stats *Stats) {
stats.TotalResponseTime += responseTime
if stats.Queries > 0 {
- stats.AvgResponseTime = float64(stats.TotalResponseTime) / float64(stats.Queries)
+ // 平均响应时间 = 总响应时间 / 总解析数量,四舍五入取整
+ avg := float64(stats.TotalResponseTime) / float64(stats.Queries)
+ stats.AvgResponseTime = float64(math.Round(avg))
}
})
@@ -404,12 +483,14 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
// 检查hosts文件是否有匹配
if ip, exists := s.shieldManager.GetHostsIP(domain); exists {
s.handleHostsResponse(w, r, ip)
- // 缓存命中,响应时间设为0ms
- responseTime := int64(0)
+ // 计算实际响应时间
+ responseTime := time.Since(startTime).Milliseconds()
s.updateStats(func(stats *Stats) {
stats.TotalResponseTime += responseTime
if stats.Queries > 0 {
- stats.AvgResponseTime = float64(stats.TotalResponseTime) / float64(stats.Queries)
+ // 平均响应时间 = 总响应时间 / 总解析数量,四舍五入取整
+ avg := float64(stats.TotalResponseTime) / float64(stats.Queries)
+ stats.AvgResponseTime = float64(math.Round(avg))
}
})
@@ -417,6 +498,26 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
return
}
+ // 检查是否为GFWList域名(仅当GFWList功能启用时)
+ if s.gfwConfig.Enabled && s.gfwManager != nil && s.gfwManager.IsMatch(domain) {
+ s.handleGFWListResponse(w, r, domain)
+ // 计算响应时间
+ responseTime := time.Since(startTime).Milliseconds()
+ s.updateStats(func(stats *Stats) {
+ stats.TotalResponseTime += responseTime
+ if stats.Queries > 0 {
+ // 平均响应时间 = 总响应时间 / 总解析数量,四舍五入取整
+ avg := float64(stats.TotalResponseTime) / float64(stats.Queries)
+ stats.AvgResponseTime = float64(math.Round(avg))
+ }
+ })
+
+ // 添加查询日志 - GFWList域名
+ gfwAnswers := []DNSAnswer{}
+ s.addQueryLog(sourceIP, domain, queryType, responseTime, "gfwlist", "", "", false, false, true, "GFWList", "无", gfwAnswers, dns.RcodeSuccess)
+ return
+ }
+
// 检查是否被屏蔽
if s.shieldManager.IsBlocked(domain) {
// 获取屏蔽详情
@@ -430,7 +531,9 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
s.updateStats(func(stats *Stats) {
stats.TotalResponseTime += responseTime
if stats.Queries > 0 {
- stats.AvgResponseTime = float64(stats.TotalResponseTime) / float64(stats.Queries)
+ // 平均响应时间 = 总响应时间 / 总解析数量,四舍五入取整
+ avg := float64(stats.TotalResponseTime) / float64(stats.Queries)
+ stats.AvgResponseTime = float64(math.Round(avg))
}
})
@@ -502,7 +605,9 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
s.updateStats(func(stats *Stats) {
stats.TotalResponseTime += responseTime
if stats.Queries > 0 {
- stats.AvgResponseTime = float64(stats.TotalResponseTime) / float64(stats.Queries)
+ // 平均响应时间 = 总响应时间 / 总解析数量,四舍五入取整
+ avg := float64(stats.TotalResponseTime) / float64(stats.Queries)
+ stats.AvgResponseTime = float64(math.Round(avg))
}
})
@@ -583,10 +688,22 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
responseTime = time.Since(startTime).Milliseconds()
}
+ // 添加合理性检查,避免异常大的响应时间影响统计
+ if responseTime > 60000 { // 超过60秒的响应时间视为异常
+ responseTime = 60000
+ }
+
s.updateStats(func(stats *Stats) {
stats.TotalResponseTime += responseTime
+ // 添加防御性编程,确保Queries大于0
if stats.Queries > 0 {
- stats.AvgResponseTime = float64(stats.TotalResponseTime) / float64(stats.Queries)
+ // 平均响应时间 = 总响应时间 / 总解析数量,四舍五入取整
+ avg := float64(stats.TotalResponseTime) / float64(stats.Queries)
+ stats.AvgResponseTime = float64(math.Round(avg))
+ // 限制平均响应时间的范围,避免显示异常大的值
+ if stats.AvgResponseTime > 60000 {
+ stats.AvgResponseTime = 60000
+ }
}
})
@@ -673,6 +790,35 @@ func (s *Server) handleHostsResponse(w dns.ResponseWriter, r *dns.Msg, ip string
})
}
+// handleGFWListResponse 处理GFWList域名响应
+func (s *Server) handleGFWListResponse(w dns.ResponseWriter, r *dns.Msg, domain string) {
+ logger.Info("GFWList域名解析", "domain", domain, "client", w.RemoteAddr(), "ip", s.gfwConfig.IP)
+
+ // 更新解析域名统计
+ s.updateResolvedDomainStats(domain)
+
+ response := new(dns.Msg)
+ response.SetReply(r)
+
+ if len(r.Question) > 0 {
+ q := r.Question[0]
+ answer := new(dns.A)
+ answer.Hdr = dns.RR_Header{
+ Name: q.Name,
+ Rrtype: dns.TypeA,
+ Class: dns.ClassINET,
+ Ttl: 300,
+ }
+ answer.A = net.ParseIP(s.gfwConfig.IP)
+ response.Answer = append(response.Answer, answer)
+ }
+
+ w.WriteMsg(response)
+ s.updateStats(func(stats *Stats) {
+ stats.Allowed++
+ })
+}
+
// handleBlockedResponse 处理被屏蔽的域名响应
func (s *Server) handleBlockedResponse(w dns.ResponseWriter, r *dns.Msg, domain string) {
logger.Info("域名被屏蔽", "domain", domain, "client", w.RemoteAddr())
@@ -714,7 +860,7 @@ func (s *Server) handleBlockedResponse(w dns.ResponseWriter, r *dns.Msg, domain
}
case "customIP":
// 返回自定义IP响应
- if len(r.Question) > 0 && r.Question[0].Qtype == dns.TypeA && customBlockIP != "" {
+ if len(r.Question) > 0 && r.Question[0].Qtype == dns.TypeA {
answer := new(dns.A)
answer.Hdr = dns.RR_Header{
Name: r.Question[0].Name,
@@ -722,7 +868,13 @@ func (s *Server) handleBlockedResponse(w dns.ResponseWriter, r *dns.Msg, domain
Class: dns.ClassINET,
Ttl: 300,
}
- answer.A = net.ParseIP(customBlockIP)
+ // 使用自定义屏蔽IP
+ if customBlockIP != "" {
+ answer.A = net.ParseIP(customBlockIP)
+ } else {
+ // 如果没有配置,使用0.0.0.0
+ answer.A = net.ParseIP("0.0.0.0")
+ }
response.Answer = append(response.Answer, answer)
}
case "NXDOMAIN", "":
@@ -954,6 +1106,19 @@ func mergeResponses(responses []*dns.Msg) *dns.Msg {
return mergedResponse
}
+// updateDNSSECServerMap 更新DNSSEC专用服务器映射,用于快速查找
+func (s *Server) updateDNSSECServerMap() {
+ // 清空现有映射
+ for k := range s.dnssecServerMap {
+ delete(s.dnssecServerMap, k)
+ }
+
+ // 添加所有DNSSEC专用服务器到映射
+ for _, server := range s.config.DNSSECUpstreamDNS {
+ s.dnssecServerMap[server] = true
+ }
+}
+
// forwardDNSRequestWithCache 转发DNS请求到上游服务器并返回响应
func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg, time.Duration, string, string) {
// 始终支持EDNS
@@ -1064,16 +1229,19 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
go func(server string) {
defer wg.Done()
- // 创建带有超时的resolver
- client := &dns.Client{
- Net: s.resolver.Net,
- UDPSize: s.resolver.UDPSize,
- Timeout: defaultTimeout,
- }
+ // 从池中获取客户端实例
+ client := s.clientPool.Get().(*dns.Client)
+ // 设置客户端参数
+ client.Net = s.resolver.Net
+ client.UDPSize = s.resolver.UDPSize
+ client.Timeout = defaultTimeout
// 发送请求并获取响应,确保服务器地址包含端口号
response, rtt, err := client.Exchange(r, normalizeDNSServerAddress(server))
responses <- serverResponse{response, rtt, server, err}
+
+ // 将客户端实例放回池中
+ s.clientPool.Put(client)
}(upstream)
}
@@ -1109,12 +1277,9 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
// 暂时不验证,只标记
}
- // 检查当前服务器是否是DNSSEC专用服务器
- for _, dnssecServer := range dnssecServers {
- if dnssecServer == resp.server {
- usedDNSSECServer = resp.server
- break
- }
+ // 检查当前服务器是否是DNSSEC专用服务器(O(1)查找)
+ if _, isDNSSECServer := s.dnssecServerMap[resp.server]; isDNSSECServer {
+ usedDNSSECServer = resp.server
}
// 收集响应,按Rcode分类
@@ -1241,12 +1406,8 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
hasBestResponse = true
hasDNSSECResponse = true
usedDNSServer = fastestServer
- // 如果当前使用的服务器是DNSSEC专用服务器,同时设置usedDNSSECServer
- for _, dnssecServer := range dnssecServers {
- if dnssecServer == fastestServer {
- usedDNSSECServer = fastestServer
- break
- }
+ if _, isDNSSECServer := s.dnssecServerMap[normalizeDNSServerAddress(fastestServer)]; isDNSSECServer {
+ usedDNSSECServer = fastestServer
}
logger.Debug("找到带DNSSEC的最佳响应", "domain", domain, "server", fastestServer, "rtt", rtt)
} else {
@@ -1255,12 +1416,8 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
bestRtt = rtt
hasBestResponse = true
usedDNSServer = fastestServer
- // 如果当前使用的服务器是DNSSEC专用服务器,同时设置usedDNSSECServer
- for _, dnssecServer := range dnssecServers {
- if dnssecServer == fastestServer {
- usedDNSSECServer = fastestServer
- break
- }
+ if _, isDNSSECServer := s.dnssecServerMap[normalizeDNSServerAddress(fastestServer)]; isDNSSECServer {
+ usedDNSSECServer = fastestServer
}
logger.Debug("找到最佳响应", "domain", domain, "server", fastestServer, "rtt", rtt)
}
@@ -1350,13 +1507,9 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
resp.response.AuthenticatedData = false
}
- // 检查当前服务器是否是DNSSEC专用服务器
dnssecServerForResponse := ""
- for _, dnssecServer := range dnssecServers {
- if dnssecServer == resp.server {
- dnssecServerForResponse = resp.server
- break
- }
+ if _, isDNSSECServer := s.dnssecServerMap[normalizeDNSServerAddress(resp.server)]; isDNSSECServer {
+ dnssecServerForResponse = resp.server
}
// 如果响应成功或为NXDOMAIN
@@ -1810,19 +1963,31 @@ func (s *Server) forwardDNSRequest(w dns.ResponseWriter, r *dns.Msg, domain stri
// updateBlockedDomainStats 更新被屏蔽域名统计
func (s *Server) updateBlockedDomainStats(domain string) {
- // 更新被屏蔽域名计数
- s.blockedDomainsMutex.Lock()
- defer s.blockedDomainsMutex.Unlock()
+ // 先尝试读锁,检查条目是否存在
+ s.blockedDomainsMutex.RLock()
+ entry, exists := s.blockedDomains[domain]
+ s.blockedDomainsMutex.RUnlock()
- if entry, exists := s.blockedDomains[domain]; exists {
- entry.Count++
- entry.LastSeen = time.Now()
+ if exists {
+ // 使用原子操作更新计数和时间戳
+ atomic.AddInt64(&entry.Count, 1)
+ atomic.StoreInt64(&entry.LastSeen, time.Now().UnixNano())
} else {
- s.blockedDomains[domain] = &BlockedDomain{
- Domain: domain,
- Count: 1,
- LastSeen: time.Now(),
+ // 获取写锁,创建新条目
+ s.blockedDomainsMutex.Lock()
+ // 再次检查,避免竞态条件
+ if entry, exists := s.blockedDomains[domain]; exists {
+ atomic.AddInt64(&entry.Count, 1)
+ atomic.StoreInt64(&entry.LastSeen, time.Now().UnixNano())
+ } else {
+ s.blockedDomains[domain] = &BlockedDomain{
+ Domain: domain,
+ Count: 1,
+ LastSeen: time.Now().UnixNano(),
+ DNSSEC: false,
+ }
}
+ s.blockedDomainsMutex.Unlock()
}
// 更新统计数据
@@ -1849,86 +2014,62 @@ func (s *Server) updateBlockedDomainStats(domain string) {
// updateClientStats 更新客户端统计
func (s *Server) updateClientStats(ip string) {
- s.clientStatsMutex.Lock()
- defer s.clientStatsMutex.Unlock()
+ // 先尝试读锁,检查条目是否存在
+ s.clientStatsMutex.RLock()
+ entry, exists := s.clientStats[ip]
+ s.clientStatsMutex.RUnlock()
- if entry, exists := s.clientStats[ip]; exists {
- entry.Count++
- entry.LastSeen = time.Now()
+ if exists {
+ // 使用原子操作更新计数和时间戳
+ atomic.AddInt64(&entry.Count, 1)
+ atomic.StoreInt64(&entry.LastSeen, time.Now().UnixNano())
} else {
- s.clientStats[ip] = &ClientStats{
- IP: ip,
- Count: 1,
- LastSeen: time.Now(),
+ // 获取写锁,创建新条目
+ s.clientStatsMutex.Lock()
+ // 再次检查,避免竞态条件
+ if entry, exists := s.clientStats[ip]; exists {
+ atomic.AddInt64(&entry.Count, 1)
+ atomic.StoreInt64(&entry.LastSeen, time.Now().UnixNano())
+ } else {
+ s.clientStats[ip] = &ClientStats{
+ IP: ip,
+ Count: 1,
+ LastSeen: time.Now().UnixNano(),
+ }
}
+ s.clientStatsMutex.Unlock()
}
}
// hasDNSSECRecords 检查响应是否包含DNSSEC记录
func (s *Server) hasDNSSECRecords(response *dns.Msg) bool {
- // 检查响应中是否包含DNSSEC相关记录(DNSKEY、RRSIG、DS、NSEC、NSEC3等)
- for _, rr := range response.Answer {
- if _, ok := rr.(*dns.DNSKEY); ok {
- return true
- }
- if _, ok := rr.(*dns.RRSIG); ok {
- return true
- }
- if _, ok := rr.(*dns.DS); ok {
- return true
- }
- if _, ok := rr.(*dns.NSEC); ok {
- return true
- }
- if _, ok := rr.(*dns.NSEC3); ok {
- return true
- }
- }
- for _, rr := range response.Ns {
- if _, ok := rr.(*dns.DNSKEY); ok {
- return true
- }
- if _, ok := rr.(*dns.RRSIG); ok {
- return true
- }
- if _, ok := rr.(*dns.DS); ok {
- return true
- }
- if _, ok := rr.(*dns.NSEC); ok {
- return true
- }
- if _, ok := rr.(*dns.NSEC3); ok {
- return true
- }
- }
- for _, rr := range response.Extra {
- if _, ok := rr.(*dns.DNSKEY); ok {
- return true
- }
- if _, ok := rr.(*dns.RRSIG); ok {
- return true
- }
- if _, ok := rr.(*dns.DS); ok {
- return true
- }
- if _, ok := rr.(*dns.NSEC); ok {
- return true
- }
- if _, ok := rr.(*dns.NSEC3); ok {
- return true
- }
- }
- return false
+ // 直接调用包内的hasDNSSECRecords函数,避免重复代码
+ return hasDNSSECRecords(response)
}
// verifyDNSSEC 验证DNSSEC签名
func (s *Server) verifyDNSSEC(response *dns.Msg) bool {
- // 提取DNSKEY和RRSIG记录
+ // 提取DNSKEY和RRSIG记录,并按类型和名称组织记录
dnskeys := make(map[uint16]*dns.DNSKEY) // KeyTag -> DNSKEY
rrsigs := make([]*dns.RRSIG, 0)
+ // 按 (名称, 类型) 组织记录集,用于快速查找
+ rrSets := make(map[string]map[uint16][]dns.RR) // name -> type -> records
- // 从响应中提取所有DNSKEY和RRSIG记录
- for _, rr := range response.Answer {
+ // 定义处理单个记录的函数
+ processRecord := func(rr dns.RR) {
+ num := rr.Header().Rrtype
+ name := rr.Header().Name
+
+ // 组织记录集
+ if _, exists := rrSets[name]; !exists {
+ rrSets[name] = make(map[uint16][]dns.RR)
+ }
+ if _, exists := rrSets[name][num]; !exists {
+ rrSets[name][num] = make([]dns.RR, 0)
+ }
+ rrSets[name][num] = append(rrSets[name][num], rr)
+
+ // 特别处理DNSKEY和RRSIG
if dnskey, ok := rr.(*dns.DNSKEY); ok {
tag := dnskey.KeyTag()
dnskeys[tag] = dnskey
@@ -1936,21 +2077,16 @@ func (s *Server) verifyDNSSEC(response *dns.Msg) bool {
rrsigs = append(rrsigs, rrsig)
}
}
+
+ // 一次遍历所有响应部分,同时完成记录收集和组织
+ for _, rr := range response.Answer {
+ processRecord(rr)
+ }
for _, rr := range response.Ns {
- if dnskey, ok := rr.(*dns.DNSKEY); ok {
- tag := dnskey.KeyTag()
- dnskeys[tag] = dnskey
- } else if rrsig, ok := rr.(*dns.RRSIG); ok {
- rrsigs = append(rrsigs, rrsig)
- }
+ processRecord(rr)
}
for _, rr := range response.Extra {
- if dnskey, ok := rr.(*dns.DNSKEY); ok {
- tag := dnskey.KeyTag()
- dnskeys[tag] = dnskey
- } else if rrsig, ok := rr.(*dns.RRSIG); ok {
- rrsigs = append(rrsigs, rrsig)
- }
+ processRecord(rr)
}
// 如果没有RRSIG记录,验证失败
@@ -1975,18 +2111,10 @@ func (s *Server) verifyDNSSEC(response *dns.Msg) bool {
continue
}
- // 收集需要验证的记录集
- rrset := make([]dns.RR, 0)
- for _, rr := range response.Answer {
- if rr.Header().Name == rrsig.Header().Name && rr.Header().Rrtype == rrsig.TypeCovered {
- rrset = append(rrset, rr)
- }
- }
- for _, rr := range response.Ns {
- if rr.Header().Name == rrsig.Header().Name && rr.Header().Rrtype == rrsig.TypeCovered {
- rrset = append(rrset, rr)
- }
- }
+ // 快速查找需要验证的记录集
+ name := rrsig.Header().Name
+ typeCovered := rrsig.TypeCovered
+ rrset := rrSets[name][typeCovered]
// 验证签名
if len(rrset) > 0 {
@@ -2019,30 +2147,44 @@ func (s *Server) updateDomainDNSSECStatus(domain string, dnssec bool) {
s.resolvedDomains[domain] = &BlockedDomain{
Domain: domain,
Count: 1,
- LastSeen: time.Now(),
+ LastSeen: time.Now().UnixNano(),
DNSSEC: dnssec,
}
}
- // 更新domainDNSSECStatus映射
+ // 更新domainDNSSECStatus映射(使用单独的锁)
+ s.domainDNSSECStatusMutex.Lock()
s.domainDNSSECStatus[domain] = dnssec
+ s.domainDNSSECStatusMutex.Unlock()
}
// updateResolvedDomainStats 更新解析域名统计
func (s *Server) updateResolvedDomainStats(domain string) {
- s.resolvedDomainsMutex.Lock()
- defer s.resolvedDomainsMutex.Unlock()
+ // 先尝试读锁,检查条目是否存在
+ s.resolvedDomainsMutex.RLock()
+ entry, exists := s.resolvedDomains[domain]
+ s.resolvedDomainsMutex.RUnlock()
- if entry, exists := s.resolvedDomains[domain]; exists {
- entry.Count++
- entry.LastSeen = time.Now()
+ if exists {
+ // 使用原子操作更新计数和时间戳
+ atomic.AddInt64(&entry.Count, 1)
+ atomic.StoreInt64(&entry.LastSeen, time.Now().UnixNano())
} else {
- s.resolvedDomains[domain] = &BlockedDomain{
- Domain: domain,
- Count: 1,
- LastSeen: time.Now(),
- DNSSEC: false,
+ // 获取写锁,创建新条目
+ s.resolvedDomainsMutex.Lock()
+ // 再次检查,避免竞态条件
+ if entry, exists := s.resolvedDomains[domain]; exists {
+ atomic.AddInt64(&entry.Count, 1)
+ atomic.StoreInt64(&entry.LastSeen, time.Now().UnixNano())
+ } else {
+ s.resolvedDomains[domain] = &BlockedDomain{
+ Domain: domain,
+ Count: 1,
+ LastSeen: time.Now().UnixNano(),
+ DNSSEC: false,
+ }
}
+ s.resolvedDomainsMutex.Unlock()
}
}
@@ -2052,22 +2194,26 @@ func (s *Server) getServerStats(server string) *ServerStats {
stats, exists := s.serverStats[server]
s.serverStatsMutex.RUnlock()
- if !exists {
- // 创建新的服务器统计信息
- stats = &ServerStats{
- SuccessCount: 0,
- FailureCount: 0,
- LastResponse: time.Now(),
- ResponseTime: 0,
- ConnectionSpeed: 0,
- }
-
- // 加锁更新服务器统计信息
- s.serverStatsMutex.Lock()
- s.serverStats[server] = stats
- s.serverStatsMutex.Unlock()
+ if exists {
+ return stats
}
+ s.serverStatsMutex.Lock()
+ defer s.serverStatsMutex.Unlock()
+
+ if stats, exists := s.serverStats[server]; exists {
+ return stats
+ }
+
+ stats = &ServerStats{
+ SuccessCount: 0,
+ FailureCount: 0,
+ LastResponse: time.Now(),
+ ResponseTime: 0,
+ ConnectionSpeed: 0,
+ }
+
+ s.serverStats[server] = stats
return stats
}
@@ -2075,27 +2221,32 @@ func (s *Server) getServerStats(server string) *ServerStats {
func (s *Server) updateServerStats(server string, success bool, rtt time.Duration) {
stats := s.getServerStats(server)
- s.serverStatsMutex.Lock()
- defer s.serverStatsMutex.Unlock()
-
- // 更新统计信息
- stats.LastResponse = time.Now()
-
+ // 使用原子操作更新成功和失败计数
if success {
- stats.SuccessCount++
+ successCount := atomic.AddInt64(&stats.SuccessCount, 1)
+
+ // 只在需要更新平均响应时间时获取锁
+ s.serverStatsMutex.Lock()
+ stats.LastResponse = time.Now()
+
// 更新平均响应时间(简单移动平均)
- // 将所有值转换为纳秒进行计算,然后再转换回Duration
- if stats.SuccessCount == 1 {
+ if successCount == 1 {
// 第一次成功,直接使用当前响应时间
stats.ResponseTime = rtt
} else {
// 使用纳秒进行计算以避免类型不匹配
- prevTotal := stats.ResponseTime.Nanoseconds() * (stats.SuccessCount - 1)
+ prevTotal := stats.ResponseTime.Nanoseconds() * (successCount - 1)
newTotal := prevTotal + rtt.Nanoseconds()
- stats.ResponseTime = time.Duration(newTotal / stats.SuccessCount)
+ stats.ResponseTime = time.Duration(newTotal / successCount)
}
+ s.serverStatsMutex.Unlock()
} else {
- stats.FailureCount++
+ atomic.AddInt64(&stats.FailureCount, 1)
+
+ // 只更新LastResponse时获取锁
+ s.serverStatsMutex.Lock()
+ stats.LastResponse = time.Now()
+ s.serverStatsMutex.Unlock()
}
}
@@ -2109,80 +2260,81 @@ func (s *Server) selectWeightedRandomServer(servers []string) string {
return servers[0]
}
- // 计算每个服务器的权重
type serverWeight struct {
- server string
- weight int64
+ server string
+ weight int64
+ responseTime time.Duration
+ successCount int64
+ failureCount int64
}
var totalWeight int64
- weights := make([]serverWeight, 0, len(servers))
-
- // 获取所有服务器的平均响应时间,用于归一化
var totalResponseTime time.Duration
- validServers := 0
+ var validServers int
+ var currentWeight int64
- for _, server := range servers {
+ serversInfo := make([]serverWeight, len(servers))
+
+ for i, server := range servers {
stats := s.getServerStats(server)
+
+ serversInfo[i] = serverWeight{
+ server: server,
+ responseTime: stats.ResponseTime,
+ successCount: atomic.LoadInt64(&stats.SuccessCount),
+ failureCount: atomic.LoadInt64(&stats.FailureCount),
+ }
+
if stats.ResponseTime > 0 {
totalResponseTime += stats.ResponseTime
validServers++
}
}
- // 计算平均响应时间基准值
var avgResponseTime time.Duration
if validServers > 0 {
avgResponseTime = totalResponseTime / time.Duration(validServers)
} else {
- avgResponseTime = 1 * time.Second // 默认基准值
+ avgResponseTime = 1 * time.Second
}
- for _, server := range servers {
- stats := s.getServerStats(server)
+ var randomGen = rand.New(rand.NewSource(time.Now().UnixNano()))
- // 计算基础权重:成功次数 - 失败次数 * 2(失败权重更高)
- // 确保权重至少为1
- baseWeight := stats.SuccessCount - stats.FailureCount*2
+ for i := range serversInfo {
+ baseWeight := serversInfo[i].successCount - serversInfo[i].failureCount*2
if baseWeight < 1 {
baseWeight = 1
}
- // 计算响应时间调整因子:响应时间越短,因子越高
- // 如果没有响应时间数据,使用默认值1
- var responseFactor float64 = 1.0
- if stats.ResponseTime > 0 {
- // 使用平均响应时间作为基准,计算调整因子
- // 响应时间越短,因子越高,最高为2.0,最低为0.5
- responseFactor = float64(avgResponseTime) / float64(stats.ResponseTime)
- // 限制调整因子的范围,避免权重波动过大
- if responseFactor > 2.0 {
- responseFactor = 2.0
- } else if responseFactor < 0.5 {
- responseFactor = 0.5
+ var responseFactor int64 = 100
+ if serversInfo[i].responseTime > 0 {
+ if serversInfo[i].responseTime < avgResponseTime {
+ factor := (avgResponseTime.Nanoseconds() * 200) / serversInfo[i].responseTime.Nanoseconds()
+ if factor > 200 {
+ factor = 200
+ }
+ responseFactor = factor
+ } else {
+ factor := (avgResponseTime.Nanoseconds() * 200) / serversInfo[i].responseTime.Nanoseconds()
+ if factor < 50 {
+ factor = 50
+ }
+ responseFactor = factor
}
}
- // 综合计算最终权重,四舍五入到整数
- finalWeight := int64(float64(baseWeight) * responseFactor)
- // 确保最终权重至少为1
+ finalWeight := (baseWeight * responseFactor) / 100
if finalWeight < 1 {
finalWeight = 1
}
- weights = append(weights, serverWeight{server, finalWeight})
+ serversInfo[i].weight = finalWeight
totalWeight += finalWeight
}
- // 随机选择一个权重
- random := time.Now().UnixNano() % totalWeight
- if random < 0 {
- random += totalWeight
- }
+ random := randomGen.Int63n(totalWeight)
- // 选择对应的服务器
- var currentWeight int64
- for _, sw := range weights {
+ for _, sw := range serversInfo {
currentWeight += sw.weight
if random < currentWeight {
return sw.server
@@ -2195,28 +2347,22 @@ func (s *Server) selectWeightedRandomServer(servers []string) string {
// measureServerSpeed 测量服务器TCP连接速度
func (s *Server) measureServerSpeed(server string) time.Duration {
- // 提取服务器地址和端口
addr := server
if !strings.Contains(server, ":") {
addr = server + ":53"
}
- // 测量TCP连接时间
startTime := time.Now()
conn, err := net.DialTimeout("tcp", addr, 2*time.Second)
if err != nil {
- // 连接失败,返回最大持续时间
return 2 * time.Second
}
defer conn.Close()
- // 计算连接建立时间
connTime := time.Since(startTime)
- // 更新服务器连接速度
stats := s.getServerStats(server)
s.serverStatsMutex.Lock()
- // 使用指数移动平均更新连接速度
stats.ConnectionSpeed = (stats.ConnectionSpeed*3 + connTime) / 4
s.serverStatsMutex.Unlock()
@@ -2304,16 +2450,13 @@ func (s *Server) addQueryLog(clientIP, domain, queryType string, responseTime in
ResponseCode: responseCode,
}
- // 添加到日志列表
- s.queryLogsMutex.Lock()
- defer s.queryLogsMutex.Unlock()
-
- // 插入到列表开头
- s.queryLogs = append([]QueryLog{log}, s.queryLogs...)
-
- // 限制日志数量
- if len(s.queryLogs) > s.maxQueryLogs {
- s.queryLogs = s.queryLogs[:s.maxQueryLogs]
+ // 发送到日志处理通道(非阻塞)
+ select {
+ case s.logChannel <- log:
+ // 日志发送成功
+ default:
+ // 通道已满,丢弃日志以避免阻塞请求处理
+ logger.Warn("日志通道已满,丢弃一条日志记录")
}
}
@@ -2556,7 +2699,7 @@ func (s *Server) GetRecentBlockedDomains(limit int) []BlockedDomain {
// 按时间排序
sort.Slice(domains, func(i, j int) bool {
- return domains[i].LastSeen.After(domains[j].LastSeen)
+ return domains[i].LastSeen > domains[j].LastSeen
})
// 返回限制数量
@@ -2698,8 +2841,36 @@ func (s *Server) loadStatsData() {
// 恢复统计数据
s.statsMutex.Lock()
if statsData.Stats != nil {
- s.stats = statsData.Stats
- // 确保使用当前配置中的EnableDNSSEC值,覆盖从文件加载的值
+ // 只恢复有效数据,避免破坏统计关系
+ s.stats.Queries += statsData.Stats.Queries
+ s.stats.Blocked += statsData.Stats.Blocked
+ s.stats.Allowed += statsData.Stats.Allowed
+ s.stats.Errors += statsData.Stats.Errors
+ s.stats.TotalResponseTime += statsData.Stats.TotalResponseTime
+ s.stats.DNSSECQueries += statsData.Stats.DNSSECQueries
+ s.stats.DNSSECSuccess += statsData.Stats.DNSSECSuccess
+ s.stats.DNSSECFailed += statsData.Stats.DNSSECFailed
+
+ // 重新计算平均响应时间,确保一致性
+ if s.stats.Queries > 0 {
+ s.stats.AvgResponseTime = float64(s.stats.TotalResponseTime) / float64(s.stats.Queries)
+ // 限制平均响应时间的范围,避免显示异常大的值
+ if s.stats.AvgResponseTime > 60000 {
+ s.stats.AvgResponseTime = 60000
+ }
+ }
+
+ // 合并查询类型统计
+ for k, v := range statsData.Stats.QueryTypes {
+ s.stats.QueryTypes[k] += v
+ }
+
+ // 合并来源IP统计
+ for ip := range statsData.Stats.SourceIPs {
+ s.stats.SourceIPs[ip] = true
+ }
+
+ // 确保使用当前配置中的EnableDNSSEC值
s.stats.DNSSECEnabled = s.config.EnableDNSSEC
}
s.statsMutex.Unlock()
@@ -2792,6 +2963,46 @@ func (s *Server) loadQueryLogs() {
logger.Info("查询日志加载成功", "count", len(logs))
}
+// processLogs 异步处理日志记录
+func (s *Server) processLogs() {
+ for {
+ select {
+ case logEntry, ok := <-s.logChannel:
+ if !ok {
+ // 通道关闭,退出循环
+ return
+ }
+
+ // 加锁保护queryLogs
+ s.queryLogsMutex.Lock()
+
+ // 如果日志数量超过最大限制,删除最旧的日志
+ if len(s.queryLogs) >= s.maxQueryLogs {
+ // 保留最新的s.maxQueryLogs条日志
+ newLogs := make([]QueryLog, 0, s.maxQueryLogs)
+ // 复制最新的日志到新切片
+ for i := len(s.queryLogs) - s.maxQueryLogs + 1; i < len(s.queryLogs); i++ {
+ newLogs = append(newLogs, s.queryLogs[i])
+ }
+ // 添加新日志
+ newLogs = append(newLogs, logEntry)
+ // 替换原有日志
+ s.queryLogs = newLogs
+ } else {
+ // 直接添加新日志
+ s.queryLogs = append(s.queryLogs, logEntry)
+ }
+
+ // 解锁
+ s.queryLogsMutex.Unlock()
+
+ case <-s.ctx.Done():
+ // 上下文取消,退出循环
+ return
+ }
+ }
+}
+
// saveStatsData 保存统计数据到文件
func (s *Server) saveStatsData() {
// 获取绝对路径以避免工作目录问题
diff --git a/download.sh b/download.sh
new file mode 100755
index 0000000..9b10210
--- /dev/null
+++ b/download.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+set -e -f -u -x
+
+# This script syncs companies DB that we bundle with AdGuard Home. The source
+# for this database is https://github.com/AdguardTeam/companiesdb.
+#
+trackers_url='https://raw.githubusercontent.com/AdguardTeam/companiesdb/main/dist/trackers.json'
+output='./trackers.json'
+readonly trackers_url output
+
+curl -o "$output" -v "$trackers_url"
diff --git a/gfw/manager.go b/gfw/manager.go
new file mode 100644
index 0000000..9cfde7f
--- /dev/null
+++ b/gfw/manager.go
@@ -0,0 +1,241 @@
+package gfw
+
+import (
+ "encoding/base64"
+ "fmt"
+ "os"
+ "regexp"
+ "strings"
+ "sync"
+
+ "dns-server/config"
+ "dns-server/logger"
+)
+
+// regexRule 正则规则结构,包含编译后的表达式和原始字符串
+type regexRule struct {
+ pattern *regexp.Regexp
+ original string
+}
+
+// GFWListManager GFWList管理器
+type GFWListManager struct {
+ config *config.GFWListConfig
+ domainRules map[string]bool
+ regexRules []regexRule
+ rulesMutex sync.RWMutex
+}
+
+// NewGFWListManager 创建GFWList管理器实例
+func NewGFWListManager(config *config.GFWListConfig) *GFWListManager {
+ return &GFWListManager{
+ config: config,
+ domainRules: make(map[string]bool),
+ regexRules: []regexRule{},
+ }
+}
+
+// LoadRules 加载GFWList规则
+func (m *GFWListManager) LoadRules() error {
+ // 如果GFWList功能未启用,不加载规则
+ if !m.config.Enabled {
+ return nil
+ }
+
+ m.rulesMutex.Lock()
+ defer m.rulesMutex.Unlock()
+
+ // 清空现有规则
+ m.domainRules = make(map[string]bool)
+ m.regexRules = []regexRule{}
+
+ if m.config.Content == "" {
+ return nil // 没有GFWList内容,直接返回
+ }
+
+ // 从文件路径读取GFWList内容
+ fileContent, err := os.ReadFile(m.config.Content)
+ if err != nil {
+ return fmt.Errorf("读取GFWList文件失败: %w", err)
+ }
+
+ rawContent := string(fileContent)
+ var ruleContent string
+
+ // 过滤注释行,收集可能的Base64内容
+ var base64Content strings.Builder
+ lines := strings.Split(rawContent, "\n")
+ for _, line := range lines {
+ line = strings.TrimSpace(line)
+ if line == "" || strings.HasPrefix(line, "!") || strings.HasPrefix(line, "[") {
+ // 跳过注释行和头信息行
+ continue
+ }
+ base64Content.WriteString(line)
+ }
+
+ // 尝试Base64解码
+ decoded, err := base64.StdEncoding.DecodeString(base64Content.String())
+ if err == nil {
+ // 解码成功,使用解码后的内容
+ ruleContent = string(decoded)
+ logger.Info("GFWList文件为Base64编码,已成功解码")
+ } else {
+ // 解码失败,使用原始内容(可能是纯文本格式)
+ ruleContent = rawContent
+ logger.Info("GFWList文件为纯文本格式,直接解析")
+ }
+
+ // 按行解析规则内容
+ ruleLines := strings.Split(ruleContent, "\n")
+ for _, line := range ruleLines {
+ line = strings.TrimSpace(line)
+ if line == "" || strings.HasPrefix(line, "!") || strings.HasPrefix(line, "[") {
+ // 跳过空行、注释行和头信息行
+ continue
+ }
+ m.parseRule(line)
+ }
+
+ logger.Info(fmt.Sprintf("GFWList规则加载完成,域名规则: %d, 正则规则: %d",
+ len(m.domainRules), len(m.regexRules)))
+ return nil
+}
+
+// parseRule 解析规则行
+func (m *GFWListManager) parseRule(line string) {
+ // 保存原始规则用于后续使用
+ originalLine := line
+
+ // 处理注释
+ if strings.HasPrefix(line, "!") || strings.HasPrefix(line, "#") || line == "" {
+ return
+ }
+
+ // 移除规则选项部分(暂时不处理规则选项)
+ if strings.Contains(line, "$") {
+ parts := strings.SplitN(line, "$", 2)
+ line = parts[0]
+ // 规则选项暂时不处理
+ }
+
+ // 处理不同类型的规则
+ switch {
+ case strings.HasPrefix(line, "||") && strings.HasSuffix(line, "^"):
+ // AdGuardHome域名规则格式: ||example.com^
+ domain := strings.TrimSuffix(strings.TrimPrefix(line, "||"), "^")
+ m.addDomainRule(domain, originalLine)
+
+ case strings.HasPrefix(line, "||"):
+ // 域名片段匹配规则: ||google 匹配任何包含google的域名
+ domain := strings.TrimPrefix(line, "||")
+ // 添加精确域名匹配
+ m.addDomainRule(domain, originalLine)
+ // 同时添加正则表达式规则,匹配任何包含该域名片段的域名
+ if re, err := regexp.Compile("(?i).*" + regexp.QuoteMeta(domain) + ".*"); err == nil {
+ m.addRegexRule(re, originalLine)
+ }
+
+ case strings.HasPrefix(line, "*"):
+ // 通配符规则,转换为正则表达式
+ pattern := strings.ReplaceAll(line, "*", ".*")
+ pattern = "^" + pattern + "$"
+ if re, err := regexp.Compile(pattern); err == nil {
+ // 保存原始规则字符串
+ m.addRegexRule(re, originalLine)
+ }
+
+ case strings.HasPrefix(line, "/") && strings.HasSuffix(line, "/"):
+ // 正则表达式匹配规则:/regex/ 格式,不区分大小写
+ pattern := strings.TrimPrefix(strings.TrimSuffix(line, "/"), "/")
+ // 编译为不区分大小写的正则表达式,确保能匹配域名中任意位置
+ // 对于像 /domain/ 这样的规则,应该匹配包含 domain 字符串的任何域名
+ if re, err := regexp.Compile("(?i).*" + regexp.QuoteMeta(pattern) + ".*"); err == nil {
+ // 保存原始规则字符串
+ m.addRegexRule(re, originalLine)
+ }
+
+ case strings.HasPrefix(line, "|") && strings.HasSuffix(line, "|"):
+ // 完整URL匹配规则
+ urlPattern := strings.TrimPrefix(strings.TrimSuffix(line, "|"), "|")
+ // 将URL模式转换为正则表达式
+ pattern := "^" + regexp.QuoteMeta(urlPattern) + "$"
+ if re, err := regexp.Compile(pattern); err == nil {
+ m.addRegexRule(re, originalLine)
+ }
+
+ case strings.HasPrefix(line, "|"):
+ // URL开头匹配规则
+ urlPattern := strings.TrimPrefix(line, "|")
+ pattern := "^" + regexp.QuoteMeta(urlPattern)
+ if re, err := regexp.Compile(pattern); err == nil {
+ m.addRegexRule(re, originalLine)
+ }
+
+ case strings.HasSuffix(line, "|"):
+ // URL结尾匹配规则
+ urlPattern := strings.TrimSuffix(line, "|")
+ pattern := regexp.QuoteMeta(urlPattern) + "$"
+ if re, err := regexp.Compile(pattern); err == nil {
+ m.addRegexRule(re, originalLine)
+ }
+
+ default:
+ // 默认作为普通域名规则
+ m.addDomainRule(line, originalLine)
+ }
+}
+
+// addDomainRule 添加域名规则
+func (m *GFWListManager) addDomainRule(domain string, original string) {
+ m.domainRules[domain] = true
+}
+
+// addRegexRule 添加正则表达式规则
+func (m *GFWListManager) addRegexRule(re *regexp.Regexp, original string) {
+ rule := regexRule{
+ pattern: re,
+ original: original,
+ }
+ m.regexRules = append(m.regexRules, rule)
+}
+
+// IsMatch 检查域名是否匹配GFWList规则
+func (m *GFWListManager) IsMatch(domain string) bool {
+ m.rulesMutex.RLock()
+ defer m.rulesMutex.RUnlock()
+
+ // 预处理域名,去除可能的端口号
+ if strings.Contains(domain, ":") {
+ parts := strings.Split(domain, ":")
+ domain = parts[0]
+ }
+
+ // 检查精确域名匹配
+ if m.domainRules[domain] {
+ return true
+ }
+
+ // 检查子域名匹配
+ parts := strings.Split(domain, ".")
+ for i := 0; i < len(parts)-1; i++ {
+ subdomain := strings.Join(parts[i:], ".")
+ if m.domainRules[subdomain] {
+ return true
+ }
+ }
+
+ // 检查正则表达式匹配
+ for _, re := range m.regexRules {
+ if re.pattern.MatchString(domain) {
+ return true
+ }
+ }
+
+ return false
+}
+
+// GetGFWListIP 获取GFWList的目标IP地址
+func (m *GFWListManager) GetGFWListIP() string {
+ return m.config.IP
+}
diff --git a/http/server.go b/http/server.go
index 70e9181..2631e31 100644
--- a/http/server.go
+++ b/http/server.go
@@ -12,6 +12,7 @@ import (
"dns-server/config"
"dns-server/dns"
+ "dns-server/gfw"
"dns-server/logger"
"dns-server/shield"
@@ -24,6 +25,7 @@ type Server struct {
config *config.HTTPConfig
dnsServer *dns.Server
shieldManager *shield.ShieldManager
+ gfwManager *gfw.GFWListManager
server *http.Server
// 会话管理相关字段
@@ -39,12 +41,13 @@ type Server struct {
}
// NewServer 创建HTTP服务器实例
-func NewServer(globalConfig *config.Config, dnsServer *dns.Server, shieldManager *shield.ShieldManager) *Server {
+func NewServer(globalConfig *config.Config, dnsServer *dns.Server, shieldManager *shield.ShieldManager, gfwManager *gfw.GFWListManager) *Server {
server := &Server{
globalConfig: globalConfig,
config: &globalConfig.HTTP,
dnsServer: dnsServer,
shieldManager: shieldManager,
+ gfwManager: gfwManager,
upgrader: websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
@@ -584,7 +587,7 @@ func (s *Server) handleRecentBlockedDomains(w http.ResponseWriter, r *http.Reque
for i, domain := range domains {
result[i] = map[string]interface{}{
"domain": domain.Domain,
- "time": domain.LastSeen.Format("15:04:05"),
+ "time": time.Unix(domain.LastSeen, 0).Format("15:04:05"),
}
}
@@ -1239,6 +1242,10 @@ func (s *Server) handleConfig(w http.ResponseWriter, r *http.Request) {
"blacklists": s.globalConfig.Shield.Blacklists,
"updateInterval": s.globalConfig.Shield.UpdateInterval,
},
+ "GFWList": map[string]interface{}{
+ "ip": s.globalConfig.GFWList.IP,
+ "content": s.globalConfig.GFWList.Content,
+ },
"DNSServer": map[string]interface{}{
"port": s.globalConfig.DNS.Port,
"UpstreamServers": s.globalConfig.DNS.UpstreamDNS,
@@ -1272,6 +1279,10 @@ func (s *Server) handleConfig(w http.ResponseWriter, r *http.Request) {
Blacklists []config.BlacklistEntry `json:"blacklists"`
UpdateInterval int `json:"updateInterval"`
} `json:"shield"`
+ GFWList struct {
+ IP string `json:"ip"`
+ Content string `json:"content"`
+ } `json:"gfwList"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
@@ -1334,6 +1345,17 @@ func (s *Server) handleConfig(w http.ResponseWriter, r *http.Request) {
s.globalConfig.Shield.CustomBlockIP = req.Shield.CustomBlockIP
}
+ // 更新GFWList配置
+ s.globalConfig.GFWList.IP = req.GFWList.IP
+ s.globalConfig.GFWList.Content = req.GFWList.Content
+
+ // 重新加载GFWList规则
+ if s.gfwManager != nil {
+ if err := s.gfwManager.LoadRules(); err != nil {
+ logger.Error("重新加载GFWList规则失败", "error", err)
+ }
+ }
+
// 更新黑名单配置
if req.Shield.Blacklists != nil {
// 验证黑名单配置
diff --git a/main.go b/main.go
index 960a439..d24dd09 100644
--- a/main.go
+++ b/main.go
@@ -22,6 +22,7 @@ import (
"dns-server/config"
"dns-server/dns"
+ "dns-server/gfw"
"dns-server/http"
"dns-server/logger"
"dns-server/shield"
@@ -82,6 +83,11 @@ func createDefaultConfig(configFile string) error {
"customBlockIP": "",
"statsSaveInterval": 60
},
+ "gfwList": {
+ "ip": "127.0.0.1",
+ "content": "./data/gfwlist.txt",
+ "enabled": true
+ },
"log": {
"level": "debug",
"maxSize": 100,
@@ -129,6 +135,13 @@ func createRequiredFiles(cfg *config.Config) error {
}
}
+ // 创建GFWList文件
+ if _, err := os.Stat("data/gfwlist.txt"); os.IsNotExist(err) {
+ if err := os.WriteFile("data/gfwlist.txt", []byte("# GFWList规则文件\n# 格式:每行一条规则\n# 例如:www.google.com\n"), 0644); err != nil {
+ return fmt.Errorf("创建GFWList文件失败: %w", err)
+ }
+ }
+
// 创建统计数据文件
if _, err := os.Stat("data/stats.json"); os.IsNotExist(err) {
if err := os.WriteFile("data/stats.json", []byte("{}"), 0644); err != nil {
@@ -188,8 +201,14 @@ func main() {
logger.Error("加载屏蔽规则失败", "error", err)
}
+ // 初始化GFWList管理系统
+ gfwManager := gfw.NewGFWListManager(&cfg.GFWList)
+ if err := gfwManager.LoadRules(); err != nil {
+ logger.Error("加载GFWList规则失败", "error", err)
+ }
+
// 启动DNS服务器
- dnsServer := dns.NewServer(&cfg.DNS, &cfg.Shield, shieldManager)
+ dnsServer := dns.NewServer(&cfg.DNS, &cfg.Shield, shieldManager, &cfg.GFWList, gfwManager)
go func() {
if err := dnsServer.Start(); err != nil {
logger.Error("DNS服务器启动失败", "error", err)
@@ -198,7 +217,7 @@ func main() {
}()
// 启动HTTP控制台服务器
- httpServer := http.NewServer(cfg, dnsServer, shieldManager)
+ httpServer := http.NewServer(cfg, dnsServer, shieldManager, gfwManager)
go func() {
if err := httpServer.Start(); err != nil {
logger.Error("HTTP控制台服务器启动失败", "error", err)
diff --git a/shield/manager.go b/shield/manager.go
index 15e82cc..99bc56a 100644
--- a/shield/manager.go
+++ b/shield/manager.go
@@ -544,6 +544,7 @@ func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interf
"blockRule": "",
"blockRuleType": "",
"blocksource": "",
+ "isGFWList": false,
"excluded": false,
"excludeRule": "",
"excludeRuleType": "",
@@ -631,6 +632,7 @@ func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interf
result["blockRule"] = m.domainRulesOriginal[domain]
result["blockRuleType"] = "exact_domain"
result["blocksource"] = m.domainRulesSource[domain]
+ result["isGFWList"] = m.domainRulesSource[domain] == "GFWList"
return result
}
@@ -640,6 +642,7 @@ func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interf
result["blockRule"] = m.domainRulesOriginal[domain]
result["blockRuleType"] = "exact_domain"
result["blocksource"] = m.domainRulesSource[domain]
+ result["isGFWList"] = m.domainRulesSource[domain] == "GFWList"
return result
}
@@ -654,6 +657,7 @@ func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interf
result["blockRule"] = m.domainRulesOriginal[subdomain]
result["blockRuleType"] = "subdomain"
result["blocksource"] = m.domainRulesSource[subdomain]
+ result["isGFWList"] = m.domainRulesSource[subdomain] == "GFWList"
return result
}
}
@@ -666,6 +670,7 @@ func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interf
result["blockRule"] = m.domainRulesOriginal[subdomain]
result["blockRuleType"] = "subdomain"
result["blocksource"] = m.domainRulesSource[subdomain]
+ result["isGFWList"] = m.domainRulesSource[subdomain] == "GFWList"
return result
}
}
@@ -677,6 +682,7 @@ func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interf
result["blockRule"] = re.original
result["blockRuleType"] = "regex"
result["blocksource"] = re.source
+ result["isGFWList"] = re.source == "GFWList"
return result
}
}
@@ -688,6 +694,7 @@ func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interf
result["blockRule"] = re.original
result["blockRuleType"] = "regex"
result["blocksource"] = re.source
+ result["isGFWList"] = re.source == "GFWList"
return result
}
}
diff --git a/static/css/font-awesome.min.css b/static/css/font-awesome.min.css
new file mode 100644
index 0000000..7a3c2f6
--- /dev/null
+++ b/static/css/font-awesome.min.css
@@ -0,0 +1,3045 @@
+/*!
+ * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
+ * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */
+@font-face {
+ font-family: 'FontAwesome';
+ src: url('./webfonts/fontawesome-webfont.eot?v=4.7.0');
+ src: url('./webfonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('./webfonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('./webfonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('./webfonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('./webfonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');
+ font-weight: normal;
+ font-style: normal
+}
+
+.fa {
+ display: inline-block;
+ font: normal normal normal 14px/1 FontAwesome;
+ font-size: inherit;
+ text-rendering: auto;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale
+}
+
+.fa-lg {
+ font-size: 1.33333333em;
+ line-height: .75em;
+ vertical-align: -15%
+}
+
+.fa-2x {
+ font-size: 2em
+}
+
+.fa-3x {
+ font-size: 3em
+}
+
+.fa-4x {
+ font-size: 4em
+}
+
+.fa-5x {
+ font-size: 5em
+}
+
+.fa-fw {
+ width: 1.28571429em;
+ text-align: center
+}
+
+.fa-ul {
+ padding-left: 0;
+ margin-left: 2.14285714em;
+ list-style-type: none
+}
+
+.fa-ul>li {
+ position: relative
+}
+
+.fa-li {
+ position: absolute;
+ left: -2.14285714em;
+ width: 2.14285714em;
+ top: .14285714em;
+ text-align: center
+}
+
+.fa-li.fa-lg {
+ left: -1.85714286em
+}
+
+.fa-border {
+ padding: .2em .25em .15em;
+ border: solid .08em #eee;
+ border-radius: .1em
+}
+
+.fa-pull-left {
+ float: left
+}
+
+.fa-pull-right {
+ float: right
+}
+
+.fa.fa-pull-left {
+ margin-right: .3em
+}
+
+.fa.fa-pull-right {
+ margin-left: .3em
+}
+
+.pull-right {
+ float: right
+}
+
+.pull-left {
+ float: left
+}
+
+.fa.pull-left {
+ margin-right: .3em
+}
+
+.fa.pull-right {
+ margin-left: .3em
+}
+
+.fa-spin {
+ -webkit-animation: fa-spin 2s infinite linear;
+ animation: fa-spin 2s infinite linear
+}
+
+.fa-pulse {
+ -webkit-animation: fa-spin 1s infinite steps(8);
+ animation: fa-spin 1s infinite steps(8)
+}
+
+@-webkit-keyframes fa-spin {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg)
+ }
+
+ 100% {
+ -webkit-transform: rotate(359deg);
+ transform: rotate(359deg)
+ }
+}
+
+@keyframes fa-spin {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg)
+ }
+
+ 100% {
+ -webkit-transform: rotate(359deg);
+ transform: rotate(359deg)
+ }
+}
+
+.fa-rotate-90 {
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
+ -webkit-transform: rotate(90deg);
+ -ms-transform: rotate(90deg);
+ transform: rotate(90deg)
+}
+
+.fa-rotate-180 {
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
+ -webkit-transform: rotate(180deg);
+ -ms-transform: rotate(180deg);
+ transform: rotate(180deg)
+}
+
+.fa-rotate-270 {
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
+ -webkit-transform: rotate(270deg);
+ -ms-transform: rotate(270deg);
+ transform: rotate(270deg)
+}
+
+.fa-flip-horizontal {
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
+ -webkit-transform: scale(-1, 1);
+ -ms-transform: scale(-1, 1);
+ transform: scale(-1, 1)
+}
+
+.fa-flip-vertical {
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
+ -webkit-transform: scale(1, -1);
+ -ms-transform: scale(1, -1);
+ transform: scale(1, -1)
+}
+
+:root .fa-rotate-90,
+:root .fa-rotate-180,
+:root .fa-rotate-270,
+:root .fa-flip-horizontal,
+:root .fa-flip-vertical {
+ filter: none
+}
+
+.fa-stack {
+ position: relative;
+ display: inline-block;
+ width: 2em;
+ height: 2em;
+ line-height: 2em;
+ vertical-align: middle
+}
+
+.fa-stack-1x,
+.fa-stack-2x {
+ position: absolute;
+ left: 0;
+ width: 100%;
+ text-align: center
+}
+
+.fa-stack-1x {
+ line-height: inherit
+}
+
+.fa-stack-2x {
+ font-size: 2em
+}
+
+.fa-inverse {
+ color: #fff
+}
+
+.fa-glass:before {
+ content: "\f000"
+}
+
+.fa-music:before {
+ content: "\f001"
+}
+
+.fa-search:before {
+ content: "\f002"
+}
+
+.fa-envelope-o:before {
+ content: "\f003"
+}
+
+.fa-heart:before {
+ content: "\f004"
+}
+
+.fa-star:before {
+ content: "\f005"
+}
+
+.fa-star-o:before {
+ content: "\f006"
+}
+
+.fa-user:before {
+ content: "\f007"
+}
+
+.fa-film:before {
+ content: "\f008"
+}
+
+.fa-th-large:before {
+ content: "\f009"
+}
+
+.fa-th:before {
+ content: "\f00a"
+}
+
+.fa-th-list:before {
+ content: "\f00b"
+}
+
+.fa-check:before {
+ content: "\f00c"
+}
+
+.fa-remove:before,
+.fa-close:before,
+.fa-times:before {
+ content: "\f00d"
+}
+
+.fa-search-plus:before {
+ content: "\f00e"
+}
+
+.fa-search-minus:before {
+ content: "\f010"
+}
+
+.fa-power-off:before {
+ content: "\f011"
+}
+
+.fa-signal:before {
+ content: "\f012"
+}
+
+.fa-gear:before,
+.fa-cog:before {
+ content: "\f013"
+}
+
+.fa-trash-o:before {
+ content: "\f014"
+}
+
+.fa-home:before {
+ content: "\f015"
+}
+
+.fa-file-o:before {
+ content: "\f016"
+}
+
+.fa-clock-o:before {
+ content: "\f017"
+}
+
+.fa-road:before {
+ content: "\f018"
+}
+
+.fa-download:before {
+ content: "\f019"
+}
+
+.fa-arrow-circle-o-down:before {
+ content: "\f01a"
+}
+
+.fa-arrow-circle-o-up:before {
+ content: "\f01b"
+}
+
+.fa-inbox:before {
+ content: "\f01c"
+}
+
+.fa-play-circle-o:before {
+ content: "\f01d"
+}
+
+.fa-rotate-right:before,
+.fa-repeat:before {
+ content: "\f01e"
+}
+
+.fa-refresh:before {
+ content: "\f021"
+}
+
+.fa-list-alt:before {
+ content: "\f022"
+}
+
+.fa-lock:before {
+ content: "\f023"
+}
+
+.fa-flag:before {
+ content: "\f024"
+}
+
+.fa-headphones:before {
+ content: "\f025"
+}
+
+.fa-volume-off:before {
+ content: "\f026"
+}
+
+.fa-volume-down:before {
+ content: "\f027"
+}
+
+.fa-volume-up:before {
+ content: "\f028"
+}
+
+.fa-qrcode:before {
+ content: "\f029"
+}
+
+.fa-barcode:before {
+ content: "\f02a"
+}
+
+.fa-tag:before {
+ content: "\f02b"
+}
+
+.fa-tags:before {
+ content: "\f02c"
+}
+
+.fa-book:before {
+ content: "\f02d"
+}
+
+.fa-bookmark:before {
+ content: "\f02e"
+}
+
+.fa-print:before {
+ content: "\f02f"
+}
+
+.fa-camera:before {
+ content: "\f030"
+}
+
+.fa-font:before {
+ content: "\f031"
+}
+
+.fa-bold:before {
+ content: "\f032"
+}
+
+.fa-italic:before {
+ content: "\f033"
+}
+
+.fa-text-height:before {
+ content: "\f034"
+}
+
+.fa-text-width:before {
+ content: "\f035"
+}
+
+.fa-align-left:before {
+ content: "\f036"
+}
+
+.fa-align-center:before {
+ content: "\f037"
+}
+
+.fa-align-right:before {
+ content: "\f038"
+}
+
+.fa-align-justify:before {
+ content: "\f039"
+}
+
+.fa-list:before {
+ content: "\f03a"
+}
+
+.fa-dedent:before,
+.fa-outdent:before {
+ content: "\f03b"
+}
+
+.fa-indent:before {
+ content: "\f03c"
+}
+
+.fa-video-camera:before {
+ content: "\f03d"
+}
+
+.fa-photo:before,
+.fa-image:before,
+.fa-picture-o:before {
+ content: "\f03e"
+}
+
+.fa-pencil:before {
+ content: "\f040"
+}
+
+.fa-map-marker:before {
+ content: "\f041"
+}
+
+.fa-adjust:before {
+ content: "\f042"
+}
+
+.fa-tint:before {
+ content: "\f043"
+}
+
+.fa-edit:before,
+.fa-pencil-square-o:before {
+ content: "\f044"
+}
+
+.fa-share-square-o:before {
+ content: "\f045"
+}
+
+.fa-check-square-o:before {
+ content: "\f046"
+}
+
+.fa-arrows:before {
+ content: "\f047"
+}
+
+.fa-step-backward:before {
+ content: "\f048"
+}
+
+.fa-fast-backward:before {
+ content: "\f049"
+}
+
+.fa-backward:before {
+ content: "\f04a"
+}
+
+.fa-play:before {
+ content: "\f04b"
+}
+
+.fa-pause:before {
+ content: "\f04c"
+}
+
+.fa-stop:before {
+ content: "\f04d"
+}
+
+.fa-forward:before {
+ content: "\f04e"
+}
+
+.fa-fast-forward:before {
+ content: "\f050"
+}
+
+.fa-step-forward:before {
+ content: "\f051"
+}
+
+.fa-eject:before {
+ content: "\f052"
+}
+
+.fa-chevron-left:before {
+ content: "\f053"
+}
+
+.fa-chevron-right:before {
+ content: "\f054"
+}
+
+.fa-plus-circle:before {
+ content: "\f055"
+}
+
+.fa-minus-circle:before {
+ content: "\f056"
+}
+
+.fa-times-circle:before {
+ content: "\f057"
+}
+
+.fa-check-circle:before {
+ content: "\f058"
+}
+
+.fa-question-circle:before {
+ content: "\f059"
+}
+
+.fa-info-circle:before {
+ content: "\f05a"
+}
+
+.fa-crosshairs:before {
+ content: "\f05b"
+}
+
+.fa-times-circle-o:before {
+ content: "\f05c"
+}
+
+.fa-check-circle-o:before {
+ content: "\f05d"
+}
+
+.fa-ban:before {
+ content: "\f05e"
+}
+
+.fa-arrow-left:before {
+ content: "\f060"
+}
+
+.fa-arrow-right:before {
+ content: "\f061"
+}
+
+.fa-arrow-up:before {
+ content: "\f062"
+}
+
+.fa-arrow-down:before {
+ content: "\f063"
+}
+
+.fa-mail-forward:before,
+.fa-share:before {
+ content: "\f064"
+}
+
+.fa-expand:before {
+ content: "\f065"
+}
+
+.fa-compress:before {
+ content: "\f066"
+}
+
+.fa-plus:before {
+ content: "\f067"
+}
+
+.fa-minus:before {
+ content: "\f068"
+}
+
+.fa-asterisk:before {
+ content: "\f069"
+}
+
+.fa-exclamation-circle:before {
+ content: "\f06a"
+}
+
+.fa-gift:before {
+ content: "\f06b"
+}
+
+.fa-leaf:before {
+ content: "\f06c"
+}
+
+.fa-fire:before {
+ content: "\f06d"
+}
+
+.fa-eye:before {
+ content: "\f06e"
+}
+
+.fa-eye-slash:before {
+ content: "\f070"
+}
+
+.fa-warning:before,
+.fa-exclamation-triangle:before {
+ content: "\f071"
+}
+
+.fa-plane:before {
+ content: "\f072"
+}
+
+.fa-calendar:before {
+ content: "\f073"
+}
+
+.fa-random:before {
+ content: "\f074"
+}
+
+.fa-comment:before {
+ content: "\f075"
+}
+
+.fa-magnet:before {
+ content: "\f076"
+}
+
+.fa-chevron-up:before {
+ content: "\f077"
+}
+
+.fa-chevron-down:before {
+ content: "\f078"
+}
+
+.fa-retweet:before {
+ content: "\f079"
+}
+
+.fa-shopping-cart:before {
+ content: "\f07a"
+}
+
+.fa-folder:before {
+ content: "\f07b"
+}
+
+.fa-folder-open:before {
+ content: "\f07c"
+}
+
+.fa-arrows-v:before {
+ content: "\f07d"
+}
+
+.fa-arrows-h:before {
+ content: "\f07e"
+}
+
+.fa-bar-chart-o:before,
+.fa-bar-chart:before {
+ content: "\f080"
+}
+
+.fa-twitter-square:before {
+ content: "\f081"
+}
+
+.fa-facebook-square:before {
+ content: "\f082"
+}
+
+.fa-camera-retro:before {
+ content: "\f083"
+}
+
+.fa-key:before {
+ content: "\f084"
+}
+
+.fa-gears:before,
+.fa-cogs:before {
+ content: "\f085"
+}
+
+.fa-comments:before {
+ content: "\f086"
+}
+
+.fa-thumbs-o-up:before {
+ content: "\f087"
+}
+
+.fa-thumbs-o-down:before {
+ content: "\f088"
+}
+
+.fa-star-half:before {
+ content: "\f089"
+}
+
+.fa-heart-o:before {
+ content: "\f08a"
+}
+
+.fa-sign-out:before {
+ content: "\f08b"
+}
+
+.fa-linkedin-square:before {
+ content: "\f08c"
+}
+
+.fa-thumb-tack:before {
+ content: "\f08d"
+}
+
+.fa-external-link:before {
+ content: "\f08e"
+}
+
+.fa-sign-in:before {
+ content: "\f090"
+}
+
+.fa-trophy:before {
+ content: "\f091"
+}
+
+.fa-github-square:before {
+ content: "\f092"
+}
+
+.fa-upload:before {
+ content: "\f093"
+}
+
+.fa-lemon-o:before {
+ content: "\f094"
+}
+
+.fa-phone:before {
+ content: "\f095"
+}
+
+.fa-square-o:before {
+ content: "\f096"
+}
+
+.fa-bookmark-o:before {
+ content: "\f097"
+}
+
+.fa-phone-square:before {
+ content: "\f098"
+}
+
+.fa-twitter:before {
+ content: "\f099"
+}
+
+.fa-facebook-f:before,
+.fa-facebook:before {
+ content: "\f09a"
+}
+
+.fa-github:before {
+ content: "\f09b"
+}
+
+.fa-unlock:before {
+ content: "\f09c"
+}
+
+.fa-credit-card:before {
+ content: "\f09d"
+}
+
+.fa-feed:before,
+.fa-rss:before {
+ content: "\f09e"
+}
+
+.fa-hdd-o:before {
+ content: "\f0a0"
+}
+
+.fa-bullhorn:before {
+ content: "\f0a1"
+}
+
+.fa-bell:before {
+ content: "\f0f3"
+}
+
+.fa-certificate:before {
+ content: "\f0a3"
+}
+
+.fa-hand-o-right:before {
+ content: "\f0a4"
+}
+
+.fa-hand-o-left:before {
+ content: "\f0a5"
+}
+
+.fa-hand-o-up:before {
+ content: "\f0a6"
+}
+
+.fa-hand-o-down:before {
+ content: "\f0a7"
+}
+
+.fa-arrow-circle-left:before {
+ content: "\f0a8"
+}
+
+.fa-arrow-circle-right:before {
+ content: "\f0a9"
+}
+
+.fa-arrow-circle-up:before {
+ content: "\f0aa"
+}
+
+.fa-arrow-circle-down:before {
+ content: "\f0ab"
+}
+
+.fa-globe:before {
+ content: "\f0ac"
+}
+
+.fa-wrench:before {
+ content: "\f0ad"
+}
+
+.fa-tasks:before {
+ content: "\f0ae"
+}
+
+.fa-filter:before {
+ content: "\f0b0"
+}
+
+.fa-briefcase:before {
+ content: "\f0b1"
+}
+
+.fa-arrows-alt:before {
+ content: "\f0b2"
+}
+
+.fa-group:before,
+.fa-users:before {
+ content: "\f0c0"
+}
+
+.fa-chain:before,
+.fa-link:before {
+ content: "\f0c1"
+}
+
+.fa-cloud:before {
+ content: "\f0c2"
+}
+
+.fa-flask:before {
+ content: "\f0c3"
+}
+
+.fa-cut:before,
+.fa-scissors:before {
+ content: "\f0c4"
+}
+
+.fa-copy:before,
+.fa-files-o:before {
+ content: "\f0c5"
+}
+
+.fa-paperclip:before {
+ content: "\f0c6"
+}
+
+.fa-save:before,
+.fa-floppy-o:before {
+ content: "\f0c7"
+}
+
+.fa-square:before {
+ content: "\f0c8"
+}
+
+.fa-navicon:before,
+.fa-reorder:before,
+.fa-bars:before {
+ content: "\f0c9"
+}
+
+.fa-list-ul:before {
+ content: "\f0ca"
+}
+
+.fa-list-ol:before {
+ content: "\f0cb"
+}
+
+.fa-strikethrough:before {
+ content: "\f0cc"
+}
+
+.fa-underline:before {
+ content: "\f0cd"
+}
+
+.fa-table:before {
+ content: "\f0ce"
+}
+
+.fa-magic:before {
+ content: "\f0d0"
+}
+
+.fa-truck:before {
+ content: "\f0d1"
+}
+
+.fa-pinterest:before {
+ content: "\f0d2"
+}
+
+.fa-pinterest-square:before {
+ content: "\f0d3"
+}
+
+.fa-google-plus-square:before {
+ content: "\f0d4"
+}
+
+.fa-google-plus:before {
+ content: "\f0d5"
+}
+
+.fa-money:before {
+ content: "\f0d6"
+}
+
+.fa-caret-down:before {
+ content: "\f0d7"
+}
+
+.fa-caret-up:before {
+ content: "\f0d8"
+}
+
+.fa-caret-left:before {
+ content: "\f0d9"
+}
+
+.fa-caret-right:before {
+ content: "\f0da"
+}
+
+.fa-columns:before {
+ content: "\f0db"
+}
+
+.fa-unsorted:before,
+.fa-sort:before {
+ content: "\f0dc"
+}
+
+.fa-sort-down:before,
+.fa-sort-desc:before {
+ content: "\f0dd"
+}
+
+.fa-sort-up:before,
+.fa-sort-asc:before {
+ content: "\f0de"
+}
+
+.fa-envelope:before {
+ content: "\f0e0"
+}
+
+.fa-linkedin:before {
+ content: "\f0e1"
+}
+
+.fa-rotate-left:before,
+.fa-undo:before {
+ content: "\f0e2"
+}
+
+.fa-legal:before,
+.fa-gavel:before {
+ content: "\f0e3"
+}
+
+.fa-dashboard:before,
+.fa-tachometer:before {
+ content: "\f0e4"
+}
+
+.fa-comment-o:before {
+ content: "\f0e5"
+}
+
+.fa-comments-o:before {
+ content: "\f0e6"
+}
+
+.fa-flash:before,
+.fa-bolt:before {
+ content: "\f0e7"
+}
+
+.fa-sitemap:before {
+ content: "\f0e8"
+}
+
+.fa-umbrella:before {
+ content: "\f0e9"
+}
+
+.fa-paste:before,
+.fa-clipboard:before {
+ content: "\f0ea"
+}
+
+.fa-lightbulb-o:before {
+ content: "\f0eb"
+}
+
+.fa-exchange:before {
+ content: "\f0ec"
+}
+
+.fa-cloud-download:before {
+ content: "\f0ed"
+}
+
+.fa-cloud-upload:before {
+ content: "\f0ee"
+}
+
+.fa-user-md:before {
+ content: "\f0f0"
+}
+
+.fa-stethoscope:before {
+ content: "\f0f1"
+}
+
+.fa-suitcase:before {
+ content: "\f0f2"
+}
+
+.fa-bell-o:before {
+ content: "\f0a2"
+}
+
+.fa-coffee:before {
+ content: "\f0f4"
+}
+
+.fa-cutlery:before {
+ content: "\f0f5"
+}
+
+.fa-file-text-o:before {
+ content: "\f0f6"
+}
+
+.fa-building-o:before {
+ content: "\f0f7"
+}
+
+.fa-hospital-o:before {
+ content: "\f0f8"
+}
+
+.fa-ambulance:before {
+ content: "\f0f9"
+}
+
+.fa-medkit:before {
+ content: "\f0fa"
+}
+
+.fa-fighter-jet:before {
+ content: "\f0fb"
+}
+
+.fa-beer:before {
+ content: "\f0fc"
+}
+
+.fa-h-square:before {
+ content: "\f0fd"
+}
+
+.fa-plus-square:before {
+ content: "\f0fe"
+}
+
+.fa-angle-double-left:before {
+ content: "\f100"
+}
+
+.fa-angle-double-right:before {
+ content: "\f101"
+}
+
+.fa-angle-double-up:before {
+ content: "\f102"
+}
+
+.fa-angle-double-down:before {
+ content: "\f103"
+}
+
+.fa-angle-left:before {
+ content: "\f104"
+}
+
+.fa-angle-right:before {
+ content: "\f105"
+}
+
+.fa-angle-up:before {
+ content: "\f106"
+}
+
+.fa-angle-down:before {
+ content: "\f107"
+}
+
+.fa-desktop:before {
+ content: "\f108"
+}
+
+.fa-laptop:before {
+ content: "\f109"
+}
+
+.fa-tablet:before {
+ content: "\f10a"
+}
+
+.fa-mobile-phone:before,
+.fa-mobile:before {
+ content: "\f10b"
+}
+
+.fa-circle-o:before {
+ content: "\f10c"
+}
+
+.fa-quote-left:before {
+ content: "\f10d"
+}
+
+.fa-quote-right:before {
+ content: "\f10e"
+}
+
+.fa-spinner:before {
+ content: "\f110"
+}
+
+.fa-circle:before {
+ content: "\f111"
+}
+
+.fa-mail-reply:before,
+.fa-reply:before {
+ content: "\f112"
+}
+
+.fa-github-alt:before {
+ content: "\f113"
+}
+
+.fa-folder-o:before {
+ content: "\f114"
+}
+
+.fa-folder-open-o:before {
+ content: "\f115"
+}
+
+.fa-smile-o:before {
+ content: "\f118"
+}
+
+.fa-frown-o:before {
+ content: "\f119"
+}
+
+.fa-meh-o:before {
+ content: "\f11a"
+}
+
+.fa-gamepad:before {
+ content: "\f11b"
+}
+
+.fa-keyboard-o:before {
+ content: "\f11c"
+}
+
+.fa-flag-o:before {
+ content: "\f11d"
+}
+
+.fa-flag-checkered:before {
+ content: "\f11e"
+}
+
+.fa-terminal:before {
+ content: "\f120"
+}
+
+.fa-code:before {
+ content: "\f121"
+}
+
+.fa-mail-reply-all:before,
+.fa-reply-all:before {
+ content: "\f122"
+}
+
+.fa-star-half-empty:before,
+.fa-star-half-full:before,
+.fa-star-half-o:before {
+ content: "\f123"
+}
+
+.fa-location-arrow:before {
+ content: "\f124"
+}
+
+.fa-crop:before {
+ content: "\f125"
+}
+
+.fa-code-fork:before {
+ content: "\f126"
+}
+
+.fa-unlink:before,
+.fa-chain-broken:before {
+ content: "\f127"
+}
+
+.fa-question:before {
+ content: "\f128"
+}
+
+.fa-info:before {
+ content: "\f129"
+}
+
+.fa-exclamation:before {
+ content: "\f12a"
+}
+
+.fa-superscript:before {
+ content: "\f12b"
+}
+
+.fa-subscript:before {
+ content: "\f12c"
+}
+
+.fa-eraser:before {
+ content: "\f12d"
+}
+
+.fa-puzzle-piece:before {
+ content: "\f12e"
+}
+
+.fa-microphone:before {
+ content: "\f130"
+}
+
+.fa-microphone-slash:before {
+ content: "\f131"
+}
+
+.fa-shield:before {
+ content: "\f132"
+}
+
+.fa-calendar-o:before {
+ content: "\f133"
+}
+
+.fa-fire-extinguisher:before {
+ content: "\f134"
+}
+
+.fa-rocket:before {
+ content: "\f135"
+}
+
+.fa-maxcdn:before {
+ content: "\f136"
+}
+
+.fa-chevron-circle-left:before {
+ content: "\f137"
+}
+
+.fa-chevron-circle-right:before {
+ content: "\f138"
+}
+
+.fa-chevron-circle-up:before {
+ content: "\f139"
+}
+
+.fa-chevron-circle-down:before {
+ content: "\f13a"
+}
+
+.fa-html5:before {
+ content: "\f13b"
+}
+
+.fa-css3:before {
+ content: "\f13c"
+}
+
+.fa-anchor:before {
+ content: "\f13d"
+}
+
+.fa-unlock-alt:before {
+ content: "\f13e"
+}
+
+.fa-bullseye:before {
+ content: "\f140"
+}
+
+.fa-ellipsis-h:before {
+ content: "\f141"
+}
+
+.fa-ellipsis-v:before {
+ content: "\f142"
+}
+
+.fa-rss-square:before {
+ content: "\f143"
+}
+
+.fa-play-circle:before {
+ content: "\f144"
+}
+
+.fa-ticket:before {
+ content: "\f145"
+}
+
+.fa-minus-square:before {
+ content: "\f146"
+}
+
+.fa-minus-square-o:before {
+ content: "\f147"
+}
+
+.fa-level-up:before {
+ content: "\f148"
+}
+
+.fa-level-down:before {
+ content: "\f149"
+}
+
+.fa-check-square:before {
+ content: "\f14a"
+}
+
+.fa-pencil-square:before {
+ content: "\f14b"
+}
+
+.fa-external-link-square:before {
+ content: "\f14c"
+}
+
+.fa-share-square:before {
+ content: "\f14d"
+}
+
+.fa-compass:before {
+ content: "\f14e"
+}
+
+.fa-toggle-down:before,
+.fa-caret-square-o-down:before {
+ content: "\f150"
+}
+
+.fa-toggle-up:before,
+.fa-caret-square-o-up:before {
+ content: "\f151"
+}
+
+.fa-toggle-right:before,
+.fa-caret-square-o-right:before {
+ content: "\f152"
+}
+
+.fa-euro:before,
+.fa-eur:before {
+ content: "\f153"
+}
+
+.fa-gbp:before {
+ content: "\f154"
+}
+
+.fa-dollar:before,
+.fa-usd:before {
+ content: "\f155"
+}
+
+.fa-rupee:before,
+.fa-inr:before {
+ content: "\f156"
+}
+
+.fa-cny:before,
+.fa-rmb:before,
+.fa-yen:before,
+.fa-jpy:before {
+ content: "\f157"
+}
+
+.fa-ruble:before,
+.fa-rouble:before,
+.fa-rub:before {
+ content: "\f158"
+}
+
+.fa-won:before,
+.fa-krw:before {
+ content: "\f159"
+}
+
+.fa-bitcoin:before,
+.fa-btc:before {
+ content: "\f15a"
+}
+
+.fa-file:before {
+ content: "\f15b"
+}
+
+.fa-file-text:before {
+ content: "\f15c"
+}
+
+.fa-sort-alpha-asc:before {
+ content: "\f15d"
+}
+
+.fa-sort-alpha-desc:before {
+ content: "\f15e"
+}
+
+.fa-sort-amount-asc:before {
+ content: "\f160"
+}
+
+.fa-sort-amount-desc:before {
+ content: "\f161"
+}
+
+.fa-sort-numeric-asc:before {
+ content: "\f162"
+}
+
+.fa-sort-numeric-desc:before {
+ content: "\f163"
+}
+
+.fa-thumbs-up:before {
+ content: "\f164"
+}
+
+.fa-thumbs-down:before {
+ content: "\f165"
+}
+
+.fa-youtube-square:before {
+ content: "\f166"
+}
+
+.fa-youtube:before {
+ content: "\f167"
+}
+
+.fa-xing:before {
+ content: "\f168"
+}
+
+.fa-xing-square:before {
+ content: "\f169"
+}
+
+.fa-youtube-play:before {
+ content: "\f16a"
+}
+
+.fa-dropbox:before {
+ content: "\f16b"
+}
+
+.fa-stack-overflow:before {
+ content: "\f16c"
+}
+
+.fa-instagram:before {
+ content: "\f16d"
+}
+
+.fa-flickr:before {
+ content: "\f16e"
+}
+
+.fa-adn:before {
+ content: "\f170"
+}
+
+.fa-bitbucket:before {
+ content: "\f171"
+}
+
+.fa-bitbucket-square:before {
+ content: "\f172"
+}
+
+.fa-tumblr:before {
+ content: "\f173"
+}
+
+.fa-tumblr-square:before {
+ content: "\f174"
+}
+
+.fa-long-arrow-down:before {
+ content: "\f175"
+}
+
+.fa-long-arrow-up:before {
+ content: "\f176"
+}
+
+.fa-long-arrow-left:before {
+ content: "\f177"
+}
+
+.fa-long-arrow-right:before {
+ content: "\f178"
+}
+
+.fa-apple:before {
+ content: "\f179"
+}
+
+.fa-windows:before {
+ content: "\f17a"
+}
+
+.fa-android:before {
+ content: "\f17b"
+}
+
+.fa-linux:before {
+ content: "\f17c"
+}
+
+.fa-dribbble:before {
+ content: "\f17d"
+}
+
+.fa-skype:before {
+ content: "\f17e"
+}
+
+.fa-foursquare:before {
+ content: "\f180"
+}
+
+.fa-trello:before {
+ content: "\f181"
+}
+
+.fa-female:before {
+ content: "\f182"
+}
+
+.fa-male:before {
+ content: "\f183"
+}
+
+.fa-gittip:before,
+.fa-gratipay:before {
+ content: "\f184"
+}
+
+.fa-sun-o:before {
+ content: "\f185"
+}
+
+.fa-moon-o:before {
+ content: "\f186"
+}
+
+.fa-archive:before {
+ content: "\f187"
+}
+
+.fa-bug:before {
+ content: "\f188"
+}
+
+.fa-vk:before {
+ content: "\f189"
+}
+
+.fa-weibo:before {
+ content: "\f18a"
+}
+
+.fa-renren:before {
+ content: "\f18b"
+}
+
+.fa-pagelines:before {
+ content: "\f18c"
+}
+
+.fa-stack-exchange:before {
+ content: "\f18d"
+}
+
+.fa-arrow-circle-o-right:before {
+ content: "\f18e"
+}
+
+.fa-arrow-circle-o-left:before {
+ content: "\f190"
+}
+
+.fa-toggle-left:before,
+.fa-caret-square-o-left:before {
+ content: "\f191"
+}
+
+.fa-dot-circle-o:before {
+ content: "\f192"
+}
+
+.fa-wheelchair:before {
+ content: "\f193"
+}
+
+.fa-vimeo-square:before {
+ content: "\f194"
+}
+
+.fa-turkish-lira:before,
+.fa-try:before {
+ content: "\f195"
+}
+
+.fa-plus-square-o:before {
+ content: "\f196"
+}
+
+.fa-space-shuttle:before {
+ content: "\f197"
+}
+
+.fa-slack:before {
+ content: "\f198"
+}
+
+.fa-envelope-square:before {
+ content: "\f199"
+}
+
+.fa-wordpress:before {
+ content: "\f19a"
+}
+
+.fa-openid:before {
+ content: "\f19b"
+}
+
+.fa-institution:before,
+.fa-bank:before,
+.fa-university:before {
+ content: "\f19c"
+}
+
+.fa-mortar-board:before,
+.fa-graduation-cap:before {
+ content: "\f19d"
+}
+
+.fa-yahoo:before {
+ content: "\f19e"
+}
+
+.fa-google:before {
+ content: "\f1a0"
+}
+
+.fa-reddit:before {
+ content: "\f1a1"
+}
+
+.fa-reddit-square:before {
+ content: "\f1a2"
+}
+
+.fa-stumbleupon-circle:before {
+ content: "\f1a3"
+}
+
+.fa-stumbleupon:before {
+ content: "\f1a4"
+}
+
+.fa-delicious:before {
+ content: "\f1a5"
+}
+
+.fa-digg:before {
+ content: "\f1a6"
+}
+
+.fa-pied-piper-pp:before {
+ content: "\f1a7"
+}
+
+.fa-pied-piper-alt:before {
+ content: "\f1a8"
+}
+
+.fa-drupal:before {
+ content: "\f1a9"
+}
+
+.fa-joomla:before {
+ content: "\f1aa"
+}
+
+.fa-language:before {
+ content: "\f1ab"
+}
+
+.fa-fax:before {
+ content: "\f1ac"
+}
+
+.fa-building:before {
+ content: "\f1ad"
+}
+
+.fa-child:before {
+ content: "\f1ae"
+}
+
+.fa-paw:before {
+ content: "\f1b0"
+}
+
+.fa-spoon:before {
+ content: "\f1b1"
+}
+
+.fa-cube:before {
+ content: "\f1b2"
+}
+
+.fa-cubes:before {
+ content: "\f1b3"
+}
+
+.fa-behance:before {
+ content: "\f1b4"
+}
+
+.fa-behance-square:before {
+ content: "\f1b5"
+}
+
+.fa-steam:before {
+ content: "\f1b6"
+}
+
+.fa-steam-square:before {
+ content: "\f1b7"
+}
+
+.fa-recycle:before {
+ content: "\f1b8"
+}
+
+.fa-automobile:before,
+.fa-car:before {
+ content: "\f1b9"
+}
+
+.fa-cab:before,
+.fa-taxi:before {
+ content: "\f1ba"
+}
+
+.fa-tree:before {
+ content: "\f1bb"
+}
+
+.fa-spotify:before {
+ content: "\f1bc"
+}
+
+.fa-deviantart:before {
+ content: "\f1bd"
+}
+
+.fa-soundcloud:before {
+ content: "\f1be"
+}
+
+.fa-database:before {
+ content: "\f1c0"
+}
+
+.fa-file-pdf-o:before {
+ content: "\f1c1"
+}
+
+.fa-file-word-o:before {
+ content: "\f1c2"
+}
+
+.fa-file-excel-o:before {
+ content: "\f1c3"
+}
+
+.fa-file-powerpoint-o:before {
+ content: "\f1c4"
+}
+
+.fa-file-photo-o:before,
+.fa-file-picture-o:before,
+.fa-file-image-o:before {
+ content: "\f1c5"
+}
+
+.fa-file-zip-o:before,
+.fa-file-archive-o:before {
+ content: "\f1c6"
+}
+
+.fa-file-sound-o:before,
+.fa-file-audio-o:before {
+ content: "\f1c7"
+}
+
+.fa-file-movie-o:before,
+.fa-file-video-o:before {
+ content: "\f1c8"
+}
+
+.fa-file-code-o:before {
+ content: "\f1c9"
+}
+
+.fa-vine:before {
+ content: "\f1ca"
+}
+
+.fa-codepen:before {
+ content: "\f1cb"
+}
+
+.fa-jsfiddle:before {
+ content: "\f1cc"
+}
+
+.fa-life-bouy:before,
+.fa-life-buoy:before,
+.fa-life-saver:before,
+.fa-support:before,
+.fa-life-ring:before {
+ content: "\f1cd"
+}
+
+.fa-circle-o-notch:before {
+ content: "\f1ce"
+}
+
+.fa-ra:before,
+.fa-resistance:before,
+.fa-rebel:before {
+ content: "\f1d0"
+}
+
+.fa-ge:before,
+.fa-empire:before {
+ content: "\f1d1"
+}
+
+.fa-git-square:before {
+ content: "\f1d2"
+}
+
+.fa-git:before {
+ content: "\f1d3"
+}
+
+.fa-y-combinator-square:before,
+.fa-yc-square:before,
+.fa-hacker-news:before {
+ content: "\f1d4"
+}
+
+.fa-tencent-weibo:before {
+ content: "\f1d5"
+}
+
+.fa-qq:before {
+ content: "\f1d6"
+}
+
+.fa-wechat:before,
+.fa-weixin:before {
+ content: "\f1d7"
+}
+
+.fa-send:before,
+.fa-paper-plane:before {
+ content: "\f1d8"
+}
+
+.fa-send-o:before,
+.fa-paper-plane-o:before {
+ content: "\f1d9"
+}
+
+.fa-history:before {
+ content: "\f1da"
+}
+
+.fa-circle-thin:before {
+ content: "\f1db"
+}
+
+.fa-header:before {
+ content: "\f1dc"
+}
+
+.fa-paragraph:before {
+ content: "\f1dd"
+}
+
+.fa-sliders:before {
+ content: "\f1de"
+}
+
+.fa-share-alt:before {
+ content: "\f1e0"
+}
+
+.fa-share-alt-square:before {
+ content: "\f1e1"
+}
+
+.fa-bomb:before {
+ content: "\f1e2"
+}
+
+.fa-soccer-ball-o:before,
+.fa-futbol-o:before {
+ content: "\f1e3"
+}
+
+.fa-tty:before {
+ content: "\f1e4"
+}
+
+.fa-binoculars:before {
+ content: "\f1e5"
+}
+
+.fa-plug:before {
+ content: "\f1e6"
+}
+
+.fa-slideshare:before {
+ content: "\f1e7"
+}
+
+.fa-twitch:before {
+ content: "\f1e8"
+}
+
+.fa-yelp:before {
+ content: "\f1e9"
+}
+
+.fa-newspaper-o:before {
+ content: "\f1ea"
+}
+
+.fa-wifi:before {
+ content: "\f1eb"
+}
+
+.fa-calculator:before {
+ content: "\f1ec"
+}
+
+.fa-paypal:before {
+ content: "\f1ed"
+}
+
+.fa-google-wallet:before {
+ content: "\f1ee"
+}
+
+.fa-cc-visa:before {
+ content: "\f1f0"
+}
+
+.fa-cc-mastercard:before {
+ content: "\f1f1"
+}
+
+.fa-cc-discover:before {
+ content: "\f1f2"
+}
+
+.fa-cc-amex:before {
+ content: "\f1f3"
+}
+
+.fa-cc-paypal:before {
+ content: "\f1f4"
+}
+
+.fa-cc-stripe:before {
+ content: "\f1f5"
+}
+
+.fa-bell-slash:before {
+ content: "\f1f6"
+}
+
+.fa-bell-slash-o:before {
+ content: "\f1f7"
+}
+
+.fa-trash:before {
+ content: "\f1f8"
+}
+
+.fa-copyright:before {
+ content: "\f1f9"
+}
+
+.fa-at:before {
+ content: "\f1fa"
+}
+
+.fa-eyedropper:before {
+ content: "\f1fb"
+}
+
+.fa-paint-brush:before {
+ content: "\f1fc"
+}
+
+.fa-birthday-cake:before {
+ content: "\f1fd"
+}
+
+.fa-area-chart:before {
+ content: "\f1fe"
+}
+
+.fa-pie-chart:before {
+ content: "\f200"
+}
+
+.fa-line-chart:before {
+ content: "\f201"
+}
+
+.fa-lastfm:before {
+ content: "\f202"
+}
+
+.fa-lastfm-square:before {
+ content: "\f203"
+}
+
+.fa-toggle-off:before {
+ content: "\f204"
+}
+
+.fa-toggle-on:before {
+ content: "\f205"
+}
+
+.fa-bicycle:before {
+ content: "\f206"
+}
+
+.fa-bus:before {
+ content: "\f207"
+}
+
+.fa-ioxhost:before {
+ content: "\f208"
+}
+
+.fa-angellist:before {
+ content: "\f209"
+}
+
+.fa-cc:before {
+ content: "\f20a"
+}
+
+.fa-shekel:before,
+.fa-sheqel:before,
+.fa-ils:before {
+ content: "\f20b"
+}
+
+.fa-meanpath:before {
+ content: "\f20c"
+}
+
+.fa-buysellads:before {
+ content: "\f20d"
+}
+
+.fa-connectdevelop:before {
+ content: "\f20e"
+}
+
+.fa-dashcube:before {
+ content: "\f210"
+}
+
+.fa-forumbee:before {
+ content: "\f211"
+}
+
+.fa-leanpub:before {
+ content: "\f212"
+}
+
+.fa-sellsy:before {
+ content: "\f213"
+}
+
+.fa-shirtsinbulk:before {
+ content: "\f214"
+}
+
+.fa-simplybuilt:before {
+ content: "\f215"
+}
+
+.fa-skyatlas:before {
+ content: "\f216"
+}
+
+.fa-cart-plus:before {
+ content: "\f217"
+}
+
+.fa-cart-arrow-down:before {
+ content: "\f218"
+}
+
+.fa-diamond:before {
+ content: "\f219"
+}
+
+.fa-ship:before {
+ content: "\f21a"
+}
+
+.fa-user-secret:before {
+ content: "\f21b"
+}
+
+.fa-motorcycle:before {
+ content: "\f21c"
+}
+
+.fa-street-view:before {
+ content: "\f21d"
+}
+
+.fa-heartbeat:before {
+ content: "\f21e"
+}
+
+.fa-venus:before {
+ content: "\f221"
+}
+
+.fa-mars:before {
+ content: "\f222"
+}
+
+.fa-mercury:before {
+ content: "\f223"
+}
+
+.fa-intersex:before,
+.fa-transgender:before {
+ content: "\f224"
+}
+
+.fa-transgender-alt:before {
+ content: "\f225"
+}
+
+.fa-venus-double:before {
+ content: "\f226"
+}
+
+.fa-mars-double:before {
+ content: "\f227"
+}
+
+.fa-venus-mars:before {
+ content: "\f228"
+}
+
+.fa-mars-stroke:before {
+ content: "\f229"
+}
+
+.fa-mars-stroke-v:before {
+ content: "\f22a"
+}
+
+.fa-mars-stroke-h:before {
+ content: "\f22b"
+}
+
+.fa-neuter:before {
+ content: "\f22c"
+}
+
+.fa-genderless:before {
+ content: "\f22d"
+}
+
+.fa-facebook-official:before {
+ content: "\f230"
+}
+
+.fa-pinterest-p:before {
+ content: "\f231"
+}
+
+.fa-whatsapp:before {
+ content: "\f232"
+}
+
+.fa-server:before {
+ content: "\f233"
+}
+
+.fa-user-plus:before {
+ content: "\f234"
+}
+
+.fa-user-times:before {
+ content: "\f235"
+}
+
+.fa-hotel:before,
+.fa-bed:before {
+ content: "\f236"
+}
+
+.fa-viacoin:before {
+ content: "\f237"
+}
+
+.fa-train:before {
+ content: "\f238"
+}
+
+.fa-subway:before {
+ content: "\f239"
+}
+
+.fa-medium:before {
+ content: "\f23a"
+}
+
+.fa-yc:before,
+.fa-y-combinator:before {
+ content: "\f23b"
+}
+
+.fa-optin-monster:before {
+ content: "\f23c"
+}
+
+.fa-opencart:before {
+ content: "\f23d"
+}
+
+.fa-expeditedssl:before {
+ content: "\f23e"
+}
+
+.fa-battery-4:before,
+.fa-battery:before,
+.fa-battery-full:before {
+ content: "\f240"
+}
+
+.fa-battery-3:before,
+.fa-battery-three-quarters:before {
+ content: "\f241"
+}
+
+.fa-battery-2:before,
+.fa-battery-half:before {
+ content: "\f242"
+}
+
+.fa-battery-1:before,
+.fa-battery-quarter:before {
+ content: "\f243"
+}
+
+.fa-battery-0:before,
+.fa-battery-empty:before {
+ content: "\f244"
+}
+
+.fa-mouse-pointer:before {
+ content: "\f245"
+}
+
+.fa-i-cursor:before {
+ content: "\f246"
+}
+
+.fa-object-group:before {
+ content: "\f247"
+}
+
+.fa-object-ungroup:before {
+ content: "\f248"
+}
+
+.fa-sticky-note:before {
+ content: "\f249"
+}
+
+.fa-sticky-note-o:before {
+ content: "\f24a"
+}
+
+.fa-cc-jcb:before {
+ content: "\f24b"
+}
+
+.fa-cc-diners-club:before {
+ content: "\f24c"
+}
+
+.fa-clone:before {
+ content: "\f24d"
+}
+
+.fa-balance-scale:before {
+ content: "\f24e"
+}
+
+.fa-hourglass-o:before {
+ content: "\f250"
+}
+
+.fa-hourglass-1:before,
+.fa-hourglass-start:before {
+ content: "\f251"
+}
+
+.fa-hourglass-2:before,
+.fa-hourglass-half:before {
+ content: "\f252"
+}
+
+.fa-hourglass-3:before,
+.fa-hourglass-end:before {
+ content: "\f253"
+}
+
+.fa-hourglass:before {
+ content: "\f254"
+}
+
+.fa-hand-grab-o:before,
+.fa-hand-rock-o:before {
+ content: "\f255"
+}
+
+.fa-hand-stop-o:before,
+.fa-hand-paper-o:before {
+ content: "\f256"
+}
+
+.fa-hand-scissors-o:before {
+ content: "\f257"
+}
+
+.fa-hand-lizard-o:before {
+ content: "\f258"
+}
+
+.fa-hand-spock-o:before {
+ content: "\f259"
+}
+
+.fa-hand-pointer-o:before {
+ content: "\f25a"
+}
+
+.fa-hand-peace-o:before {
+ content: "\f25b"
+}
+
+.fa-trademark:before {
+ content: "\f25c"
+}
+
+.fa-registered:before {
+ content: "\f25d"
+}
+
+.fa-creative-commons:before {
+ content: "\f25e"
+}
+
+.fa-gg:before {
+ content: "\f260"
+}
+
+.fa-gg-circle:before {
+ content: "\f261"
+}
+
+.fa-tripadvisor:before {
+ content: "\f262"
+}
+
+.fa-odnoklassniki:before {
+ content: "\f263"
+}
+
+.fa-odnoklassniki-square:before {
+ content: "\f264"
+}
+
+.fa-get-pocket:before {
+ content: "\f265"
+}
+
+.fa-wikipedia-w:before {
+ content: "\f266"
+}
+
+.fa-safari:before {
+ content: "\f267"
+}
+
+.fa-chrome:before {
+ content: "\f268"
+}
+
+.fa-firefox:before {
+ content: "\f269"
+}
+
+.fa-opera:before {
+ content: "\f26a"
+}
+
+.fa-internet-explorer:before {
+ content: "\f26b"
+}
+
+.fa-tv:before,
+.fa-television:before {
+ content: "\f26c"
+}
+
+.fa-contao:before {
+ content: "\f26d"
+}
+
+.fa-500px:before {
+ content: "\f26e"
+}
+
+.fa-amazon:before {
+ content: "\f270"
+}
+
+.fa-calendar-plus-o:before {
+ content: "\f271"
+}
+
+.fa-calendar-minus-o:before {
+ content: "\f272"
+}
+
+.fa-calendar-times-o:before {
+ content: "\f273"
+}
+
+.fa-calendar-check-o:before {
+ content: "\f274"
+}
+
+.fa-industry:before {
+ content: "\f275"
+}
+
+.fa-map-pin:before {
+ content: "\f276"
+}
+
+.fa-map-signs:before {
+ content: "\f277"
+}
+
+.fa-map-o:before {
+ content: "\f278"
+}
+
+.fa-map:before {
+ content: "\f279"
+}
+
+.fa-commenting:before {
+ content: "\f27a"
+}
+
+.fa-commenting-o:before {
+ content: "\f27b"
+}
+
+.fa-houzz:before {
+ content: "\f27c"
+}
+
+.fa-vimeo:before {
+ content: "\f27d"
+}
+
+.fa-black-tie:before {
+ content: "\f27e"
+}
+
+.fa-fonticons:before {
+ content: "\f280"
+}
+
+.fa-reddit-alien:before {
+ content: "\f281"
+}
+
+.fa-edge:before {
+ content: "\f282"
+}
+
+.fa-credit-card-alt:before {
+ content: "\f283"
+}
+
+.fa-codiepie:before {
+ content: "\f284"
+}
+
+.fa-modx:before {
+ content: "\f285"
+}
+
+.fa-fort-awesome:before {
+ content: "\f286"
+}
+
+.fa-usb:before {
+ content: "\f287"
+}
+
+.fa-product-hunt:before {
+ content: "\f288"
+}
+
+.fa-mixcloud:before {
+ content: "\f289"
+}
+
+.fa-scribd:before {
+ content: "\f28a"
+}
+
+.fa-pause-circle:before {
+ content: "\f28b"
+}
+
+.fa-pause-circle-o:before {
+ content: "\f28c"
+}
+
+.fa-stop-circle:before {
+ content: "\f28d"
+}
+
+.fa-stop-circle-o:before {
+ content: "\f28e"
+}
+
+.fa-shopping-bag:before {
+ content: "\f290"
+}
+
+.fa-shopping-basket:before {
+ content: "\f291"
+}
+
+.fa-hashtag:before {
+ content: "\f292"
+}
+
+.fa-bluetooth:before {
+ content: "\f293"
+}
+
+.fa-bluetooth-b:before {
+ content: "\f294"
+}
+
+.fa-percent:before {
+ content: "\f295"
+}
+
+.fa-gitlab:before {
+ content: "\f296"
+}
+
+.fa-wpbeginner:before {
+ content: "\f297"
+}
+
+.fa-wpforms:before {
+ content: "\f298"
+}
+
+.fa-envira:before {
+ content: "\f299"
+}
+
+.fa-universal-access:before {
+ content: "\f29a"
+}
+
+.fa-wheelchair-alt:before {
+ content: "\f29b"
+}
+
+.fa-question-circle-o:before {
+ content: "\f29c"
+}
+
+.fa-blind:before {
+ content: "\f29d"
+}
+
+.fa-audio-description:before {
+ content: "\f29e"
+}
+
+.fa-volume-control-phone:before {
+ content: "\f2a0"
+}
+
+.fa-braille:before {
+ content: "\f2a1"
+}
+
+.fa-assistive-listening-systems:before {
+ content: "\f2a2"
+}
+
+.fa-asl-interpreting:before,
+.fa-american-sign-language-interpreting:before {
+ content: "\f2a3"
+}
+
+.fa-deafness:before,
+.fa-hard-of-hearing:before,
+.fa-deaf:before {
+ content: "\f2a4"
+}
+
+.fa-glide:before {
+ content: "\f2a5"
+}
+
+.fa-glide-g:before {
+ content: "\f2a6"
+}
+
+.fa-signing:before,
+.fa-sign-language:before {
+ content: "\f2a7"
+}
+
+.fa-low-vision:before {
+ content: "\f2a8"
+}
+
+.fa-viadeo:before {
+ content: "\f2a9"
+}
+
+.fa-viadeo-square:before {
+ content: "\f2aa"
+}
+
+.fa-snapchat:before {
+ content: "\f2ab"
+}
+
+.fa-snapchat-ghost:before {
+ content: "\f2ac"
+}
+
+.fa-snapchat-square:before {
+ content: "\f2ad"
+}
+
+.fa-pied-piper:before {
+ content: "\f2ae"
+}
+
+.fa-first-order:before {
+ content: "\f2b0"
+}
+
+.fa-yoast:before {
+ content: "\f2b1"
+}
+
+.fa-themeisle:before {
+ content: "\f2b2"
+}
+
+.fa-google-plus-circle:before,
+.fa-google-plus-official:before {
+ content: "\f2b3"
+}
+
+.fa-fa:before,
+.fa-font-awesome:before {
+ content: "\f2b4"
+}
+
+.fa-handshake-o:before {
+ content: "\f2b5"
+}
+
+.fa-envelope-open:before {
+ content: "\f2b6"
+}
+
+.fa-envelope-open-o:before {
+ content: "\f2b7"
+}
+
+.fa-linode:before {
+ content: "\f2b8"
+}
+
+.fa-address-book:before {
+ content: "\f2b9"
+}
+
+.fa-address-book-o:before {
+ content: "\f2ba"
+}
+
+.fa-vcard:before,
+.fa-address-card:before {
+ content: "\f2bb"
+}
+
+.fa-vcard-o:before,
+.fa-address-card-o:before {
+ content: "\f2bc"
+}
+
+.fa-user-circle:before {
+ content: "\f2bd"
+}
+
+.fa-user-circle-o:before {
+ content: "\f2be"
+}
+
+.fa-user-o:before {
+ content: "\f2c0"
+}
+
+.fa-id-badge:before {
+ content: "\f2c1"
+}
+
+.fa-drivers-license:before,
+.fa-id-card:before {
+ content: "\f2c2"
+}
+
+.fa-drivers-license-o:before,
+.fa-id-card-o:before {
+ content: "\f2c3"
+}
+
+.fa-quora:before {
+ content: "\f2c4"
+}
+
+.fa-free-code-camp:before {
+ content: "\f2c5"
+}
+
+.fa-telegram:before {
+ content: "\f2c6"
+}
+
+.fa-thermometer-4:before,
+.fa-thermometer:before,
+.fa-thermometer-full:before {
+ content: "\f2c7"
+}
+
+.fa-thermometer-3:before,
+.fa-thermometer-three-quarters:before {
+ content: "\f2c8"
+}
+
+.fa-thermometer-2:before,
+.fa-thermometer-half:before {
+ content: "\f2c9"
+}
+
+.fa-thermometer-1:before,
+.fa-thermometer-quarter:before {
+ content: "\f2ca"
+}
+
+.fa-thermometer-0:before,
+.fa-thermometer-empty:before {
+ content: "\f2cb"
+}
+
+.fa-shower:before {
+ content: "\f2cc"
+}
+
+.fa-bathtub:before,
+.fa-s15:before,
+.fa-bath:before {
+ content: "\f2cd"
+}
+
+.fa-podcast:before {
+ content: "\f2ce"
+}
+
+.fa-window-maximize:before {
+ content: "\f2d0"
+}
+
+.fa-window-minimize:before {
+ content: "\f2d1"
+}
+
+.fa-window-restore:before {
+ content: "\f2d2"
+}
+
+.fa-times-rectangle:before,
+.fa-window-close:before {
+ content: "\f2d3"
+}
+
+.fa-times-rectangle-o:before,
+.fa-window-close-o:before {
+ content: "\f2d4"
+}
+
+.fa-bandcamp:before {
+ content: "\f2d5"
+}
+
+.fa-grav:before {
+ content: "\f2d6"
+}
+
+.fa-etsy:before {
+ content: "\f2d7"
+}
+
+.fa-imdb:before {
+ content: "\f2d8"
+}
+
+.fa-ravelry:before {
+ content: "\f2d9"
+}
+
+.fa-eercast:before {
+ content: "\f2da"
+}
+
+.fa-microchip:before {
+ content: "\f2db"
+}
+
+.fa-snowflake-o:before {
+ content: "\f2dc"
+}
+
+.fa-superpowers:before {
+ content: "\f2dd"
+}
+
+.fa-wpexplorer:before {
+ content: "\f2de"
+}
+
+.fa-meetup:before {
+ content: "\f2e0"
+}
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0
+}
+
+.sr-only-focusable:active,
+.sr-only-focusable:focus {
+ position: static;
+ width: auto;
+ height: auto;
+ margin: 0;
+ overflow: visible;
+ clip: auto
+}
\ No newline at end of file
diff --git a/static/css/webfonts/fontawesome-webfont.eot b/static/css/webfonts/fontawesome-webfont.eot
new file mode 100644
index 0000000..e9f60ca
Binary files /dev/null and b/static/css/webfonts/fontawesome-webfont.eot differ
diff --git a/static/css/webfonts/fontawesome-webfont.svg b/static/css/webfonts/fontawesome-webfont.svg
new file mode 100644
index 0000000..855c845
--- /dev/null
+++ b/static/css/webfonts/fontawesome-webfont.svg
@@ -0,0 +1,2671 @@
+
+
+
diff --git a/static/css/webfonts/fontawesome-webfont.woff b/static/css/webfonts/fontawesome-webfont.woff
new file mode 100644
index 0000000..400014a
Binary files /dev/null and b/static/css/webfonts/fontawesome-webfont.woff differ
diff --git a/static/css/webfonts/fontawesome-webfont.woff2 b/static/css/webfonts/fontawesome-webfont.woff2
new file mode 100644
index 0000000..4d13fc6
Binary files /dev/null and b/static/css/webfonts/fontawesome-webfont.woff2 differ
diff --git a/static/domain-info/domains/domain-info.json b/static/domain-info/domains/domain-info.json
index 3e3d594..8f87b2a 100644
--- a/static/domain-info/domains/domain-info.json
+++ b/static/domain-info/domains/domain-info.json
@@ -26,7 +26,9 @@
"22": "网络服务",
"23": "支付平台",
"24": "API服务",
- "25": "其他"
+ "25": "其他",
+ "26": "游戏网站",
+ "27": "行为分析、跟踪遥测、数据统计等"
},
"domains": {
"网易": {
@@ -54,17 +56,17 @@
"url": "https://dict.youdao.com/",
"icon": "https://dict.youdao.com/favicon.ico"
},
- "网易uu加速器":{
+ "网易uu加速器": {
"name": "网易uu加速器",
"categoryId": 0,
"url": {
- "1": "https://uu.163.com/",
- "2": "https://log.uu.163.com/"
+ "1": "https://uu.163.com/",
+ "2": "https://log.uu.163.com/"
}
},
"company": "网易股份有限公司"
},
- "华为":{
+ "华为": {
"华为全球官网": {
"name": "华为全球官网",
"categoryId": 0,
@@ -103,7 +105,7 @@
},
"华为新闻中心": {
"name": "华为新闻中心",
- "categoryId": 3,
+ "categoryId": 11,
"url": "news.huawei.com",
"icon": "https://www.huawei.com/-/media/htemplate-home/1.0.1.20251205144752/components/assets/img/favicon-logo.svg"
},
@@ -149,6 +151,21 @@
"url": "pay.huawei.com",
"icon": "https://www.huawei.com/-/media/htemplate-home/1.0.1.20251205144752/components/assets/img/favicon-logo.svg"
},
+ "华为HiCloud网络连通性检测服务": {
+ "name": "华为HiCloud网络连通性检测服务",
+ "categoryId": 21,
+ "url": "connectivitycheck.platform.hicloud.com",
+ "icon": "https://www.huawei.com/-/media/htemplate-home/1.0.1.20251205144752/components/assets/img/favicon-logo.svg"
+ },
+ "华为云CDN CNAME归属域": {
+ "name": "华为云CDN CNAME归属域/加速服务",
+ "categoryId": 2,
+ "url": {
+ "1": "cdnhwc1.com",
+ "2": "cdnhwc6.com"
+ },
+ "icon": "https://www.huawei.com/-/media/htemplate-home/1.0.1.20251205144752/components/assets/img/favicon-logo.svg"
+ },
"华为鸿蒙开发者社区": {
"name": "华为鸿蒙开发者社区",
"categoryId": 11,
@@ -248,7 +265,6 @@
"url": "dl.google.com",
"icon": "https://www.google.com/favicon.ico"
},
-
"company": "谷歌 LLC"
},
"阿里云": {
@@ -279,11 +295,97 @@
"url": "https://www.alipay.com/",
"icon": "https://www.alipay.com/favicon.ico"
},
- "淘宝": {
- "name": "淘宝",
- "categoryId": 4,
- "url": "https://www.taobao.com/",
- "icon": "https://www.taobao.com/favicon.ico"
+ "淘宝相关": {
+ "淘宝": {
+ "name": "淘宝",
+ "categoryId": 4,
+ "url": "https://www.taobao.com/",
+ "icon": "https://www.taobao.com/favicon.ico"
+ },
+ "淘宝UT数据看板服务": {
+ "name": "淘宝UT数据看板服务/淘宝内部数据监控与分析工具",
+ "categoryId": 6,
+ "url": "h-adashx.ut.taobao.com",
+ "icon": "https://www.taobao.com/favicon.ico"
+ },
+ "淘宝移动端账号静态资源CDN": {
+ "name": "淘宝移动端账号静态资源CDN/淘宝移动端账号相关静态资源分发加速",
+ "categoryId": 2,
+ "url": "accscdn.m.taobao.com",
+ "icon": "https://www.taobao.com/favicon.ico"
+ },
+ "淘宝AudID标识API服务": {
+ "name": "淘宝AudID标识API服务/用户设备唯一标识生成与验证接口",
+ "categoryId": 24,
+ "url": "audid-api.taobao.com",
+ "icon": "https://www.taobao.com/favicon.ico"
+ },
+ "淘宝移动端主站": {
+ "name": "淘宝移动端主站/手机淘宝核心入口",
+ "categoryId": 4,
+ "url": "m.taobao.com",
+ "icon": "https://www.taobao.com/favicon.ico"
+ },
+ "淘宝开放平台网关": {
+ "name": "淘宝开放平台网关/第三方应用API接入入口",
+ "categoryId": 24,
+ "url": "gw.api.taobao.com",
+ "icon": "https://www.taobao.com/favicon.ico"
+ },
+ "淘宝TOP开放平台接口": {
+ "name": "淘宝TOP开放平台接口/电商业务API调用服务",
+ "categoryId": 24,
+ "url": "top.api.taobao.com",
+ "icon": "https://www.taobao.com/favicon.ico"
+ },
+ "淘宝商家工具集": {
+ "name": "淘宝商家工具集/商家运营辅助工具",
+ "categoryId": 6,
+ "url": "tool.taobao.com",
+ "icon": "https://www.taobao.com/favicon.ico"
+ },
+ "淘宝卖家中心": {
+ "name": "淘宝卖家中心/店铺管理与运营后台",
+ "categoryId": 6,
+ "url": "seller.taobao.com",
+ "icon": "https://www.taobao.com/favicon.ico"
+ },
+ "淘宝支付服务入口": {
+ "name": "淘宝支付服务入口/订单支付与结算服务",
+ "categoryId": 23,
+ "url": "pay.taobao.com",
+ "icon": "https://www.taobao.com/favicon.ico"
+ },
+ "淘宝核心静态资源CDN": {
+ "name": "淘宝核心静态资源CDN/全局样式脚本分发加速",
+ "categoryId": 2,
+ "url": "g.tbcdn.cn",
+ "icon": "https://www.taobao.com/favicon.ico"
+ },
+ "淘宝前端静态资源CDN": {
+ "name": "淘宝前端静态资源CDN/网页静态文件分发加速",
+ "categoryId": 2,
+ "url": "assets.taobao.com",
+ "icon": "https://www.taobao.com/favicon.ico"
+ },
+ "淘宝图片存储CDN": {
+ "name": "淘宝图片存储CDN/商品图片与素材分发加速",
+ "categoryId": 2,
+ "url": "img.alicdn.com",
+ "icon": "https://www.taobao.com/favicon.ico"
+ },
+ "淘宝移动端专属CDN": {
+ "name": "淘宝移动端专属CDN/手机淘宝静态资源分发加速",
+ "categoryId": 2,
+ "url": "cdn.m.taobao.com",
+ "icon": "https://www.taobao.com/favicon.ico"
+ },
+ "淘宝搜索结果CDN": {
+ "name": "淘宝搜索结果CDN/搜索页面静态资源分发加速",
+ "categoryId": 2,
+ "url": "s.tbcdn.cn",
+ "icon": "https://www.taobao.com/favicon.ico"
+ }
},
"天猫": {
"name": "天猫",
@@ -291,12 +393,12 @@
"url": "https://www.tmall.com/",
"icon": "https://www.tmall.com/favicon.ico"
},
- "阿里云镜像源":{
+ "阿里云镜像源": {
"name": "阿里巴巴开源镜像站",
"categoryId": 0,
"url": {
"1": "https://mirrors.aliyun.com/"
- },
+ },
"icon": "https://img.alicdn.com/tfs/TB1_ZXuNcfpK1RjSZFOXXa6nFXa-32-32.ico"
},
"万网": {
@@ -306,28 +408,27 @@
"icon": "https://img.alicdn.com/tfs/TB1_ZXuNcfpK1RjSZFOXXa6nFXa-32-32.ico"
},
"阿里CDN": {
- "阿里云CDN":{
+ "阿里云CDN": {
"name": "阿里CDN",
"categoryId": 2,
"url": {
- "1": "kunlunsl.com",
- "2": "cdn.aliyuncs.com",
- "3": "cloudfront.net",
- "4": "alibabadns.com"
+ "1": "kunlunsl.com",
+ "2": "cdn.aliyuncs.com",
+ "3": "cloudfront.net",
+ "4": "alibabadns.com"
},
"icon": "https://img.alicdn.com/tfs/TB1_ZXuNcfpK1RjSZFOXXa6nFXa-32-32.ico"
},
- "GSLB 全局负载均衡调度":{
+ "GSLB 全局负载均衡调度": {
"name": "GSLB 全局负载均衡调度",
"categoryId": 2,
"url": "cdngslb.com",
"icon": "https://img.alicdn.com/tfs/TB1_ZXuNcfpK1RjSZFOXXa6nFXa-32-32.ico"
}
-
},
"company": "阿里云计算有限公司/阿里巴巴集团"
},
- "UC":{
+ "UC": {
"UC浏览器官网": {
"name": "UC浏览器官网",
"categoryId": 4,
@@ -346,21 +447,26 @@
"url": {
"1": "https://wx.qq.com/",
"2": "https://weixin.qq.com/",
- "3": "https://res.wx.qq.com/",
+ "3": "https://res.wx.qq.com/",
"4": "dns.weixin.qq.com",
"5": "pc.weixin.qq.com"
},
"icon": "https://res.wx.qq.com/a/wx_fed/assets/res/NTI4MWU5.ico"
},
+ "腾讯会议核心代理服务": {
+ "name": "腾讯会议核心代理服务",
+ "categoryId": 19,
+ "url": "oth.eve.mdt.qq.com",
+ "icon": "https://meeting.tencent.com/favicon.ico"
+ },
"WeChat": {
"name": "WeChat",
"categoryId": 7,
"url": {
- "1":"wechat.com",
- "2": "support.wechat.com",
- "3": "www.wechat.com"
- },
-
+ "1": "wechat.com",
+ "2": "support.wechat.com",
+ "3": "www.wechat.com"
+ },
"icon": "https://wechat.com/favicon.ico"
},
"微信开放平台": {
@@ -372,57 +478,60 @@
"微信支付": {
"name": "微信支付商户平台",
"categoryId": 23,
- "url": {"1": "pay.weixin.qq.com",
- "2": "pay.wechatpay.cn",
- "3": "act.weixin.qq.com",
- "4": "api.wechatpay.cn",
- "5": "api.mch.weixin.qq.com",
- "6": "api2.mch.weixin.qq.com",
- "7": "api.wechatpay.cn",
- "8": "api2.wechatpay.cn",
- "9": "payapp.weixin.qq.com",
- "10": "payapp.wechatpay.cn",
- "11": "fraud.mch.weixin.qq.com",
- "12": "fraud.wechatpay.cn",
- "13": "action.weixin.qq.com",
- "14": "action.wechatpay.cn",
- "15": "wechatpay.cn"
- },
-
+ "url": {
+ "1": "pay.weixin.qq.com",
+ "2": "pay.wechatpay.cn",
+ "3": "act.weixin.qq.com",
+ "4": "api.wechatpay.cn",
+ "5": "api.mch.weixin.qq.com",
+ "6": "api2.mch.weixin.qq.com",
+ "7": "api.wechatpay.cn",
+ "8": "api2.wechatpay.cn",
+ "9": "payapp.weixin.qq.com",
+ "10": "payapp.wechatpay.cn",
+ "11": "fraud.mch.weixin.qq.com",
+ "12": "fraud.wechatpay.cn",
+ "13": "action.weixin.qq.com",
+ "14": "action.wechatpay.cn",
+ "15": "wechatpay.cn"
+ },
"icon": "https://gtimg.wechatpay.cn/core/favicon.ico"
},
"微信支付海外版": {
"name": "微信支付海外版",
"categoryId": 23,
- "url": {"1": "https://pay.wechatpay.global/",
- "2": "apihk.mch.weixin.qq.com",
- "3": "apius.mch.weixin.qq.com"
- },
+ "url": {
+ "1": "https://pay.wechatpay.global/",
+ "2": "apihk.mch.weixin.qq.com",
+ "3": "apius.mch.weixin.qq.com"
+ },
"icon": "https://gtimg.wechatpay.cn/core/favicon.ico"
},
"微信CDN": {
"name": "微信CDN和日志等资源",
"categoryId": 23,
- "url": {"1": "https://gtimg.wechatpay.cn/",
- "2": "log.wechatpay.cn",
- "3": "newres.wechat.com",
- "4": "res.wx.qq.com",
- "5": "sh.servicewechat.com",
- "6": "res.servicewechat.com"
- },
+ "url": {
+ "1": "https://gtimg.wechatpay.cn/",
+ "2": "log.wechatpay.cn",
+ "3": "newres.wechat.com",
+ "4": "res.wx.qq.com",
+ "5": "sh.servicewechat.com",
+ "6": "res.servicewechat.com"
+ },
"icon": "https://gtimg.wechatpay.cn/core/favicon.ico"
},
"腾讯下载加速网络CDN": {
"name": "腾讯下载加速网络CDN",
"categoryId": 23,
- "url": {"1": "dldir1v6.qq.com",
- "2": "dldir1.qq.com",
- "3": "dldir2.qq.com",
- "4": "dldir3.qq.com",
- "5": "dl.qq.com",
- "6": "dldir.tencent.com",
- "7": "pc.qq.com"
- },
+ "url": {
+ "1": "dldir1v6.qq.com",
+ "2": "dldir1.qq.com",
+ "3": "dldir2.qq.com",
+ "4": "dldir3.qq.com",
+ "5": "dl.qq.com",
+ "6": "dldir.tencent.com",
+ "7": "pc.qq.com"
+ },
"icon": "https://www.tencent.com/favicon.ico"
},
"腾讯QQ": {
@@ -471,14 +580,14 @@
"name": "腾讯广告",
"categoryId": 0,
"url": {
- "1": "ugdtimg.com",
- "2": "gdtimg.com",
+ "1": "ugdtimg.com",
+ "2": "gdtimg.com",
"3": "ad.qq.com",
"4": "v3.gdt.qq.com",
"5": "c3.gdt.qq.com",
"6": "v2mi.gdt.qq.com",
"7": "gdt.qq.com"
- },
+ },
"icon": "https://www.tencent.com/favicon.ico"
},
"腾讯Beacon事件日志上报": {
@@ -487,91 +596,137 @@
"url": "aeventlog.beacon.qq.com",
"icon": "https://www.tencent.com/favicon.ico"
},
- "QQ邮箱相关":{
+ "QQ邮箱相关": {
"name": "QQ邮箱",
"categoryId": 0,
- "url": {"1": "mail.qq.com",
- "2": "imap.qq.com",
- "3": "smtp.qq.com",
- "4": "w.mail.qq.com"
- },
+ "url": {
+ "1": "mail.qq.com",
+ "2": "imap.qq.com",
+ "3": "smtp.qq.com",
+ "4": "w.mail.qq.com"
+ },
"icon": "https://mail.qq.com/favicon.ico",
"company": "深圳腾讯计算机系统有限公司"
},
- "腾讯企业邮箱":{
+ "腾讯企业邮箱": {
"name": "腾讯企业邮箱",
"categoryId": 0,
- "url": {"1": "https://exmail.qq.com/",
- "2": "https://exmail.qq.com/"
- },
+ "url": {
+ "1": "https://exmail.qq.com/",
+ "2": "https://exmail.qq.com/"
+ },
"icon": "https://exmail.qq.com/favicon.ico",
"company": "深圳腾讯计算机系统有限公司"
},
- "企业微信":{
+ "企业微信": {
"name": "企业微信",
"categoryId": 0,
- "url": {"1": "https://work.weixin.qq.com/",
- "2": "https://work.weixin.qq.com/"
- },
+ "url": {
+ "1": "https://work.weixin.qq.com/",
+ "2": "https://work.weixin.qq.com/"
+ },
"icon": "https://work.weixin.qq.com/favicon.ico",
"company": "深圳腾讯计算机系统有限公司"
},
- "微信青少年相关":{
- "微信守护平台":{
+ "微信青少年相关": {
+ "微信守护平台": {
"name": "微信守护平台",
"categoryId": 22,
- "url": "wxguard.weixin.qq.com",
+ "url": "wxguard.weixin.qq.com",
"icon": "https://open.weixin.qq.com/favicon.ico"
},
- "微信未成年人服务短链接":{
+ "微信未成年人服务短链接": {
"name": "微信未成年人服务短链接",
"categoryId": 22,
- "url": {"1": "minorshort.weixin.qq.com",
- "2": "szminorshort.weixin.qq.com"
- },
+ "url": {
+ "1": "minorshort.weixin.qq.com",
+ "2": "szminorshort.weixin.qq.com"
+ },
"icon": "https://open.weixin.qq.com/favicon.ico"
},
- "深圳地区扩展短链接":{
+ "深圳地区扩展短链接": {
"name": "深圳地区扩展短链接",
"categoryId": 22,
"url": "szextshort.weixin.qq.com",
"icon": "https://open.weixin.qq.com/favicon.ico"
},
- "腾讯云":{
+ "腾讯云": {
"name": "腾讯云",
"categoryId": 0,
- "url": "https://cloud.tencent.com/",
+ "url": {
+ "1": "https://cloud.tencent.com/",
+ "2": "https://www.tencentcloud.com/"
+ },
"icon": "https://cloud.tencent.com/favicon.ico",
"company": "深圳腾讯计算机系统有限公司"
},
- "腾讯云CDN":{
+ "腾讯云CDN": {
"name": "腾讯云CDN",
"categoryId": 2,
"url": {
- "1": "dnsv1.com",
- "2": "tencent-cloud.cn",
- "3": "tencent-cloud.com"
- },
+ "1": "dnsv1.com",
+ "2": "tencent-cloud.cn",
+ "3": "tencent-cloud.com"
+ },
"icon": "https://cloud.tencent.com/favicon.ico",
"company": "深圳腾讯计算机系统有限公司"
},
- "PCDN 性能统计与数据上报":{
- "name": "PCDN 性能统计与数据上报",
+ "qcloud": {
+ "name": "腾讯云核心服务域",
"categoryId": 2,
- "url": "tencent-cloud.net",
+ "url": "myqcloud.com",
"icon": "https://cloud.tencent.com/favicon.ico"
},
- "company": "深圳腾讯计算机系统有限公司"
- }
- },
+ "腾讯云 EdgeOne (EO) 边缘安全加速平台": {
+ "name": "腾讯云 EdgeOne (EO) 边缘安全加速平台",
+ "categoryId": 2,
+ "url": {
+ "1": "dnse3.com",
+ "2": "eo.dnse3.com"
+ },
+ "icon": "https://cloud.tencent.com/favicon.ico"
+ },
+ "腾讯云全球加速 CDN": {
+ "name": "腾讯云全球加速 CDN",
+ "categoryId": 2,
+ "url": {
+ "1": "dnse1.com",
+ "2": "dnse2.com",
+ "3": "dnsoe2.com",
+ "4": "dnse5.com"
+ },
+ "icon": "https://cloud.tencent.com/favicon.ico"
+ },
+ "腾讯云DNSPod智能调度服务": {
+ "name": "腾讯云DNSPod智能调度服务",
+ "categoryId": 2,
+ "url": "tdnsdp1.cn",
+ "icon": "https://dnspod.com/favicon.ico"
+ },
+ "PCDN 性能统计与数据上报": {
+ "name": "PCDN 性能统计与数据上报",
+ "categoryId": 2,
+ "url": "apd-pcdnwxstat.teg.tencent-cloud.net",
+ "icon": "https://cloud.tencent.com/favicon.ico"
+ },
+ "IAS(智能加速服务)的动态调度 / 边缘节点": {
+ "name": "IAS(智能加速服务)的动态调度 / 边缘节点",
+ "categoryId": 2,
+ "url": "ias.tencent-cloud.net",
+ "icon": "https://cloud.tencent.com/favicon.ico"
+ }
+ },
+ "company": "深圳市腾讯计算机系统有限公司"
+ },
"高德地图相关": {
"高德地图": {
"name": "高德地图",
"categoryId": 12,
- "url": {"1": "https://map.amap.com/",
- "2": "https://ditu.amap.com/",
- "3": "https://www.amap.com/"
- },
+ "url": {
+ "1": "https://map.amap.com/",
+ "2": "https://ditu.amap.com/",
+ "3": "https://www.amap.com/"
+ },
"icon": "https://a.amap.com/pc/static/favicon.ico"
},
"高德开放平台": {
@@ -581,16 +736,16 @@
"icon": "https://a.amap.com/pc/static/favicon.ico"
},
"高德地图API": {
- "高德地图API":{
+ "高德地图API": {
"name": "高德地图API",
"categoryId": 22,
"url": {
- "1":"https://restapi.amap.com/",
- "2": "https://webapi.amap.com/"
+ "1": "https://restapi.amap.com/",
+ "2": "https://webapi.amap.com/"
},
"icon": "https://a.amap.com/pc/static/favicon.ico"
},
- "高德前端性能与数据采集":{
+ "高德前端性能与数据采集": {
"name": "高德前端性能与数据采集",
"categoryId": 22,
"url": "fp.amap.com",
@@ -598,12 +753,13 @@
}
},
"高德CDN": {
- "移动端 CDN 资源分发":{
+ "移动端 CDN 资源分发": {
"name": "移动端 CDN 资源分发N",
"categoryId": 2,
- "url": {"1": "m5.amap.com",
- "2": "m5-x.amap.com"
- },
+ "url": {
+ "1": "m5.amap.com",
+ "2": "m5-x.amap.com"
+ },
"icon": "https://a.amap.com/pc/static/favicon.ico"
}
},
@@ -629,38 +785,76 @@
"icon": "https://www.microsoft.com/favicon.ico?v2"
},
"微软必应": {
- "name": "微软必应",
- "categoryId": 1,
- "url": {"1": "www.bing.com",
+ "微软必应": {
+ "name": "微软必应",
+ "categoryId": 1,
+ "url": {
+ "1": "www.bing.com",
"2": "cn.bing.com",
"3": "bing.com"
},
- "icon": "https://www.bing.com/favicon.ico"
+ "icon": "https://www.bing.com/favicon.ico"
+ },
+ "Bing重定向服务": {
+ "name": "Bing重定向服务/搜索请求重定向处理",
+ "categoryId": 1,
+ "url": "r.bing.com",
+ "icon": "https://www.bing.com/favicon.ico"
+ },
+ "Bing静态资源节点": {
+ "name": "Bing静态资源节点/页面静态资源分发加速",
+ "categoryId": 2,
+ "url": {
+ "1": "ts1.tc.mm.bing.net",
+ "2": "ts2.tc.mm.bing.net",
+ "3": "ts3.tc.mm.bing.net",
+ "4": "ts4.tc.mm.bing.net"
+ },
+ "icon": "https://www.bing.com/favicon.ico"
+ },
+ "Bing备用搜索主站": {
+ "name": "Bing备用搜索主站/搜索引擎备用访问入口",
+ "categoryId": 1,
+ "url": "www2.bing.com",
+ "icon": "https://www.bing.com/favicon.ico"
+ },
+ "Bing图片缩略图服务": {
+ "name": "Bing图片缩略图服务/搜索结果图片缩略图分发",
+ "categoryId": 2,
+ "url": "th.bing.com",
+ "icon": "https://www.bing.com/favicon.ico"
+ },
+ "Bing奖励计划官网": {
+ "name": "Bing奖励计划官网/搜索奖励积分查询与兑换",
+ "categoryId": 6,
+ "url": "rewards.bing.com",
+ "icon": "https://www.bing.com/favicon.ico"
+ }
},
"OneCollector/1DS(微软统一数据收集平台)": {
"name": "微软OneCollector/1DS (One Data Strategy) 统一遥测收集系统",
"categoryId": 0,
- "url": {"1": "mobile.events.data.microsoft.com",
- "2": "us-mobile.events.data.microsoft.com",
- "3": "eu-mobile.events.data.microsoft.com",
- "4": "au-mobile.events.data.microsoft.com",
- "5": "jp-mobile.events.data.microsoft.com",
- "6": "browser.events.data.microsoft.com",
- "7": "v10.events.data.microsoft.com",
- "8": "mobile.pipe.aria.microsoft.com",
- "9": "in.appcenter.ms"
- },
-
+ "url": {
+ "1": "mobile.events.data.microsoft.com",
+ "2": "us-mobile.events.data.microsoft.com",
+ "3": "eu-mobile.events.data.microsoft.com",
+ "4": "au-mobile.events.data.microsoft.com",
+ "5": "jp-mobile.events.data.microsoft.com",
+ "6": "browser.events.data.microsoft.com",
+ "7": "v10.events.data.microsoft.com",
+ "8": "mobile.pipe.aria.microsoft.com",
+ "9": "in.appcenter.ms"
+ },
"icon": "https://www.microsoft.com/favicon.ico?v2"
},
"微软连接测试": {
"name": "微软连接测试",
"categoryId": 0,
"url": {
- "1": "msftconnecttest.com",
- "2": "ipv6.msftconnecttest.com",
- "3": "ipv4.msftconnecttest.com",
- "4": "www.msftconnecttest.com"
+ "1": "msftconnecttest.com",
+ "2": "ipv6.msftconnecttest.com",
+ "3": "ipv4.msftconnecttest.com",
+ "4": "www.msftconnecttest.com"
},
"icon": "https://www.microsoft.com/favicon.ico?v2"
},
@@ -689,10 +883,24 @@
"icon": "https://insider.microsoft.com/favicon.ico"
},
"Azure": {
- "name": "Azure",
- "categoryId": 0,
- "url": "https://www.azure.com/",
- "icon": "https://portal.azure.com/Content/favicon.ico"
+ "微软Azure官网": {
+ "name": "Azure",
+ "categoryId": 0,
+ "url": "https://www.azure.com/",
+ "icon": "https://portal.azure.com/Content/favicon.ico"
+ },
+ "微软Azure AD统一身份认证服务": {
+ "name": "微软Azure AD统一身份认证服务",
+ "categoryId": 21,
+ "url": "login.microsoftonline.com",
+ "icon": "https://login.microsoftonline.com/favicon.ico"
+ },
+ "微软身份平台服务CDN": {
+ "name": "微软身份平台服务CDN",
+ "categoryId": 2,
+ "url": "msidentity.com",
+ "icon": "https://login.microsoftonline.com/favicon.ico"
+ }
},
"网络连接状态指示器(NCSI)": {
"name": "网络连接状态指示器(NCSI)",
@@ -704,19 +912,46 @@
},
"icon": "https://www.microsoft.com/favicon.ico"
},
- "微软MSN": {
- "name": "微软MSN",
- "categoryId": 0,
- "url": "https://www.msn.cn/",
- "icon": "https://www.msn.cn/favicon.ico"
+ "MSN": {
+ "微软MSN": {
+ "name": "微软MSN",
+ "categoryId": 0,
+ "url": "https://www.msn.cn/",
+ "icon": "https://www.msn.cn/favicon.ico"
+ },
+ "MSN中国事件数据收集沙盒服务器": {
+ "name": "MSN中国事件数据收集沙盒服务器/测试环境用户行为与事件数据采集服务",
+ "categoryId": 5,
+ "url": "events-sandbox.data.msn.cn",
+ "icon": "https://www.msn.cn/favicon.ico"
+ },
+ "msnapi": {
+ "name": "微软MSN API",
+ "categoryId": 0,
+ "url": {
+ "1": "https://api.msn.com/",
+ "2": "api.msn.cn"
+ },
+ "icon": "https://www.msn.cn/favicon.ico"
+ },
+ "MSN静态资源": {
+ "name": "微软MSN静态资源",
+ "categoryId": 2,
+ "url": {
+ "1": "assets.msn.cn",
+ "2": "img-s.msn.cn",
+ "icon": "https://www.msn.cn/favicon.ico"
+ },
+ "company": "上海美思恩网络通信技术有限公司 (MSN 中国)"
+ }
},
- "Edge CDN":{
+ "Edge CDN": {
"name": "微软 Edge 浏览器的CDN 与边缘服务根域名",
"categoryId": 2,
"url": "a-msedge.net",
"icon": "https://www.microsoft.com/favicon.ico"
},
- "微软 WNS(Windows 推送通知服务)的客户端通信":{
+ "微软 WNS(Windows 推送通知服务)的客户端通信": {
"name": "微软 WNS(Windows 推送通知服务)的客户端通信域名",
"categoryId": 2,
"url": "client.wns.windows.com",
@@ -725,11 +960,19 @@
"company": "微软 Microsoft"
},
"字节跳动": {
- "抖音": {
- "name": "抖音",
- "categoryId": 0,
- "url": "https://www.douyin.com/",
- "icon": "https://www.douyin.com/favicon.ico"
+ "抖音相关": {
+ "抖音": {
+ "name": "抖音",
+ "categoryId": 0,
+ "url": "https://www.douyin.com/",
+ "icon": "https://www.douyin.com/favicon.ico"
+ },
+ "吉云互动HTTPDNS解析服务": {
+ "name": "吉云互动HTTPDNS解析服务/移动端防劫持精准域名解析",
+ "categoryId": 7,
+ "url": "dig.bdurl.net",
+ "icon": "data:image/vnd.microsoft.icon;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAMAAABOo35HAAACFlBMVEUAAACp3f8yW7U8jf8zW7U/3vJA5P8AydMzW7VPlP955908jP8AydM8jf8yWrV5590yWrV45908jf8yW7UyW7UyW7V55909jf88jf8yW7UzW7V56N49jf80XLY+j/80XbY/lf8zZr5Ilf9GXbkA2+RCY70zZsx459x5590AydI8jP8AydN4590AydM8jf8AydM8jf88jf94590AyNM8jf8AydN5590AydM8jf8AyNIAytR5594zW7V56N09jf955948jf8AydMAytN65948jf956N56594AytMAy9Q9jv976d8AytYAy9Z66N566uE/jv8AztUAy9cAytV56OSD798A0NYAydM8jP95590yW7R45twAydN45909jP8yWrU8jf945t0AydIyWrV5590AydIyW7UzWrU8jf8AydIzW7UzWrV459145908jf8yW7UAydM8jP8AydM8jf9459155909jf8zW7UAydQAytMAydN56N0AydMzXLYAydM0W7UAydQ8jv8+jv8Ay9QzW7Y9jf965t176N80W7YAydN56N156d4yW7U0XLc+jv8+kP8Ay9VAj/81XbgAzNQ1XbY5XbZAkf9+7eQ7Yrp96+FDlv+F6emI/+4AydN55t15594AydM+jf8+jf8+jv8AydQ1W7QzW7UAydR75942XbUyWrR86OCA8uYAztqA8eN45twyWrQAyNI8jP8bed+KAAAArnRSTlMAAfb9+wUD8HMJ/fr59+jb1dTLt6eamY+Jg3xXTkhHOhIRDQsJBwX59vbr4+Dc1tXRxcC7ubSqoJuak5CKiIN+enlyb21lXVFEQj09NzAnJiQdGBUPDvzx8O/s6Obl397OzsvIyMXEwcG/vbiysbGuqqeknZSTkI2Gf3ZraGViX15YWFVTUk9OS0pEQUA8MzAtLSomHx4bGhkXCwfApKCAdGdjXlxaWTY0MyEUFBKNTPVmAAAFHElEQVR42uzX105UYRSG4YUyiCgqKvbeK3ZFsaCCXUHE3kvU2FvU2EussfcSY2KiiSUDc4fuTcl/xr8/ZuZk7/e9g/UcfcuIiIiIiIiIiIiIiMhf7wFDPxlFKx1UdunBX6NoWGEHqp40GnmwXCWHlzz9YuTBck2cvWzdVyMPlmtSAJYy8mE5sJPXNhYYebBcO06tBMyP5do1v3aTkcPytedb7R8jh+Vr2sVVHyzxpaMXfEZbLNGltcqqHjba8LGWzNJyJdZUeHTws/GWvNJ61hTW5cSQkRMsWXUWqxWsX/HvJC2xTmK5dp4pfp0UsCywXL3633hrCShrLAd2653FvBxgufaV//xoMS5HWK4+5fdHW0zLJZYDu/wolmC5xXIVHlocv6GfcyxX/IZ+HrHiN/TziuWGvsWiLLCULBaBBVZLYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYOW1erCitfn2gqnNYPlruLdwenMQWJ7+rV40I3QCy9PWx1cOhkZgeRq35uqRbYEPWD6o9d+P9wxxwOq41IblcyaHMGB1XNHLFfO6hShgeXpz/WwLFFie6m+e3x1igBVlmgeB5anhV+s0B8tX2zQHK0rB8WCBZQYWWGCBBRZYYIEFFlhggQUWWGCBBRZYYIGVZKyKgcNGmYEVCSuTyXTtO2jEGLCiYYX1mLV0bXewfFiu0mPVz1NgebEc2NzqF0Vg+bBc20/XvCoAy4flmnKupg4sL5Zr74UfdWB5sVz7K++8B8theauovPsZLIflbWYw9MGKiNU+9MGKlBv6YEWvFCwhsMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMD6325d7cQVhWEY/ocWn9JSoO5GhTqlSo2Wurt72qbu3lRwgruEQCCBE+6RHQ7YR7DWP8OeWSHvewnPwZcPLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLCmGtbhHclg+Vimkk4f2r4OLLEu8dS3Z2Gw7Ft1/OvjMFj2Xan48uAqWAqwY3s2g6Xo4r/dG8FSdKF/5wawFJ3/9e4WWIoaf7xJBsu+UJ139MHyszr6YKmOfncYLM3RB0sRWGDZBBZYYIEFFlhggQUWWGBFgpWQBZYd1PMFS9cIWMamP5lfvVq8wDJAPdy3eKV4gWVo1ufy5eIFlhigcv6PQoFlwLqbM9AmXmAZsDKyfzeLX6BY6U/FJjexMrJ/NolfoFjTZs9bkipWuYd143VKQ0iiyh5qy9xFK8Q6t7ASslKGfKiAsTZ9KlkmqtzB8q95DLDu7frbIXEvIiz/mscC6877Py3iRJFg+dc8cKz1b/vOiTPprdJk0hsH6tWRenEqpVTmx9JhkeCxruX31obEtRRQM+csvCTq9Fgz8g/WJImLWULl9RS3ijo9VvrW/SdSxdUsoG4XHD0rgRb3az45WDcLitRQ+uJ+zaPHuv6y6ExsRjbu1zw6rLUvvg+6ObKqgsfK3XbgZKKQESv3UWFVl5ARK+1+YWWnkBkrc2/ZZaGJG7vmZC7vQ3G7EBERERERERERERER0dRtBK5Q857p1uutAAAAAElFTkSuQmCC"
+ }
},
"字节跳动官网": {
"name": "字节跳动官网",
@@ -737,6 +980,248 @@
"url": "https://www.bytedance.com/",
"icon": "data:image/vnd.microsoft.icon;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAMAAABOo35HAAACFlBMVEUAAACp3f8yW7U8jf8zW7U/3vJA5P8AydMzW7VPlP955908jP8AydM8jf8yWrV5590yWrV45908jf8yW7UyW7UyW7V55909jf88jf8yW7UzW7V56N49jf80XLY+j/80XbY/lf8zZr5Ilf9GXbkA2+RCY70zZsx459x5590AydI8jP8AydN4590AydM8jf8AydM8jf88jf94590AyNM8jf8AydN5590AydM8jf8AyNIAytR5594zW7V56N09jf955948jf8AydMAytN65948jf956N56594AytMAy9Q9jv976d8AytYAy9Z66N566uE/jv8AztUAy9cAytV56OSD798A0NYAydM8jP95590yW7R45twAydN45909jP8yWrU8jf945t0AydIyWrV5590AydIyW7UzWrU8jf8AydIzW7UzWrV459145908jf8yW7UAydM8jP8AydM8jf9459155909jf8zW7UAydQAytMAydN56N0AydMzXLYAydM0W7UAydQ8jv8+jv8Ay9QzW7Y9jf965t176N80W7YAydN56N156d4yW7U0XLc+jv8+kP8Ay9VAj/81XbgAzNQ1XbY5XbZAkf9+7eQ7Yrp96+FDlv+F6emI/+4AydN55t15594AydM+jf8+jf8+jv8AydQ1W7QzW7UAydR75942XbUyWrR86OCA8uYAztqA8eN45twyWrQAyNI8jP8bed+KAAAArnRSTlMAAfb9+wUD8HMJ/fr59+jb1dTLt6eamY+Jg3xXTkhHOhIRDQsJBwX59vbr4+Dc1tXRxcC7ubSqoJuak5CKiIN+enlyb21lXVFEQj09NzAnJiQdGBUPDvzx8O/s6Obl397OzsvIyMXEwcG/vbiysbGuqqeknZSTkI2Gf3ZraGViX15YWFVTUk9OS0pEQUA8MzAtLSomHx4bGhkXCwfApKCAdGdjXlxaWTY0MyEUFBKNTPVmAAAFHElEQVR42uzX105UYRSG4YUyiCgqKvbeK3ZFsaCCXUHE3kvU2FvU2EussfcSY2KiiSUDc4fuTcl/xr8/ZuZk7/e9g/UcfcuIiIiIiIiIiIiIiMhf7wFDPxlFKx1UdunBX6NoWGEHqp40GnmwXCWHlzz9YuTBck2cvWzdVyMPlmtSAJYy8mE5sJPXNhYYebBcO06tBMyP5do1v3aTkcPytedb7R8jh+Vr2sVVHyzxpaMXfEZbLNGltcqqHjba8LGWzNJyJdZUeHTws/GWvNJ61hTW5cSQkRMsWXUWqxWsX/HvJC2xTmK5dp4pfp0UsCywXL3633hrCShrLAd2653FvBxgufaV//xoMS5HWK4+5fdHW0zLJZYDu/wolmC5xXIVHlocv6GfcyxX/IZ+HrHiN/TziuWGvsWiLLCULBaBBVZLYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYOW1erCitfn2gqnNYPlruLdwenMQWJ7+rV40I3QCy9PWx1cOhkZgeRq35uqRbYEPWD6o9d+P9wxxwOq41IblcyaHMGB1XNHLFfO6hShgeXpz/WwLFFie6m+e3x1igBVlmgeB5anhV+s0B8tX2zQHK0rB8WCBZQYWWGCBBRZYYIEFFlhggQUWWGCBBRZYYIGVZKyKgcNGmYEVCSuTyXTtO2jEGLCiYYX1mLV0bXewfFiu0mPVz1NgebEc2NzqF0Vg+bBc20/XvCoAy4flmnKupg4sL5Zr74UfdWB5sVz7K++8B8theauovPsZLIflbWYw9MGKiNU+9MGKlBv6YEWvFCwhsMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMD6325d7cQVhWEY/ocWn9JSoO5GhTqlSo2Wurt72qbu3lRwgruEQCCBE+6RHQ7YR7DWP8OeWSHvewnPwZcPLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLCmGtbhHclg+Vimkk4f2r4OLLEu8dS3Z2Gw7Ft1/OvjMFj2Xan48uAqWAqwY3s2g6Xo4r/dG8FSdKF/5wawFJ3/9e4WWIoaf7xJBsu+UJ139MHyszr6YKmOfncYLM3RB0sRWGDZBBZYYIEFFlhggQUWWGBFgpWQBZYd1PMFS9cIWMamP5lfvVq8wDJAPdy3eKV4gWVo1ufy5eIFlhigcv6PQoFlwLqbM9AmXmAZsDKyfzeLX6BY6U/FJjexMrJ/NolfoFjTZs9bkipWuYd143VKQ0iiyh5qy9xFK8Q6t7ASslKGfKiAsTZ9KlkmqtzB8q95DLDu7frbIXEvIiz/mscC6877Py3iRJFg+dc8cKz1b/vOiTPprdJk0hsH6tWRenEqpVTmx9JhkeCxruX31obEtRRQM+csvCTq9Fgz8g/WJImLWULl9RS3ijo9VvrW/SdSxdUsoG4XHD0rgRb3az45WDcLitRQ+uJ+zaPHuv6y6ExsRjbu1zw6rLUvvg+6ObKqgsfK3XbgZKKQESv3UWFVl5ARK+1+YWWnkBkrc2/ZZaGJG7vmZC7vQ3G7EBERERERERERERER0dRtBK5Q857p1uutAAAAAElFTkSuQmCC"
},
+ "Trae": {
+ "Trae中国版主站": {
+ "name": "Trae中国版官方主站/AI IDE下载入口",
+ "categoryId": 10,
+ "url": "www.trae.cn",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae中国版核心API接口": {
+ "name": "Trae中国版核心API服务/模型调用与功能扩展接口",
+ "categoryId": 11,
+ "url": "api.trae.com.cn",
+ "icon": "https://www.trae.com.cn/favicon.ico"
+ },
+ "Trae国际版Normal模式核心服务": {
+ "name": "Trae国际版Normal模式/基础AI编程功能接口",
+ "categoryId": 11,
+ "url": "grow-normal.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae中国版备用官网": {
+ "name": "Trae中国版备用官网/国内服务入口",
+ "categoryId": 10,
+ "url": "www.trae.com.cn",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版主站": {
+ "name": "Trae国际版官方主站/全球AI编程服务入口",
+ "categoryId": 10,
+ "url": "www.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae官方文档中心": {
+ "name": "Trae中国版官方文档中心/使用指南与API参考",
+ "categoryId": 15,
+ "url": "docs.trae.com.cn",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae产品资讯网站": {
+ "name": "Trae产品介绍与资讯平台",
+ "categoryId": 10,
+ "url": "traeai.com",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae中国版资讯百科": {
+ "name": "Trae中国版相关资讯与百科网站",
+ "categoryId": 10,
+ "url": "traecn.com",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae中国版CDN节点": {
+ "name": "Trae中国版静态资源CDN/官网图片JS安装包分发加速",
+ "categoryId": 2,
+ "url": "lf-cdn.trae.com.cn",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版CDN节点": {
+ "name": "Trae国际版静态资源CDN/国际版官网资源分发加速",
+ "categoryId": 2,
+ "url": "lf-cdn.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版Agent模式核心服务": {
+ "name": "Trae国际版Agent模式/智能体编程与复杂任务处理接口",
+ "categoryId": 11,
+ "url": "grow-agent.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版Builder模式核心服务": {
+ "name": "Trae国际版Builder模式/全栈项目构建与快速原型开发接口",
+ "categoryId": 11,
+ "url": "grow-builder.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版SOLO模式核心服务": {
+ "name": "Trae国际版SOLO模式/高级智能体与复杂任务自主执行接口",
+ "categoryId": 11,
+ "url": "grow-solo.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版高级功能服务": {
+ "name": "Trae国际版高级功能/代码重构与性能优化专业接口",
+ "categoryId": 11,
+ "url": "grow-advanced.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版主API接口": {
+ "name": "Trae国际版主API/用户认证与模型管理基础接口",
+ "categoryId": 11,
+ "url": "api.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版官方文档中心": {
+ "name": "Trae国际版文档中心/多语言使用指南与API参考",
+ "categoryId": 15,
+ "url": "docs.trae.ai",
+ "icon": "https://docs.trae.ai/favicon.ico"
+ },
+ "Trae国际版静态资源CDN": {
+ "name": "Trae国际版静态资源CDN/IDE客户端与网页资源加速",
+ "categoryId": 2,
+ "url": "static.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版资产文件CDN": {
+ "name": "Trae国际版资产文件CDN/模型权重与工具包分发",
+ "categoryId": 2,
+ "url": "assets.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版英文官网": {
+ "name": "Trae国际版英文官网/全球英语用户服务平台",
+ "categoryId": 1,
+ "url": "en.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版日文官网": {
+ "name": "Trae国际版日文官网/日本市场本地化服务平台",
+ "categoryId": 1,
+ "url": "ja.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版韩文官网": {
+ "name": "Trae国际版韩文官网/韩国市场本地化服务平台",
+ "categoryId": 1,
+ "url": "ko.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版德文官网": {
+ "name": "Trae国际版德文官网/德语区市场本地化服务平台",
+ "categoryId": 1,
+ "url": "de.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版法文官网": {
+ "name": "Trae国际版法文官网/法语区市场本地化服务平台",
+ "categoryId": 1,
+ "url": "fr.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版插件市场": {
+ "name": "Trae国际版插件市场/第三方工具与主题集成中心",
+ "categoryId": 12,
+ "url": "marketplace.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版集成中心": {
+ "name": "Trae国际版集成中心/外部服务与代码仓库集成指南",
+ "categoryId": 13,
+ "url": "integrations.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版API网关": {
+ "name": "Trae国际版API网关/第三方集成安全与流量管理",
+ "categoryId": 11,
+ "url": "api-gateway.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版开发者SDK": {
+ "name": "Trae国际版开发者SDK/第三方应用集成工具包",
+ "categoryId": 14,
+ "url": "sdk.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版全局CDN": {
+ "name": "Trae国际版全局CDN/跨区域静态资源加速分发",
+ "categoryId": 2,
+ "url": "cdn.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版图片CDN": {
+ "name": "Trae国际版图片CDN/媒体资源加速与存储",
+ "categoryId": 2,
+ "url": "img.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版全局管理后台": {
+ "name": "Trae国际版全局管理后台/用户与权限核心管理平台",
+ "categoryId": 16,
+ "url": "admin.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版开发者控制台": {
+ "name": "Trae国际版开发者控制台/API与资源管理中心",
+ "categoryId": 16,
+ "url": "console.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版团队管理平台": {
+ "name": "Trae国际版团队管理平台/协作与权限控制中心",
+ "categoryId": 16,
+ "url": "team.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版计费系统": {
+ "name": "Trae国际版计费系统/订阅与支付管理平台",
+ "categoryId": 17,
+ "url": "billing.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版服务监控平台": {
+ "name": "Trae国际版服务监控平台/系统状态与告警中心",
+ "categoryId": 18,
+ "url": "monitor.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版日志中心": {
+ "name": "Trae国际版日志中心/操作与请求日志管理平台",
+ "categoryId": 19,
+ "url": "logs.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版遥测服务": {
+ "name": "Trae国际版遥测服务/客户端数据收集与分析平台",
+ "categoryId": 20,
+ "url": "telemetry.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版服务状态页": {
+ "name": "Trae国际版服务状态页/实时可用性与故障公告",
+ "categoryId": 21,
+ "url": "status.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版统一认证服务": {
+ "name": "Trae国际版统一认证服务/SSO与身份管理中心",
+ "categoryId": 22,
+ "url": "auth.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版审计日志平台": {
+ "name": "Trae国际版审计日志平台/合规追踪与报告生成",
+ "categoryId": 23,
+ "url": "audit.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ },
+ "Trae国际版运维管理平台": {
+ "name": "Trae国际版运维管理平台/服务部署与故障处理中心",
+ "categoryId": 24,
+ "url": "ops.trae.ai",
+ "icon": "https://lf-cdn.trae.com.cn/obj/trae-com-cn/trae_website_prod_cn/favicon.png"
+ }
+ },
"今日头条": {
"name": "今日头条",
"categoryId": 0,
@@ -750,42 +1235,62 @@
"icon": "https://v.douyin.com/favicon.ico"
},
"字节跳动API服务": {
- "name": "字节API服务",
+ "name": "字节跳动API服务",
"categoryId": 22,
"url": "zijieapi.com",
"icon": "data:image/vnd.microsoft.icon;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAMAAABOo35HAAACFlBMVEUAAACp3f8yW7U8jf8zW7U/3vJA5P8AydMzW7VPlP955908jP8AydM8jf8yWrV5590yWrV45908jf8yW7UyW7UyW7V55909jf88jf8yW7UzW7V56N49jf80XLY+j/80XbY/lf8zZr5Ilf9GXbkA2+RCY70zZsx459x5590AydI8jP8AydN4590AydM8jf8AydM8jf88jf94590AyNM8jf8AydN5590AydM8jf8AyNIAytR5594zW7V56N09jf955948jf8AydMAytN65948jf956N56594AytMAy9Q9jv976d8AytYAy9Z66N566uE/jv8AztUAy9cAytV56OSD798A0NYAydM8jP95590yW7R45twAydN45909jP8yWrU8jf945t0AydIyWrV5590AydIyW7UzWrU8jf8AydIzW7UzWrV459145908jf8yW7UAydM8jP8AydM8jf9459155909jf8zW7UAydQAytMAydN56N0AydMzXLYAydM0W7UAydQ8jv8+jv8Ay9QzW7Y9jf965t176N80W7YAydN56N156d4yW7U0XLc+jv8+kP8Ay9VAj/81XbgAzNQ1XbY5XbZAkf9+7eQ7Yrp96+FDlv+F6emI/+4AydN55t15594AydM+jf8+jf8+jv8AydQ1W7QzW7UAydR75942XbUyWrR86OCA8uYAztqA8eN45twyWrQAyNI8jP8bed+KAAAArnRSTlMAAfb9+wUD8HMJ/fr59+jb1dTLt6eamY+Jg3xXTkhHOhIRDQsJBwX59vbr4+Dc1tXRxcC7ubSqoJuak5CKiIN+enlyb21lXVFEQj09NzAnJiQdGBUPDvzx8O/s6Obl397OzsvIyMXEwcG/vbiysbGuqqeknZSTkI2Gf3ZraGViX15YWFVTUk9OS0pEQUA8MzAtLSomHx4bGhkXCwfApKCAdGdjXlxaWTY0MyEUFBKNTPVmAAAFHElEQVR42uzX105UYRSG4YUyiCgqKvbeK3ZFsaCCXUHE3kvU2FvU2EussfcSY2KiiSUDc4fuTcl/xr8/ZuZk7/e9g/UcfcuIiIiIiIiIiIiIiMhf7wFDPxlFKx1UdunBX6NoWGEHqp40GnmwXCWHlzz9YuTBck2cvWzdVyMPlmtSAJYy8mE5sJPXNhYYebBcO06tBMyP5do1v3aTkcPytedb7R8jh+Vr2sVVHyzxpaMXfEZbLNGltcqqHjba8LGWzNJyJdZUeHTws/GWvNJ61hTW5cSQkRMsWXUWqxWsX/HvJC2xTmK5dp4pfp0UsCywXL3633hrCShrLAd2653FvBxgufaV//xoMS5HWK4+5fdHW0zLJZYDu/wolmC5xXIVHlocv6GfcyxX/IZ+HrHiN/TziuWGvsWiLLCULBaBBVZLYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYOW1erCitfn2gqnNYPlruLdwenMQWJ7+rV40I3QCy9PWx1cOhkZgeRq35uqRbYEPWD6o9d+P9wxxwOq41IblcyaHMGB1XNHLFfO6hShgeXpz/WwLFFie6m+e3x1igBVlmgeB5anhV+s0B8tX2zQHK0rB8WCBZQYWWGCBBRZYYIEFFlhggQUWWGCBBRZYYIGVZKyKgcNGmYEVCSuTyXTtO2jEGLCiYYX1mLV0bXewfFiu0mPVz1NgebEc2NzqF0Vg+bBc20/XvCoAy4flmnKupg4sL5Zr74UfdWB5sVz7K++8B8theauovPsZLIflbWYw9MGKiNU+9MGKlBv6YEWvFCwhsMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMD6325d7cQVhWEY/ocWn9JSoO5GhTqlSo2Wurt72qbu3lRwgruEQCCBE+6RHQ7YR7DWP8OeWSHvewnPwZcPLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLCmGtbhHclg+Vimkk4f2r4OLLEu8dS3Z2Gw7Ft1/OvjMFj2Xan48uAqWAqwY3s2g6Xo4r/dG8FSdKF/5wawFJ3/9e4WWIoaf7xJBsu+UJ139MHyszr6YKmOfncYLM3RB0sRWGDZBBZYYIEFFlhggQUWWGBFgpWQBZYd1PMFS9cIWMamP5lfvVq8wDJAPdy3eKV4gWVo1ufy5eIFlhigcv6PQoFlwLqbM9AmXmAZsDKyfzeLX6BY6U/FJjexMrJ/NolfoFjTZs9bkipWuYd143VKQ0iiyh5qy9xFK8Q6t7ASslKGfKiAsTZ9KlkmqtzB8q95DLDu7frbIXEvIiz/mscC6877Py3iRJFg+dc8cKz1b/vOiTPprdJk0hsH6tWRenEqpVTmx9JhkeCxruX31obEtRRQM+csvCTq9Fgz8g/WJImLWULl9RS3ijo9VvrW/SdSxdUsoG4XHD0rgRb3az45WDcLitRQ+uJ+zaPHuv6y6ExsRjbu1zw6rLUvvg+6ObKqgsfK3XbgZKKQESv3UWFVl5ARK+1+YWWnkBkrc2/ZZaGJG7vmZC7vQ3G7EBERERERERERERER0dRtBK5Q857p1uutAAAAAElFTkSuQmCC"
- },
- "今日头条API服务":{
+ },
+ "今日头条API服务": {
"name": "今日头条API 服务",
"categoryId": 22,
"url": "toutiaoapi.com",
"icon": "https://www.toutiao.com/favicon.ico"
+ },
+ "snssdk": {
+ "日志 / 数据上报核心服务": {
+ "name": "日志 / 数据上报核心服务",
+ "categoryId": 2,
+ "url": "log.snssdk.com",
+ "icon": "data:image/vnd.microsoft.icon;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAMAAABOo35HAAACFlBMVEUAAACp3f8yW7U8jf8zW7U/3vJA5P8AydMzW7VPlP955908jP8AydM8jf8yWrV5590yWrV45908jf8yW7UyW7UyW7V55909jf88jf8yW7UzW7V56N49jf80XLY+j/80XbY/lf8zZr5Ilf9GXbkA2+RCY70zZsx459x5590AydI8jP8AydN4590AydM8jf8AydM8jf88jf94590AyNM8jf8AydN5590AydM8jf8AyNIAytR5594zW7V56N09jf955948jf8AydMAytN65948jf956N56594AytMAy9Q9jv976d8AytYAy9Z66N566uE/jv8AztUAy9cAytV56OSD798A0NYAydM8jP95590yW7R45twAydN45909jP8yWrU8jf945t0AydIyWrV5590AydIyW7UzWrU8jf8AydIzW7UzWrV459145908jf8yW7UAydM8jP8AydM8jf9459155909jf8zW7UAydQAytMAydN56N0AydMzXLYAydM0W7UAydQ8jv8+jv8Ay9QzW7Y9jf965t176N80W7YAydN56N156d4yW7U0XLc+jv8+kP8Ay9VAj/81XbgAzNQ1XbY5XbZAkf9+7eQ7Yrp96+FDlv+F6emI/+4AydN55t15594AydM+jf8+jf8+jv8AydQ1W7QzW7UAydR75942XbUyWrR86OCA8uYAztqA8eN45twyWrQAyNI8jP8bed+KAAAArnRSTlMAAfb9+wUD8HMJ/fr59+jb1dTLt6eamY+Jg3xXTkhHOhIRDQsJBwX59vbr4+Dc1tXRxcC7ubSqoJuak5CKiIN+enlyb21lXVFEQj09NzAnJiQdGBUPDvzx8O/s6Obl397OzsvIyMXEwcG/vbiysbGuqqeknZSTkI2Gf3ZraGViX15YWFVTUk9OS0pEQUA8MzAtLSomHx4bGhkXCwfApKCAdGdjXlxaWTY0MyEUFBKNTPVmAAAFHElEQVR42uzX105UYRSG4YUyiCgqKvbeK3ZFsaCCXUHE3kvU2FvU2EussfcSY2KiiSUDc4fuTcl/xr8/ZuZk7/e9g/UcfcuIiIiIiIiIiIiIiMhf7wFDPxlFKx1UdunBX6NoWGEHqp40GnmwXCWHlzz9YuTBck2cvWzdVyMPlmtSAJYy8mE5sJPXNhYYebBcO06tBMyP5do1v3aTkcPytedb7R8jh+Vr2sVVHyzxpaMXfEZbLNGltcqqHjba8LGWzNJyJdZUeHTws/GWvNJ61hTW5cSQkRMsWXUWqxWsX/HvJC2xTmK5dp4pfp0UsCywXL3633hrCShrLAd2653FvBxgufaV//xoMS5HWK4+5fdHW0zLJZYDu/wolmC5xXIVHlocv6GfcyxX/IZ+HrHiN/TziuWGvsWiLLCULBaBBVZLYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYOW1erCitfn2gqnNYPlruLdwenMQWJ7+rV40I3QCy9PWx1cOhkZgeRq35uqRbYEPWD6o9d+P9wxxwOq41IblcyaHMGB1XNHLFfO6hShgeXpz/WwLFFie6m+e3x1igBVlmgeB5anhV+s0B8tX2zQHK0rB8WCBZQYWWGCBBRZYYIEFFlhggQUWWGCBBRZYYIGVZKyKgcNGmYEVCSuTyXTtO2jEGLCiYYX1mLV0bXewfFiu0mPVz1NgebEc2NzqF0Vg+bBc20/XvCoAy4flmnKupg4sL5Zr74UfdWB5sVz7K++8B8theauovPsZLIflbWYw9MGKiNU+9MGKlBv6YEWvFCwhsMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMD6325d7cQVhWEY/ocWn9JSoO5GhTqlSo2Wurt72qbu3lRwgruEQCCBE+6RHQ7YR7DWP8OeWSHvewnPwZcPLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLCmGtbhHclg+Vimkk4f2r4OLLEu8dS3Z2Gw7Ft1/OvjMFj2Xan48uAqWAqwY3s2g6Xo4r/dG8FSdKF/5wawFJ3/9e4WWIoaf7xJBsu+UJ139MHyszr6YKmOfncYLM3RB0sRWGDZBBZYYIEFFlhggQUWWGBFgpWQBZYd1PMFS9cIWMamP5lfvVq8wDJAPdy3eKV4gWVo1ufy5eIFlhigcv6PQoFlwLqbM9AmXmAZsDKyfzeLX6BY6U/FJjexMrJ/NolfoFjTZs9bkipWuYd143VKQ0iiyh5qy9xFK8Q6t7ASslKGfKiAsTZ9KlkmqtzB8q95DLDu7frbIXEvIiz/mscC6877Py3iRJFg+dc8cKz1b/vOiTPprdJk0hsH6tWRenEqpVTmx9JhkeCxruX31obEtRRQM+csvCTq9Fgz8g/WJImLWULl9RS3ijo9VvrW/SdSxdUsoG4XHD0rgRb3az45WDcLitRQ+uJ+zaPHuv6y6ExsRjbu1zw6rLUvvg+6ObKqgsfK3XbgZKKQESv3UWFVl5ARK+1+YWWnkBkrc2/ZZaGJG7vmZC7vQ3G7EBERERERERERERER0dRtBK5Q857p1uutAAAAAElFTkSuQmCC"
},
- "豆包": {
- "name": "豆包",
- "categoryId": 0,
- "url": "doubao.com",
- "icon": "https://lf-flow-web-cdn.doubao.com/obj/flow-doubao/doubao/chat/favicon.png"
+ "应用 API": {
+ "name": "应用 API",
+ "categoryId": 22,
+ "url": "open.feishu.cn",
+ "icon": "data:image/vnd.microsoft.icon;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAMAAABOo35HAAACFlBMVEUAAACp3f8yW7U8jf8zW7U/3vJA5P8AydMzW7VPlP955908jP8AydM8jf8yWrV5590yWrV45908jf8yW7UyW7UyW7V55909jf88jf8yW7UzW7V56N49jf80XLY+j/80XbY/lf8zZr5Ilf9GXbkA2+RCY70zZsx459x5590AydI8jP8AydN4590AydM8jf8AydM8jf88jf94590AyNM8jf8AydN5590AydM8jf8AyNIAytR5594zW7V56N09jf955948jf8AydMAytN65948jf956N56594AytMAy9Q9jv976d8AytYAy9Z66N566uE/jv8AztUAy9cAytV56OSD798A0NYAydM8jP95590yW7R45twAydN45909jP8yWrU8jf945t0AydIyWrV5590AydIyW7UzWrU8jf8AydIzW7UzWrV459145908jf8yW7UAydM8jP8AydM8jf9459155909jf8zW7UAydQAytMAydN56N0AydMzXLYAydM0W7UAydQ8jv8+jv8Ay9QzW7Y9jf965t176N80W7YAydN56N156d4yW7U0XLc+jv8+kP8Ay9VAj/81XbgAzNQ1XbY5XbZAkf9+7eQ7Yrp96+FDlv+F6emI/+4AydN55t15594AydM+jf8+jf8+jv8AydQ1W7QzW7UAydR75942XbUyWrR86OCA8uYAztqA8eN45twyWrQAyNI8jP8bed+KAAAArnRSTlMAAfb9+wUD8HMJ/fr59+jb1dTLt6eamY+Jg3xXTkhHOhIRDQsJBwX59vbr4+Dc1tXRxcC7ubSqoJuak5CKiIN+enlyb21lXVFEQj09NzAnJiQdGBUPDvzx8O/s6Obl397OzsvIyMXEwcG/vbiysbGuqqeknZSTkI2Gf3ZraGViX15YWFVTUk9OS0pEQUA8MzAtLSomHx4bGhkXCwfApKCAdGdjXlxaWTY0MyEUFBKNTPVmAAAFHElEQVR42uzX105UYRSG4YUyiCgqKvbeK3ZFsaCCXUHE3kvU2FvU2EussfcSY2KiiSUDc4fuTcl/xr8/ZuZk7/e9g/UcfcuIiIiIiIiIiIiIiMhf7wFDPxlFKx1UdunBX6NoWGEHqp40GnmwXCWHlzz9YuTBck2cvWzdVyMPlmtSAJYy8mE5sJPXNhYYebBcO06tBMyP5do1v3aTkcPytedb7R8jh+Vr2sVVHyzxpaMXfEZbLNGltcqqHjba8LGWzNJyJdZUeHTws/GWvNJ61hTW5cSQkRMsWXUWqxWsX/HvJC2xTmK5dp4pfp0UsCywXL3633hrCShrLAd2653FvBxgufaV//xoMS5HWK4+5fdHW0zLJZYDu/wolmC5xXIVHlocv6GfcyxX/IZ+HrHiN/TziuWGvsWiLLCULBaBBVZLYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYOW1erCitfn2gqnNYPlruLdwenMQWJ7+rV40I3QCy9PWx1cOhkZgeRq35uqRbYEPWD6o9d+P9wxxwOq41IblcyaHMGB1XNHLFfO6hShgeXpz/WwLFFie6m+e3x1igBVlmgeB5anhV+s0B8tX2zQHK0rB8WCBZQYWWGCBBRZYYIEFFlhggQUWWGCBBRZYYIGVZKyKgcNGmYEVCSuTyXTtO2jEGLCiYYX1mLV0bXewfFiu0mPVz1NgebEc2NzqF0Vg+bBc20/XvCoAy4flmnKupg4sL5Zr74UfdWB5sVz7K++8B8theauovPsZLIflbWYw9MGKiNU+9MGKlBv6YEWvFCwhsMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMD6325d7cQVhWEY/ocWn9JSoO5GhTqlSo2Wurt72qbu3lRwgruEQCCBE+6RHQ7YR7DWP8OeWSHvewnPwZcPLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLCmGtbhHclg+Vimkk4f2r4OLLEu8dS3Z2Gw7Ft1/OvjMFj2Xan48uAqWAqwY3s2g6Xo4r/dG8FSdKF/5wawFJ3/9e4WWIoaf7xJBsu+UJ139MHyszr6YKmOfncYLM3RB0sRWGDZBBZYYIEFFlhggQUWWGBFgpWQBZYd1PMFS9cIWMamP5lfvVq8wDJAPdy3eKV4gWVo1ufy5eIFlhigcv6PQoFlwLqbM9AmXmAZsDKyfzeLX6BY6U/FJjexMrJ/NolfoFjTZs9bkipWuYd143VKQ0iiyh5qy9xFK8Q6t7ASslKGfKiAsTZ9KlkmqtzB8q95DLDu7frbIXEvIiz/mscC6877Py3iRJFg+dc8cKz1b/vOiTPprdJk0hsH6tWRenEqpVTmx9JhkeCxruX31obEtRRQM+csvCTq9Fgz8g/WJImLWULl9RS3ijo9VvrW/SdSxdUsoG4XHD0rgRb3az45WDcLitRQ+uJ+zaPHuv6y6ExsRjbu1zw6rLUvvg+6ObKqgsfK3XbgZKKQESv3UWFVl5ARK+1+YWWnkBkrc2/ZZaGJG7vmZC7vQ3G7EBERERERERERERER0dRtBK5Q857p1uutAAAAAElFTkSuQmCC"
},
+ "账户 / 个人信息管理": {
+ "name": "账户 / 个人信息管理",
+ "categoryId": 22,
+ "url": "profile.snssdk.com",
+ "icon": "data:image/vnd.microsoft.icon;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAMAAABOo35HAAACFlBMVEUAAACp3f8yW7U8jf8zW7U/3vJA5P8AydMzW7VPlP955908jP8AydM8jf8yWrV5590yWrV45908jf8yW7UyW7UyW7V55909jf88jf8yW7UzW7V56N49jf80XLY+j/80XbY/lf8zZr5Ilf9GXbkA2+RCY70zZsx459x5590AydI8jP8AydN4590AydM8jf8AydM8jf88jf94590AyNM8jf8AydN5590AydM8jf8AyNIAytR5594zW7V56N09jf955948jf8AydMAytN65948jf956N56594AytMAy9Q9jv976d8AytYAy9Z66N566uE/jv8AztUAy9cAytV56OSD798A0NYAydM8jP95590yW7R45twAydN45909jP8yWrU8jf945t0AydIyWrV5590AydIyW7UzWrU8jf8AydIzW7UzWrV459145908jf8yW7UAydM8jP8AydM8jf9459155909jf8zW7UAydQAytMAydN56N0AydMzXLYAydM0W7UAydQ8jv8+jv8Ay9QzW7Y9jf965t176N80W7YAydN56N156d4yW7U0XLc+jv8+kP8Ay9VAj/81XbgAzNQ1XbY5XbZAkf9+7eQ7Yrp96+FDlv+F6emI/+4AydN55t15594AydM+jf8+jf8+jv8AydQ1W7QzW7UAydR75942XbUyWrR86OCA8uYAztqA8eN45twyWrQAyNI8jP8bed+KAAAArnRSTlMAAfb9+wUD8HMJ/fr59+jb1dTLt6eamY+Jg3xXTkhHOhIRDQsJBwX59vbr4+Dc1tXRxcC7ubSqoJuak5CKiIN+enlyb21lXVFEQj09NzAnJiQdGBUPDvzx8O/s6Obl397OzsvIyMXEwcG/vbiysbGuqqeknZSTkI2Gf3ZraGViX15YWFVTUk9OS0pEQUA8MzAtLSomHx4bGhkXCwfApKCAdGdjXlxaWTY0MyEUFBKNTPVmAAAFHElEQVR42uzX105UYRSG4YUyiCgqKvbeK3ZFsaCCXUHE3kvU2FvU2EussfcSY2KiiSUDc4fuTcl/xr8/ZuZk7/e9g/UcfcuIiIiIiIiIiIiIiMhf7wFDPxlFKx1UdunBX6NoWGEHqp40GnmwXCWHlzz9YuTBck2cvWzdVyMPlmtSAJYy8mE5sJPXNhYYebBcO06tBMyP5do1v3aTkcPytedb7R8jh+Vr2sVVHyzxpaMXfEZbLNGltcqqHjba8LGWzNJyJdZUeHTws/GWvNJ61hTW5cSQkRMsWXUWqxWsX/HvJC2xTmK5dp4pfp0UsCywXL3633hrCShrLAd2653FvBxgufaV//xoMS5HWK4+5fdHW0zLJZYDu/wolmC5xXIVHlocv6GfcyxX/IZ+HrHiN/TziuWGvsWiLLCULBaBBVZLYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYOW1erCitfn2gqnNYPlruLdwenMQWJ7+rV40I3QCy9PWx1cOhkZgeRq35uqRbYEPWD6o9d+P9wxxwOq41IblcyaHMGB1XNHLFfO6hShgeXpz/WwLFFie6m+e3x1igBVlmgeB5anhV+s0B8tX2zQHK0rB8WCBZQYWWGCBBRZYYIEFFlhggQUWWGCBBRZYYIGVZKyKgcNGmYEVCSuTyXTtO2jEGLCiYYX1mLV0bXewfFiu0mPVz1NgebEc2NzqF0Vg+bBc20/XvCoAy4flmnKupg4sL5Zr74UfdWB5sVz7K++8B8theauovPsZLIflbWYw9MGKiNU+9MGKlBv6YEWvFCwhsMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMD6325d7cQVhWEY/ocWn9JSoO5GhTqlSo2Wurt72qbu3lRwgruEQCCBE+6RHQ7YR7DWP8OeWSHvewnPwZcPLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLCmGtbhHclg+Vimkk4f2r4OLLEu8dS3Z2Gw7Ft1/OvjMFj2Xan48uAqWAqwY3s2g6Xo4r/dG8FSdKF/5wawFJ3/9e4WWIoaf7xJBsu+UJ139MHyszr6YKmOfncYLM3RB0sRWGDZBBZYYIEFFlhggQUWWGBFgpWQBZYd1PMFS9cIWMamP5lfvVq8wDJAPdy3eKV4gWVo1ufy5eIFlhigcv6PQoFlwLqbM9AmXmAZsDKyfzeLX6BY6U/FJjexMrJ/NolfoFjTZs9bkipWuYd143VKQ0iiyh5qy9xFK8Q6t7ASslKGfKiAsTZ9KlkmqtzB8q95DLDu7frbIXEvIiz/mscC6877Py3iRJFg+dc8cKz1b/vOiTPprdJk0hsH6tWRenEqpVTmx9JhkeCxruX31obEtRRQM+csvCTq9Fgz8g/WJImLWULl9RS3ijo9VvrW/SdSxdUsoG4XHD0rgRb3az45WDcLitRQ+uJ+zaPHuv6y6ExsRjbu1zw6rLUvvg+6ObKqgsfK3XbgZKKQESv3UWFVl5ARK+1+YWWnkBkrc2/ZZaGJG7vmZC7vQ3G7EBERERERERERERER0dRtBK5Q857p1uutAAAAAElFTkSuQmCC"
+ },
+ "内容索引 / 推荐数据下发": {
+ "name": "内容索引 / 推荐数据下发",
+ "categoryId": 22,
+ "url": "is.snssdk.com",
+ "icon": "data:image/vnd.microsoft.icon;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAMAAABOo35HAAACFlBMVEUAAACp3f8yW7U8jf8zW7U/3vJA5P8AydMzW7VPlP955908jP8AydM8jf8yWrV5590yWrV45908jf8yW7UyW7UyW7V55909jf88jf8yW7UzW7V56N49jf80XLY+j/80XbY/lf8zZr5Ilf9GXbkA2+RCY70zZsx459x5590AydI8jP8AydN4590AydM8jf8AydM8jf88jf94590AyNM8jf8AydN5590AydM8jf8AyNIAytR5594zW7V56N09jf955948jf8AydMAytN65948jf956N56594AytMAy9Q9jv976d8AytYAy9Z66N566uE/jv8AztUAy9cAytV56OSD798A0NYAydM8jP95590yW7R45twAydN45909jP8yWrU8jf945t0AydIyWrV5590AydIyW7UzWrU8jf8AydIzW7UzWrV459145908jf8yW7UAydM8jP8AydM8jf9459155909jf8zW7UAydQAytMAydN56N0AydMzXLYAydM0W7UAydQ8jv8+jv8Ay9QzW7Y9jf965t176N80W7YAydN56N156d4yW7U0XLc+jv8+kP8Ay9VAj/81XbgAzNQ1XbY5XbZAkf9+7eQ7Yrp96+FDlv+F6emI/+4AydN55t15594AydM+jf8+jf8+jv8AydQ1W7QzW7UAydR75942XbUyWrR86OCA8uYAztqA8eN45twyWrQAyNI8jP8bed+KAAAArnRSTlMAAfb9+wUD8HMJ/fr59+jb1dTLt6eamY+Jg3xXTkhHOhIRDQsJBwX59vbr4+Dc1tXRxcC7ubSqoJuak5CKiIN+enlyb21lXVFEQj09NzAnJiQdGBUPDvzx8O/s6Obl397OzsvIyMXEwcG/vbiysbGuqqeknZSTkI2Gf3ZraGViX15YWFVTUk9OS0pEQUA8MzAtLSomHx4bGhkXCwfApKCAdGdjXlxaWTY0MyEUFBKNTPVmAAAFHElEQVR42uzX105UYRSG4YUyiCgqKvbeK3ZFsaCCXUHE3kvU2FvU2EussfcSY2KiiSUDc4fuTcl/xr8/ZuZk7/e9g/UcfcuIiIiIiIiIiIiIiMhf7wFDPxlFKx1UdunBX6NoWGEHqp40GnmwXCWHlzz9YuTBck2cvWzdVyMPlmtSAJYy8mE5sJPXNhYYebBcO06tBMyP5do1v3aTkcPytedb7R8jh+Vr2sVVHyzxpaMXfEZbLNGltcqqHjba8LGWzNJyJdZUeHTws/GWvNJ61hTW5cSQkRMsWXUWqxWsX/HvJC2xTmK5dp4pfp0UsCywXL3633hrCShrLAd2653FvBxgufaV//xoMS5HWK4+5fdHW0zLJZYDu/wolmC5xXIVHlocv6GfcyxX/IZ+HrHiN/TziuWGvsWiLLCULBaBBVZLYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYOW1erCitfn2gqnNYPlruLdwenMQWJ7+rV40I3QCy9PWx1cOhkZgeRq35uqRbYEPWD6o9d+P9wxxwOq41IblcyaHMGB1XNHLFfO6hShgeXpz/WwLFFie6m+e3x1igBVlmgeB5anhV+s0B8tX2zQHK0rB8WCBZQYWWGCBBRZYYIEFFlhggQUWWGCBBRZYYIGVZKyKgcNGmYEVCSuTyXTtO2jEGLCiYYX1mLV0bXewfFiu0mPVz1NgebEc2NzqF0Vg+bBc20/XvCoAy4flmnKupg4sL5Zr74UfdWB5sVz7K++8B8theauovPsZLIflbWYw9MGKiNU+9MGKlBv6YEWvFCwhsMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMD6325d7cQVhWEY/ocWn9JSoO5GhTqlSo2Wurt72qbu3lRwgruEQCCBE+6RHQ7YR7DWP8OeWSHvewnPwZcPLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLCmGtbhHclg+Vimkk4f2r4OLLEu8dS3Z2Gw7Ft1/OvjMFj2Xan48uAqWAqwY3s2g6Xo4r/dG8FSdKF/5wawFJ3/9e4WWIoaf7xJBsu+UJ139MHyszr6YKmOfncYLM3RB0sRWGDZBBZYYIEFFlhggQUWWGBFgpWQBZYd1PMFS9cIWMamP5lfvVq8wDJAPdy3eKV4gWVo1ufy5eIFlhigcv6PQoFlwLqbM9AmXmAZsDKyfzeLX6BY6U/FJjexMrJ/NolfoFjTZs9bkipWuYd143VKQ0iiyh5qy9xFK8Q6t7ASslKGfKiAsTZ9KlkmqtzB8q95DLDu7frbIXEvIiz/mscC6877Py3iRJFg+dc8cKz1b/vOiTPprdJk0hsH6tWRenEqpVTmx9JhkeCxruX31obEtRRQM+csvCTq9Fgz8g/WJImLWULl9RS3ijo9VvrW/SdSxdUsoG4XHD0rgRb3az45WDcLitRQ+uJ+zaPHuv6y6ExsRjbu1zw6rLUvvg+6ObKqgsfK3XbgZKKQESv3UWFVl5ARK+1+YWWnkBkrc2/ZZaGJG7vmZC7vQ3G7EBERERERERERERER0dRtBK5Q857p1uutAAAAAElFTkSuQmCC"
+ }
+ },
"飞书": {
- "飞书":{
+ "飞书": {
"name": "飞书",
"categoryId": 7,
"url": "https://www.feishu.cn/",
"icon": "https://www.feishu.cn/favicon.ico"
}
},
- "飞书服务":{
+ "飞书服务": {
"name": "飞书CalDAV 协议服务器",
"categoryId": 22,
"url": "caldav.feishu.cn",
"icon": "https://www.feishu.cn/favicon.ico"
},
"CDN": {
- "字节静态资源":{
- "name": "字节跳动产品的官方文档、帮助中心、API 参考、静态资源",
- "categoryId": 2,
- "url": {
+ "字节静态资源": {
+ "name": "字节跳动产品的官方文档、帮助中心、API 参考、静态资源",
+ "categoryId": 2,
+ "url": {
"1": "bytednsdoc.com",
"2": "bytecdntp.com",
"3": "bytescm.com",
@@ -795,100 +1300,134 @@
"7": "statp.com",
"8": "feishu.cn"
},
- "icon": "data:image/vnd.microsoft.icon;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAMAAABOo35HAAACFlBMVEUAAACp3f8yW7U8jf8zW7U/3vJA5P8AydMzW7VPlP955908jP8AydM8jf8yWrV5590yWrV45908jf8yW7UyW7UyW7V55909jf88jf8yW7UzW7V56N49jf80XLY+j/80XbY/lf8zZr5Ilf9GXbkA2+RCY70zZsx459x5590AydI8jP8AydN4590AydM8jf8AydM8jf88jf94590AyNM8jf8AydN5590AydM8jf8AyNIAytR5594zW7V56N09jf955948jf8AydMAytN65948jf956N56594AytMAy9Q9jv976d8AytYAy9Z66N566uE/jv8AztUAy9cAytV56OSD798A0NYAydM8jP95590yW7R45twAydN45909jP8yWrU8jf945t0AydIyWrV5590AydIyW7UzWrU8jf8AydIzW7UzWrV459145908jf8yW7UAydM8jP8AydM8jf9459155909jf8zW7UAydQAytMAydN56N0AydMzXLYAydM0W7UAydQ8jv8+jv8Ay9QzW7Y9jf965t176N80W7YAydN56N156d4yW7U0XLc+jv8+kP8Ay9VAj/81XbgAzNQ1XbY5XbZAkf9+7eQ7Yrp96+FDlv+F6emI/+4AydN55t15594AydM+jf8+jf8+jv8AydQ1W7QzW7UAydR75942XbUyWrR86OCA8uYAztqA8eN45twyWrQAyNI8jP8bed+KAAAArnRSTlMAAfb9+wUD8HMJ/fr59+jb1dTLt6eamY+Jg3xXTkhHOhIRDQsJBwX59vbr4+Dc1tXRxcC7ubSqoJuak5CKiIN+enlyb21lXVFEQj09NzAnJiQdGBUPDvzx8O/s6Obl397OzsvIyMXEwcG/vbiysbGuqqeknZSTkI2Gf3ZraGViX15YWFVTUk9OS0pEQUA8MzAtLSomHx4bGhkXCwfApKCAdGdjXlxaWTY0MyEUFBKNTPVmAAAFHElEQVR42uzX105UYRSG4YUyiCgqKvbeK3ZFsaCCXUHE3kvU2FvU2EussfcSY2KiiSUDc4fuTcl/xr8/ZuZk7/e9g/UcfcuIiIiIiIiIiIiIiMhf7wFDPxlFKx1UdunBX6NoWGEHqp40GnmwXCWHlzz9YuTBck2cvWzdVyMPlmtSAJYy8mE5sJPXNhYYebBcO06tBMyP5do1v3aTkcPytedb7R8jh+Vr2sVVHyzxpaMXfEZbLNGltcqqHjba8LGWzNJyJdZUeHTws/GWvNJ61hTW5cSQkRMsWXUWqxWsX/HvJC2xTmK5dp4pfp0UsCywXL3633hrCShrLAd2653FvBxgufaV//xoMS5HWK4+5fdHW0zLJZYDu/wolmC5xXIVHlocv6GfcyxX/IZ+HrHiN/TziuWGvsWiLLCULBaBBVZLYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYOW1erCitfn2gqnNYPlruLdwenMQWJ7+rV40I3QCy9PWx1cOhkZgeRq35uqRbYEPWD6o9d+P9wxxwOq41IblcyaHMGB1XNHLFfO6hShgeXpz/WwLFFie6m+e3x1igBVlmgeB5anhV+s0B8tX2zQHK0rB8WCBZQYWWGCBBRZYYIEFFlhggQUWWGCBBRZYYIGVZKyKgcNGmYEVCSuTyXTtO2jEGLCiYYX1mLV0bXewfFiu0mPVz1NgebEc2NzqF0Vg+bBc20/XvCoAy4flmnKupg4sL5Zr74UfdWB5sVz7K++8B8theauovPsZLIflbWYw9MGKiNU+9MGKlBv6YEWvFCwhsMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMD6325d7cQVhWEY/ocWn9JSoO5GhTqlSo2Wurt72qbu3lRwgruEQCCBE+6RHQ7YR7DWP8OeWSHvewnPwZcPLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLCmGtbhHclg+Vimkk4f2r4OLLEu8dS3Z2Gw7Ft1/OvjMFj2Xan48uAqWAqwY3s2g6Xo4r/dG8FSdKF/5wawFJ3/9e4WWIoaf7xJBsu+UJ139MHyszr6YKmOfncYLM3RB0sRWGDZBBZYYIEFFlhggQUWWGBFgpWQBZYd1PMFS9cIWMamP5lfvVq8wDJAPdy3eKV4gWVo1ufy5eIFlhigcv6PQoFlwLqbM9AmXmAZsDKyfzeLX6BY6U/FJjexMrJ/NolfoFjTZs9bkipWuYd143VKQ0iiyh5qy9xFK8Q6t7ASslKGfKiAsTZ9KlkmqtzB8q95DLDu7frbIXEvIiz/mscC6877Py3iRJFg+dc8cKz1b/vOiTPprdJk0hsH6tWRenEqpVTmx9JhkeCxruX31obEtRRQM+csvCTq9Fgz8g/WJImLWULl9RS3ijo9VvrW/SdSxdUsoG4XHD0rgRb3az45WDcLitRQ+uJ+zaPHuv6y6ExsRjbu1zw6rLUvvg+6ObKqgsfK3XbgZKKQESv3UWFVl5ARK+1+YWWnkBkrc2/ZZaGJG7vmZC7vQ3G7EBERERERERERERER0dRtBK5Q857p1uutAAAAAElFTkSuQmCC"
+ "icon": "data:image/vnd.microsoft.icon;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAMAAABOo35HAAACFlBMVEUAAACp3f8yW7U8jf8zW7U/3vJA5P8AydMzW7VPlP955908jP8AydM8jf8yWrV5590yWrV45908jf8yW7UyW7UyW7V55909jf88jf8yW7UzW7V56N49jf80XLY+j/80XbY/lf8zZr5Ilf9GXbkA2+RCY70zZsx459x5590AydI8jP8AydN4590AydM8jf8AydM8jf88jf94590AyNM8jf8AydN5590AydM8jf8AyNIAytR5594zW7V56N09jf955948jf8AydMAytN65948jf956N56594AytMAy9Q9jv976d8AytYAy9Z66N566uE/jv8AztUAy9cAytV56OSD798A0NYAydM8jP95590yW7R45twAydN45909jP8yWrU8jf945t0AydIyWrV5590AydIyW7UzWrU8jf8AydIzW7UzWrV459145908jf8yW7UAydM8jP8AydM8jf9459155909jf8zW7UAydQAytMAydN56N0AydMzXLYAydM0W7UAydQ8jv8+jv8Ay9QzW7Y9jf965t176N80W7YAydN56N156d4yW7U0XLc+jv8+kP8Ay9VAj/81XbgAzNQ1XbY5XbZAkf9+7eQ7Yrp96+FDlv+F6emI/+4AydN55t15594AydM+jf8+jf8+jv8AydQ1W7QzW7UAydR75942XbUyWrR86OCA8uYAztqA8eN45twyWrQAyNI8jP8bed+KAAAArnRSTlMAAfb9+wUD8HMJ/fr59+jb1dTLt6eamY+Jg3xXTkhHOhIRDQsJBwX59vbr4+Dc1tXRxcC7ubSqoJuak5CKiIN+enlyb21lXVFEQj09NzAnJiQdGBUPDvzx8O/s6Obl397OzsvIyMXEwcG/vbiysbGuqqeknZSTkI2Gf3ZraGViX15YWFVTUk9OS0pEQUA8MzAtLSomHx4bGhkXCwfApKCAdGdjXlxaWTY0MyEUFBKNTPVmAAAFHElEQVR42uzX105UYRSG4YUyiCgqKvbeK3ZFsaCCXUHE3kvU2FvU2EussfcSY2KiiSUDc4fuTcl/xr8/ZuZk7/e9g/UcfcuIiIiIiIiIiIiIiMhf7wFDPxlFKx1UdunBX6NoWGEHqp40GnmwXCWHlzz9YuTBck2cvWzdVyMPlmtSAJYy8mE5sJPXNhYYebBcO06tBMyP5do1v3aTkcPytedb7R8jh+Vr2sVVHyzxpaMXfEZbLNGltcqqHjba8LGWzNJyJdZUeHTws/GWvNJ61hTW5cSQkRMsWXUWqxWsX/HvJC2xTmK5dp4pfp0UsCywXL3633hrCShrLAd2653FvBxgufaV//xoMS5HWK4+5fdHW0zLJZYDu/wolmC5xXIVHlocv6GfcyxX/IZ+HrHiN/TziuWGvsWiLLCULBaBBVZLYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYOW1erCitfn2gqnNYPlruLdwenMQWJ7+rV40I3QCy9PWx1cOhkZgeRq35uqRbYEPWD6o9d+P9wxxwOq41IblcyaHMGB1XNHLFfO6hShgeXpz/WwLFFie6m+e3x1igBVlmgeB5anhV+s0B8tX2zQHK0rB8WCBZQYWWGCBBRZYYIEFFlhggQUWWGCBBRZYYIGVZKyKgcNGmYEVCSuTyXTtO2jEGLCiYYX1mLV0bXewfFiu0mPVz1NgebEc2NzqF0Vg+bBc20/XvCoAy4flmnKupg4sL5Zr74UfdWB5sVz7K++8B8theauovPsZLIflbWYw9MGKiNU+9MGKlBv6YEWvFCwhsMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMD6325d7cQVhWEY/ocWn9JSoO5GhTqlSo2Wurt72qbu3lRwgruEQCCBE+6RHQ7YR7DWP8OeWSHvewnPwZcPLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLCmGtbhHclg+Vimkk4f2r4OLLEu8dS3Z2Gw7Ft1/OvjMFj2Xan48uAqWAqwY3s2g6Xo4r/dG8FSdKF/5wawFJ3/9e4WWIoaf7xJBsu+UJ139MHyszr6YKmOfncYLM3RB0sRWGDZBBZYYIEFFlhggQUWWGBFgpWQBZYd1PMFS9cIWMamP5lfvVq8wDJAPdy3eKV4gWVo1ufy5eIFlhigcv6PQoFlwLqbM9AmXmAZsDKyfzeLX6BY6U/FJjexMrJ/NolfoFjTZs9bkipWuYd143VKQ0iiyh5qy9xFK8Q6t7ASslKGfKiAsTZ9KlkmqtzB8q95DLDu7frbIXEvIiz/mscC6877Py3iRJFg+dc8cKz1b/vOiTPprdJk0hsH6tWRenEqpVTmx9JhkeCxruX31obEtRRQM+csvCTq9Fgz8g/WJImLWULl9RS3ijo9VvrW/SdSxdUsoG4XHD0rgRb3az45WDcLitRQ+uJ+zaPHuv6y6ExsRjbu1zw6rLUvvg+6ObKqgsfK3XbgZKKQESv3UWFVl5ARK+1+YWWnkBkrc2/ZZaGJG7vmZC7vQ3G7EBERERERERERERER0dRtBK5Q857p1uutAAAAAElFTkSuQmCC"
},
- "今日头条静态资源":{
- "name": "今日头条静态资源",
- "categoryId": 2,
- "url": {
+ "字节跳动火山引擎权威 DNS 与智能调度服务": {
+ "name": "字节跳动火山引擎权威 DNS 与智能调度服务",
+ "categoryId": 21,
+ "url": "bytedns3.com",
+ "icon": "https://lf3-appstore-sign.bytedance.net/obj/volcengine-public/volcengine/favicon.ico"
+ },
+ "今日头条静态资源": {
+ "name": "今日头条静态资源",
+ "categoryId": 2,
+ "url": {
"1": "toutiaoimg.com",
"2": "toutiaostatic.com",
"3": "toutiaovod.com",
"4": "toutiaocdn.com",
"5": "pglstatp-toutiao.com"
},
- "icon": "https://www.toutiao.com/favicon.ico"
+ "icon": "https://www.toutiao.com/favicon.ico"
},
- "飞书CDN":{
- "name": "飞书CDN",
- "categoryId": 2,
- "url": {
+ "飞书CDN": {
+ "name": "飞书CDN",
+ "categoryId": 2,
+ "url": {
"1": "feishucdn.com"
},
- "icon": "https://www.feishu.cn/favicon.ico"
+ "icon": "https://www.feishu.cn/favicon.ico"
}
},
- "API相关":{
+ "API相关": {
"name": "字节跳动API相关",
"categoryId": 2,
"url": {
- "1": "zijieapi.com"
+ "1": "zijieapi.com"
},
"icon": "data:image/vnd.microsoft.icon;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAMAAABOo35HAAACFlBMVEUAAACp3f8yW7U8jf8zW7U/3vJA5P8AydMzW7VPlP955908jP8AydM8jf8yWrV5590yWrV45908jf8yW7UyW7UyW7V55909jf88jf8yW7UzW7V56N49jf80XLY+j/80XbY/lf8zZr5Ilf9GXbkA2+RCY70zZsx459x5590AydI8jP8AydN4590AydM8jf8AydM8jf88jf94590AyNM8jf8AydN5590AydM8jf8AyNIAytR5594zW7V56N09jf955948jf8AydMAytN65948jf956N56594AytMAy9Q9jv976d8AytYAy9Z66N566uE/jv8AztUAy9cAytV56OSD798A0NYAydM8jP95590yW7R45twAydN45909jP8yWrU8jf945t0AydIyWrV5590AydIyW7UzWrU8jf8AydIzW7UzWrV459145908jf8yW7UAydM8jP8AydM8jf9459155909jf8zW7UAydQAytMAydN56N0AydMzXLYAydM0W7UAydQ8jv8+jv8Ay9QzW7Y9jf965t176N80W7YAydN56N156d4yW7U0XLc+jv8+kP8Ay9VAj/81XbgAzNQ1XbY5XbZAkf9+7eQ7Yrp96+FDlv+F6emI/+4AydN55t15594AydM+jf8+jf8+jv8AydQ1W7QzW7UAydR75942XbUyWrR86OCA8uYAztqA8eN45twyWrQAyNI8jP8bed+KAAAArnRSTlMAAfb9+wUD8HMJ/fr59+jb1dTLt6eamY+Jg3xXTkhHOhIRDQsJBwX59vbr4+Dc1tXRxcC7ubSqoJuak5CKiIN+enlyb21lXVFEQj09NzAnJiQdGBUPDvzx8O/s6Obl397OzsvIyMXEwcG/vbiysbGuqqeknZSTkI2Gf3ZraGViX15YWFVTUk9OS0pEQUA8MzAtLSomHx4bGhkXCwfApKCAdGdjXlxaWTY0MyEUFBKNTPVmAAAFHElEQVR42uzX105UYRSG4YUyiCgqKvbeK3ZFsaCCXUHE3kvU2FvU2EussfcSY2KiiSUDc4fuTcl/xr8/ZuZk7/e9g/UcfcuIiIiIiIiIiIiIiMhf7wFDPxlFKx1UdunBX6NoWGEHqp40GnmwXCWHlzz9YuTBck2cvWzdVyMPlmtSAJYy8mE5sJPXNhYYebBcO06tBMyP5do1v3aTkcPytedb7R8jh+Vr2sVVHyzxpaMXfEZbLNGltcqqHjba8LGWzNJyJdZUeHTws/GWvNJ61hTW5cSQkRMsWXUWqxWsX/HvJC2xTmK5dp4pfp0UsCywXL3633hrCShrLAd2653FvBxgufaV//xoMS5HWK4+5fdHW0zLJZYDu/wolmC5xXIVHlocv6GfcyxX/IZ+HrHiN/TziuWGvsWiLLCULBaBBVZLYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYOW1erCitfn2gqnNYPlruLdwenMQWJ7+rV40I3QCy9PWx1cOhkZgeRq35uqRbYEPWD6o9d+P9wxxwOq41IblcyaHMGB1XNHLFfO6hShgeXpz/WwLFFie6m+e3x1igBVlmgeB5anhV+s0B8tX2zQHK0rB8WCBZQYWWGCBBRZYYIEFFlhggQUWWGCBBRZYYIGVZKyKgcNGmYEVCSuTyXTtO2jEGLCiYYX1mLV0bXewfFiu0mPVz1NgebEc2NzqF0Vg+bBc20/XvCoAy4flmnKupg4sL5Zr74UfdWB5sVz7K++8B8theauovPsZLIflbWYw9MGKiNU+9MGKlBv6YEWvFCwhsMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMD6325d7cQVhWEY/ocWn9JSoO5GhTqlSo2Wurt72qbu3lRwgruEQCCBE+6RHQ7YR7DWP8OeWSHvewnPwZcPLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLCmGtbhHclg+Vimkk4f2r4OLLEu8dS3Z2Gw7Ft1/OvjMFj2Xan48uAqWAqwY3s2g6Xo4r/dG8FSdKF/5wawFJ3/9e4WWIoaf7xJBsu+UJ139MHyszr6YKmOfncYLM3RB0sRWGDZBBZYYIEFFlhggQUWWGBFgpWQBZYd1PMFS9cIWMamP5lfvVq8wDJAPdy3eKV4gWVo1ufy5eIFlhigcv6PQoFlwLqbM9AmXmAZsDKyfzeLX6BY6U/FJjexMrJ/NolfoFjTZs9bkipWuYd143VKQ0iiyh5qy9xFK8Q6t7ASslKGfKiAsTZ9KlkmqtzB8q95DLDu7frbIXEvIiz/mscC6877Py3iRJFg+dc8cKz1b/vOiTPprdJk0hsH6tWRenEqpVTmx9JhkeCxruX31obEtRRQM+csvCTq9Fgz8g/WJImLWULl9RS3ijo9VvrW/SdSxdUsoG4XHD0rgRb3az45WDcLitRQ+uJ+zaPHuv6y6ExsRjbu1zw6rLUvvg+6ObKqgsfK3XbgZKKQESv3UWFVl5ARK+1+YWWnkBkrc2/ZZaGJG7vmZC7vQ3G7EBERERERERERERER0dRtBK5Q857p1uutAAAAAElFTkSuQmCC"
- },
- "穿山甲广告SDK":{
+ },
+ "穿山甲广告SDK": {
"name": "穿山甲广告 SDK(com.bytedance.msdk)",
"categoryId": 24,
"url": {
- "1":"mssdk.bytedance.com",
- "2":"mssdk-bu.bytedance.com",
- "3":"mssdk-tos.bytedance.com",
- "4":"mssdk.pangle.cn",
- "5":"mssdk.pangleglobal.com"
- },
+ "1": "mssdk.bytedance.com",
+ "2": "mssdk-bu.bytedance.com",
+ "3": "mssdk-tos.bytedance.com",
+ "4": "mssdk.pangle.cn",
+ "5": "mssdk.pangleglobal.com",
+ "6": "pangolin.snssdk.com"
+ },
"icon": "https://p3-pangle-empower.byteimg.com/obj/tos-cn-i-742ihxn9bs/ad/pangle/homepage/assets/favicon.ico",
"company": "北京巨量引擎网络技术有限公司"
},
- "穿山甲广告 API 接口":{
+ "穿山甲广告 API 接口": {
"name": "穿山甲广告 API 接口",
"categoryId": 24,
"url": {
- "1":"api.pangle.cn"
- },
+ "1": "api.pangle.cn"
+ },
"icon": "https://p3-pangle-empower.byteimg.com/obj/tos-cn-i-742ihxn9bs/ad/pangle/homepage/assets/favicon.ico",
"company": "北京巨量引擎网络技术有限公司"
},
- "穿山甲广告日志上报":{
+ "穿山甲广告日志上报": {
"name": "穿山甲广告日志上报",
"categoryId": 24,
"url": {
- "1":"log.pangle.cn"
- },
+ "1": "log.pangle.cn"
+ },
"icon": "https://p3-pangle-empower.byteimg.com/obj/tos-cn-i-742ihxn9bs/ad/pangle/homepage/assets/favicon.ico",
"company": "北京巨量引擎网络技术有限公司"
},
- "穿山甲广告素材 CDN 分发":{
+ "穿山甲广告素材 CDN 分发": {
"name": "穿山甲广告素材 CDN 分发",
"categoryId": 24,
"url": {
- "1":"cdn.pangle.cn"
- },
+ "1": "cdn.pangle.cn"
+ },
"icon": "https://p3-pangle-empower.byteimg.com/obj/tos-cn-i-742ihxn9bs/ad/pangle/homepage/assets/favicon.ico",
"company": "北京巨量引擎网络技术有限公司"
},
- "穿山甲海外版 SDK 服务":{
+ "穿山甲海外版 SDK 服务": {
"name": "穿山甲海外版 SDK 服务",
"categoryId": 24,
"url": {
- "1":"mssdk.pangleglobal.com"
- },
+ "1": "mssdk.pangleglobal.com"
+ },
"icon": "https://p3-pangle-empower.byteimg.com/obj/tos-cn-i-742ihxn9bs/ad/pangle/homepage/assets/favicon.ico",
"company": "字节跳动有限公司"
},
- "穿山甲":{
+ "穿山甲": {
"name": "穿山甲",
"categoryId": 0,
"url": {
- "1":"https://www.csjplatform.com/",
- "2":"https://csjplatform.com/"
- },
+ "1": "https://www.csjplatform.com/",
+ "2": "https://csjplatform.com/"
+ },
"icon": "https://p3-pangle-empower.byteimg.com/obj/tos-cn-i-742ihxn9bs/ad/pangle/homepage/assets/favicon.ico",
- "company":"北京巨量引擎网络技术有限公司"
+ "company": "北京巨量引擎网络技术有限公司"
},
- "豆包":{
- "CDN":{
- "豆包CDN":{
- "name": "豆包 CDN",
+ "字节跳动应用商店签名校验服务": {
+ "name": "字节跳动应用商店签名校验服务",
+ "categoryId": 21,
+ "url": "lf3-appstore-sign.bytedance.net",
+ "icon": "data:image/vnd.microsoft.icon;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAMAAABOo35HAAACFlBMVEUAAACp3f8yW7U8jf8zW7U/3vJA5P8AydMzW7VPlP955908jP8AydM8jf8yWrV5590yWrV45908jf8yW7UyW7UyW7V55909jf88jf8yW7UzW7V56N49jf80XLY+j/80XbY/lf8zZr5Ilf9GXbkA2+RCY70zZsx459x5590AydI8jP8AydN4590AydM8jf8AydM8jf88jf94590AyNM8jf8AydN5590AydM8jf8AyNIAytR5594zW7V56N09jf955948jf8AydMAytN65948jf956N56594AytMAy9Q9jv976d8AytYAy9Z66N566uE/jv8AztUAy9cAytV56OSD798A0NYAydM8jP95590yW7R45twAydN45909jP8yWrU8jf945t0AydIyWrV5590AydIyW7UzWrU8jf8AydIzW7UzWrV459145908jf8yW7UAydM8jP8AydM8jf9459155909jf8zW7UAydQAytMAydN56N0AydMzXLYAydM0W7UAydQ8jv8+jv8Ay9QzW7Y9jf965t176N80W7YAydN56N156d4yW7U0XLc+jv8+kP8Ay9VAj/81XbgAzNQ1XbY5XbZAkf9+7eQ7Yrp96+FDlv+F6emI/+4AydN55t15594AydM+jf8+jf8+jv8AydQ1W7QzW7UAydR75942XbUyWrR86OCA8uYAztqA8eN45twyWrQAyNI8jP8bed+KAAAArnRSTlMAAfb9+wUD8HMJ/fr59+jb1dTLt6eamY+Jg3xXTkhHOhIRDQsJBwX59vbr4+Dc1tXRxcC7ubSqoJuak5CKiIN+enlyb21lXVFEQj09NzAnJiQdGBUPDvzx8O/s6Obl397OzsvIyMXEwcG/vbiysbGuqqeknZSTkI2Gf3ZraGViX15YWFVTUk9OS0pEQUA8MzAtLSomHx4bGhkXCwfApKCAdGdjXlxaWTY0MyEUFBKNTPVmAAAFHElEQVR42uzX105UYRSG4YUyiCgqKvbeK3ZFsaCCXUHE3kvU2FvU2EussfcSY2KiiSUDc4fuTcl/xr8/ZuZk7/e9g/UcfcuIiIiIiIiIiIiIiMhf7wFDPxlFKx1UdunBX6NoWGEHqp40GnmwXCWHlzz9YuTBck2cvWzdVyMPlmtSAJYy8mE5sJPXNhYYebBcO06tBMyP5do1v3aTkcPytedb7R8jh+Vr2sVVHyzxpaMXfEZbLNGltcqqHjba8LGWzNJyJdZUeHTws/GWvNJ61hTW5cSQkRMsWXUWqxWsX/HvJC2xTmK5dp4pfp0UsCywXL3633hrCShrLAd2653FvBxgufaV//xoMS5HWK4+5fdHW0zLJZYDu/wolmC5xXIVHlocv6GfcyxX/IZ+HrHiN/TziuWGvsWiLLCULBaBBVZLYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYOW1erCitfn2gqnNYPlruLdwenMQWJ7+rV40I3QCy9PWx1cOhkZgeRq35uqRbYEPWD6o9d+P9wxxwOq41IblcyaHMGB1XNHLFfO6hShgeXpz/WwLFFie6m+e3x1igBVlmgeB5anhV+s0B8tX2zQHK0rB8WCBZQYWWGCBBRZYYIEFFlhggQUWWGCBBRZYYIGVZKyKgcNGmYEVCSuTyXTtO2jEGLCiYYX1mLV0bXewfFiu0mPVz1NgebEc2NzqF0Vg+bBc20/XvCoAy4flmnKupg4sL5Zr74UfdWB5sVz7K++8B8theauovPsZLIflbWYw9MGKiNU+9MGKlBv6YEWvFCwhsMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMD6325d7cQVhWEY/ocWn9JSoO5GhTqlSo2Wurt72qbu3lRwgruEQCCBE+6RHQ7YR7DWP8OeWSHvewnPwZcPLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLCmGtbhHclg+Vimkk4f2r4OLLEu8dS3Z2Gw7Ft1/OvjMFj2Xan48uAqWAqwY3s2g6Xo4r/dG8FSdKF/5wawFJ3/9e4WWIoaf7xJBsu+UJ139MHyszr6YKmOfncYLM3RB0sRWGDZBBZYYIEFFlhggQUWWGBFgpWQBZYd1PMFS9cIWMamP5lfvVq8wDJAPdy3eKV4gWVo1ufy5eIFlhigcv6PQoFlwLqbM9AmXmAZsDKyfzeLX6BY6U/FJjexMrJ/NolfoFjTZs9bkipWuYd143VKQ0iiyh5qy9xFK8Q6t7ASslKGfKiAsTZ9KlkmqtzB8q95DLDu7frbIXEvIiz/mscC6877Py3iRJFg+dc8cKz1b/vOiTPprdJk0hsH6tWRenEqpVTmx9JhkeCxruX31obEtRRQM+csvCTq9Fgz8g/WJImLWULl9RS3ijo9VvrW/SdSxdUsoG4XHD0rgRb3az45WDcLitRQ+uJ+zaPHuv6y6ExsRjbu1zw6rLUvvg+6ObKqgsfK3XbgZKKQESv3UWFVl5ARK+1+YWWnkBkrc2/ZZaGJG7vmZC7vQ3G7EBERERERERERERER0dRtBK5Q857p1uutAAAAAElFTkSuQmCC"
+ },
+ "字节跳动企业核心域名": {
+ "name": "字节跳动企业核心域名",
+ "categoryId": 2,
+ "url": "bytedance.net",
+ "icon": "data:image/vnd.microsoft.icon;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAMAAABOo35HAAACFlBMVEUAAACp3f8yW7U8jf8zW7U/3vJA5P8AydMzW7VPlP955908jP8AydM8jf8yWrV5590yWrV45908jf8yW7UyW7UyW7V55909jf88jf8yW7UzW7V56N49jf80XLY+j/80XbY/lf8zZr5Ilf9GXbkA2+RCY70zZsx459x5590AydI8jP8AydN4590AydM8jf8AydM8jf88jf94590AyNM8jf8AydN5590AydM8jf8AyNIAytR5594zW7V56N09jf955948jf8AydMAytN65948jf956N56594AytMAy9Q9jv976d8AytYAy9Z66N566uE/jv8AztUAy9cAytV56OSD798A0NYAydM8jP95590yW7R45twAydN45909jP8yWrU8jf945t0AydIyWrV5590AydIyW7UzWrU8jf8AydIzW7UzWrV459145908jf8yW7UAydM8jP8AydM8jf9459155909jf8zW7UAydQAytMAydN56N0AydMzXLYAydM0W7UAydQ8jv8+jv8Ay9QzW7Y9jf965t176N80W7YAydN56N156d4yW7U0XLc+jv8+kP8Ay9VAj/81XbgAzNQ1XbY5XbZAkf9+7eQ7Yrp96+FDlv+F6emI/+4AydN55t15594AydM+jf8+jf8+jv8AydQ1W7QzW7UAydR75942XbUyWrR86OCA8uYAztqA8eN45twyWrQAyNI8jP8bed+KAAAArnRSTlMAAfb9+wUD8HMJ/fr59+jb1dTLt6eamY+Jg3xXTkhHOhIRDQsJBwX59vbr4+Dc1tXRxcC7ubSqoJuak5CKiIN+enlyb21lXVFEQj09NzAnJiQdGBUPDvzx8O/s6Obl397OzsvIyMXEwcG/vbiysbGuqqeknZSTkI2Gf3ZraGViX15YWFVTUk9OS0pEQUA8MzAtLSomHx4bGhkXCwfApKCAdGdjXlxaWTY0MyEUFBKNTPVmAAAFHElEQVR42uzX105UYRSG4YUyiCgqKvbeK3ZFsaCCXUHE3kvU2FvU2EussfcSY2KiiSUDc4fuTcl/xr8/ZuZk7/e9g/UcfcuIiIiIiIiIiIiIiMhf7wFDPxlFKx1UdunBX6NoWGEHqp40GnmwXCWHlzz9YuTBck2cvWzdVyMPlmtSAJYy8mE5sJPXNhYYebBcO06tBMyP5do1v3aTkcPytedb7R8jh+Vr2sVVHyzxpaMXfEZbLNGltcqqHjba8LGWzNJyJdZUeHTws/GWvNJ61hTW5cSQkRMsWXUWqxWsX/HvJC2xTmK5dp4pfp0UsCywXL3633hrCShrLAd2653FvBxgufaV//xoMS5HWK4+5fdHW0zLJZYDu/wolmC5xXIVHlocv6GfcyxX/IZ+HrHiN/TziuWGvsWiLLCULBaBBVZLYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYOW1erCitfn2gqnNYPlruLdwenMQWJ7+rV40I3QCy9PWx1cOhkZgeRq35uqRbYEPWD6o9d+P9wxxwOq41IblcyaHMGB1XNHLFfO6hShgeXpz/WwLFFie6m+e3x1igBVlmgeB5anhV+s0B8tX2zQHK0rB8WCBZQYWWGCBBRZYYIEFFlhggQUWWGCBBRZYYIGVZKyKgcNGmYEVCSuTyXTtO2jEGLCiYYX1mLV0bXewfFiu0mPVz1NgebEc2NzqF0Vg+bBc20/XvCoAy4flmnKupg4sL5Zr74UfdWB5sVz7K++8B8theauovPsZLIflbWYw9MGKiNU+9MGKlBv6YEWvFCwhsMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMD6325d7cQVhWEY/ocWn9JSoO5GhTqlSo2Wurt72qbu3lRwgruEQCCBE+6RHQ7YR7DWP8OeWSHvewnPwZcPLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLCmGtbhHclg+Vimkk4f2r4OLLEu8dS3Z2Gw7Ft1/OvjMFj2Xan48uAqWAqwY3s2g6Xo4r/dG8FSdKF/5wawFJ3/9e4WWIoaf7xJBsu+UJ139MHyszr6YKmOfncYLM3RB0sRWGDZBBZYYIEFFlhggQUWWGBFgpWQBZYd1PMFS9cIWMamP5lfvVq8wDJAPdy3eKV4gWVo1ufy5eIFlhigcv6PQoFlwLqbM9AmXmAZsDKyfzeLX6BY6U/FJjexMrJ/NolfoFjTZs9bkipWuYd143VKQ0iiyh5qy9xFK8Q6t7ASslKGfKiAsTZ9KlkmqtzB8q95DLDu7frbIXEvIiz/mscC6877Py3iRJFg+dc8cKz1b/vOiTPprdJk0hsH6tWRenEqpVTmx9JhkeCxruX31obEtRRQM+csvCTq9Fgz8g/WJImLWULl9RS3ijo9VvrW/SdSxdUsoG4XHD0rgRb3az45WDcLitRQ+uJ+zaPHuv6y6ExsRjbu1zw6rLUvvg+6ObKqgsfK3XbgZKKQESv3UWFVl5ARK+1+YWWnkBkrc2/ZZaGJG7vmZC7vQ3G7EBERERERERERERER0dRtBK5Q857p1uutAAAAAElFTkSuQmCC"
+ },
+ "字节跳动 A/B 测试虚拟机调度服务": {
+ "name": "字节跳动 A/B 测试虚拟机调度服务",
+ "categoryId": 18,
+ "url": "abtestvm.bytedance.com",
+ "icon": "data:image/vnd.microsoft.icon;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAMAAABOo35HAAACFlBMVEUAAACp3f8yW7U8jf8zW7U/3vJA5P8AydMzW7VPlP955908jP8AydM8jf8yWrV5590yWrV45908jf8yW7UyW7UyW7V55909jf88jf8yW7UzW7V56N49jf80XLY+j/80XbY/lf8zZr5Ilf9GXbkA2+RCY70zZsx459x5590AydI8jP8AydN4590AydM8jf8AydM8jf88jf94590AyNM8jf8AydN5590AydM8jf8AyNIAytR5594zW7V56N09jf955948jf8AydMAytN65948jf956N56594AytMAy9Q9jv976d8AytYAy9Z66N566uE/jv8AztUAy9cAytV56OSD798A0NYAydM8jP95590yW7R45twAydN45909jP8yWrU8jf945t0AydIyWrV5590AydIyW7UzWrU8jf8AydIzW7UzWrV459145908jf8yW7UAydM8jP8AydM8jf9459155909jf8zW7UAydQAytMAydN56N0AydMzXLYAydM0W7UAydQ8jv8+jv8Ay9QzW7Y9jf965t176N80W7YAydN56N156d4yW7U0XLc+jv8+kP8Ay9VAj/81XbgAzNQ1XbY5XbZAkf9+7eQ7Yrp96+FDlv+F6emI/+4AydN55t15594AydM+jf8+jf8+jv8AydQ1W7QzW7UAydR75942XbUyWrR86OCA8uYAztqA8eN45twyWrQAyNI8jP8bed+KAAAArnRSTlMAAfb9+wUD8HMJ/fr59+jb1dTLt6eamY+Jg3xXTkhHOhIRDQsJBwX59vbr4+Dc1tXRxcC7ubSqoJuak5CKiIN+enlyb21lXVFEQj09NzAnJiQdGBUPDvzx8O/s6Obl397OzsvIyMXEwcG/vbiysbGuqqeknZSTkI2Gf3ZraGViX15YWFVTUk9OS0pEQUA8MzAtLSomHx4bGhkXCwfApKCAdGdjXlxaWTY0MyEUFBKNTPVmAAAFHElEQVR42uzX105UYRSG4YUyiCgqKvbeK3ZFsaCCXUHE3kvU2FvU2EussfcSY2KiiSUDc4fuTcl/xr8/ZuZk7/e9g/UcfcuIiIiIiIiIiIiIiMhf7wFDPxlFKx1UdunBX6NoWGEHqp40GnmwXCWHlzz9YuTBck2cvWzdVyMPlmtSAJYy8mE5sJPXNhYYebBcO06tBMyP5do1v3aTkcPytedb7R8jh+Vr2sVVHyzxpaMXfEZbLNGltcqqHjba8LGWzNJyJdZUeHTws/GWvNJ61hTW5cSQkRMsWXUWqxWsX/HvJC2xTmK5dp4pfp0UsCywXL3633hrCShrLAd2653FvBxgufaV//xoMS5HWK4+5fdHW0zLJZYDu/wolmC5xXIVHlocv6GfcyxX/IZ+HrHiN/TziuWGvsWiLLCULBaBBVZLYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYAmBJQSWEFhCYOW1erCitfn2gqnNYPlruLdwenMQWJ7+rV40I3QCy9PWx1cOhkZgeRq35uqRbYEPWD6o9d+P9wxxwOq41IblcyaHMGB1XNHLFfO6hShgeXpz/WwLFFie6m+e3x1igBVlmgeB5anhV+s0B8tX2zQHK0rB8WCBZQYWWGCBBRZYYIEFFlhggQUWWGCBBRZYYIGVZKyKgcNGmYEVCSuTyXTtO2jEGLCiYYX1mLV0bXewfFiu0mPVz1NgebEc2NzqF0Vg+bBc20/XvCoAy4flmnKupg4sL5Zr74UfdWB5sVz7K++8B8theauovPsZLIflbWYw9MGKiNU+9MGKlBv6YEWvFCwhsMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLDAAgsssMD6325d7cQVhWEY/ocWn9JSoO5GhTqlSo2Wurt72qbu3lRwgruEQCCBE+6RHQ7YR7DWP8OeWSHvewnPwZcPLLDAAgsssMACCyywwAILLLDAAgsssMACCyywwAILLLCmGtbhHclg+Vimkk4f2r4OLLEu8dS3Z2Gw7Ft1/OvjMFj2Xan48uAqWAqwY3s2g6Xo4r/dG8FSdKF/5wawFJ3/9e4WWIoaf7xJBsu+UJ139MHyszr6YKmOfncYLM3RB0sRWGDZBBZYYIEFFlhggQUWWGBFgpWQBZYd1PMFS9cIWMamP5lfvVq8wDJAPdy3eKV4gWVo1ufy5eIFlhigcv6PQoFlwLqbM9AmXmAZsDKyfzeLX6BY6U/FJjexMrJ/NolfoFjTZs9bkipWuYd143VKQ0iiyh5qy9xFK8Q6t7ASslKGfKiAsTZ9KlkmqtzB8q95DLDu7frbIXEvIiz/mscC6877Py3iRJFg+dc8cKz1b/vOiTPprdJk0hsH6tWRenEqpVTmx9JhkeCxruX31obEtRRQM+csvCTq9Fgz8g/WJImLWULl9RS3ijo9VvrW/SdSxdUsoG4XHD0rgRb3az45WDcLitRQ+uJ+zaPHuv6y6ExsRjbu1zw6rLUvvg+6ObKqgsfK3XbgZKKQESv3UWFVl5ARK+1+YWWnkBkrc2/ZZaGJG7vmZC7vQ3G7EBERERERERERERER0dRtBK5Q857p1uutAAAAAElFTkSuQmCC"
+ },
+ "豆包": {
+ "CDN": {
+ "豆包官网": {
+ "name": "豆包官网",
+ "categoryId": 0,
+ "url": {
+ "1": "doubao.com",
+ "2": "www.doubao.com"
+ },
+ "icon": "https://lf-flow-web-cdn.doubao.com/obj/flow-doubao/doubao/chat/favicon.png"
+ },
+ "豆包CDN": {
+ "name": "豆包CDN",
"categoryId": 2,
"url": {
"1": "cdnbuild.net",
@@ -897,88 +1436,108 @@
"icon": "https://p3-pangle-empower.byteimg.com/obj/tos-cn-i-742ihxn9bs/ad/pangle/homepage/assets/favicon.ico",
"company": "抖音视界有限公司"
},
- "内部服务负载均衡":{
+ "豆包AI优化服务": {
+ "name": "豆包AI优化服务",
+ "categoryId": 24,
+ "url": "opt.doubao.com",
+ "icon": "https://lf-flow-web-cdn.doubao.com/obj/flow-doubao/doubao/chat/favicon.png"
+ },
+ "豆包多端云同步核心服务": {
+ "name": "豆包多端云同步核心服务",
+ "categoryId": 22,
+ "url": {
+ "1": "mcs.doubao.com"
+ },
+ "icon": "https://lf-flow-web-cdn.doubao.com/obj/flow-doubao/doubao/chat/favicon.png"
+ },
+ "内部服务负载均衡": {
"name": "内部服务负载均衡",
"categoryId": 2,
"url": {
"1": "bytelb.net"
},
- "icon": "www.doubao.com/favicon.ico",
+ "icon": "https://lf-flow-web-cdn.doubao.com/obj/flow-doubao/doubao/chat/favicon.png",
"company": "抖音视界有限公司"
},
- "WebSocket 长连接,实时交互":{
+ "WebSocket 长连接,实时交互": {
"name": "WebSocket 长连接,实时交互",
"categoryId": 2,
"url": {
"1": "wss100-normal.doubao.com"
},
- "icon": "www.doubao.com/favicon.ico",
+ "icon": "https://lf-flow-web-cdn.doubao.com/obj/flow-doubao/doubao/chat/favicon.png",
"company": "抖音视界有限公司"
},
- "前端 JS/CSS/ 图片 / 安装包分发":{
+ "前端 JS/CSS/ 图片 / 安装包分发": {
"name": "前端 JS/CSS/ 图片 / 安装包分发",
"categoryId": 2,
"url": {
"1": "lf-flow-web-cdn.doubao.com"
},
- "icon": "www.doubao.com/favicon.ico",
+ "icon": "https://lf-flow-web-cdn.doubao.com/obj/flow-doubao/doubao/chat/favicon.png",
"company": "北京春田知韵科技有限公司"
}
- },
- "豆包":{
+ },
+ "豆包": {
"name": "豆包",
"categoryId": 0,
"url": {
- "1":"www.doubao.com"
+ "1": "www.doubao.com"
},
- "icon": "www.doubao.com/favicon.ico",
+ "icon": "https://lf-flow-web-cdn.doubao.com/obj/flow-doubao/doubao/chat/favicon.png",
"company": "北京春田知韵科技有限公司"
+ },
+ "豆包日志采集与格式化服务": {
+ "name": "豆包日志采集与格式化服务",
+ "categoryId": 22,
+ "url": "logifier.doubao.com",
+ "icon": "https://lf-flow-web-cdn.doubao.com/obj/flow-doubao/doubao/chat/favicon.png"
}
},
- "company": "字节跳动有限公司"
+ "company": "字节跳动有限公司"
},
"金山办公": {
"金山文档": {
"name": "金山文档",
"categoryId": 0,
"url": {
- "1":"kdocs.cn",
- "2":"www.kdocs.cn",
- "3":"365.kdocs.cn",
- "4":"account.kdocs.cn"
- },
+ "1": "kdocs.cn",
+ "2": "www.kdocs.cn",
+ "3": "365.kdocs.cn",
+ "4": "account.kdocs.cn"
+ },
"icon": "https://qn.cache.wpscdn.cn/kdocs/mobile/touch/apple-180.png"
},
"WPS Office": {
"name": "WPS Office",
"categoryId": 6,
"url": {
- "1":"wps.cn",
- "2":"www.wps.cn"
- },
+ "1": "wps.cn",
+ "2": "www.wps.cn"
+ },
"icon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAQ2SURBVHgB7Va9biRFEK7qWa9XAuSByMEhr0FIzvARkEBgEUHEPQG79wKcTyL3Li/gnxdgl5TgTAQSQmeJxEJCIoIjOc/JyemSW4tbe70z28XX1T0z+zO2U6RzWe3unq6un6+/rl6iW7mV111Y7tzZImMekVAcvkm+NqUnxZy1S0jsQz49PdTF3Q82ych3WNzM1dM/Msp+S4UviQ32RHDlBm5MIgJrz9Bvs6y9e4Ida3MOvRXGnxRTyb2HgM6I/13nZDCQb98/IcNNtQC19J8JXf46InWM5npjfM/qnJ1dtVGjSOLgKkQWnMz4y+clcmgx2TiWb955QENpFsBhT/r7SDhymZMwEnfKOjbiFcSSRuO+k7F7FFk4xsdIECZ6xtxg7HqeuHFYQ583lh59Dgvn0qZXmL/CviGy/3tM9gJOoYPmehcMcaR7vE03Vj9yyNJsxlQf4xgkLrNFUCnvEzUwGZUoRDjjiB7peCzr9Gm9hflOgRokfZHdnfw1HPi9+X/IMu8CpHsznIIN3SgbqzvoOiXAMgBgd/nJ84TmibGx+pgcCTeoS9HSY5hpFnwh6vEPp/cr9jidE3/M+Vfb4ycv7hsf5mifzOQlYPFwM87X2B2qlq62kWnRhW3SOaC8sExDHFUadSt3OFsecg+9csGorgbAfw4GVOMDPef8fCJpy0bcnLcFVI5oFYORbcMpaQBD6/o+HybJYvawAVs4Kgq2Hd96ObqmjHK8h8gGgYw+yuWoGoXzrEXnE2SfCQ0zN3dUqc7e2VAyB0LjdtBSWugWASgK7G7EFNONtGRzFgWdp7JNKYxlCBYd+h4fP0+wFi/oGmp7ZK1WI/jowleyEIAXCy7Yl8UxGGyIzCwKjvXGxj5AZxAR2ElXPorbVDPbs7qmExKRcLUTR75plZkAFIVIDvQY3CYlpW3nKBQZ6VqoGUb6oOwgBPa1bHkUVJcnXwUd9hwAUlPZVyAAGZPnQl6UXBDLsqtrDdvReV6UHFKRuxH2AeZwCGTc2MkymO9JnSfyDN768+4WAggo7IWKxeH8vpRP3mo5TpQVUYPrBSttrabujI1sy8dvfIixZ76x3gajRhzPZq/+qEIUxjR9igr3NlVKqChZtk6NxgC9e9A8AUWL0hn+r+QvDPQTytLP+HiU3IiAmj8KXCh5IEWR8mN9C5xB1a3RfnFz/NGtTL0d7v39vsr5lQH4lTq4YM+U6aVxCuQD3JOpe19zuoNQwALp8qtsE6plvSvdXLWgmRnZL1isJXoSXjLu81GZUaGrL14otVrQ0Eg607oLfugakS2c61L9qf5a8i+e6O+CzLw3b1R1a3XHhZWp1zHhXy7Xr/NhrlvkI73fB2Vh0vvcr8pIdR0Xpisp49reILWbFPjncUe+qK0g83vK5tGbD12xqJQR3pPG0hrq8xZQ+JF/yvp0K7fyf5f/AKzIO6StcaNfAAAAAElFTkSuQmCC"
},
"WPS CDN": {
"name": "WPS CDN",
"categoryId": 2,
"url": {
- "1":"wpsdns.com",
- "2":"kdocs-om.wpscdn.cn",
- "3":"qn.cache.wpscdn.cn",
- "4":"fe-static.wpscdn.cn",
- "5":"docer-ks3.wpscdn.cn",
- "6":"cloudcdn.wpscdn.cn",
- "7":"vasvip-pub.wpscdn.cn",
- "8":"ks3.wpsplus.wpscdn.cn",
- "9":"op-kdocs.wpscdn.cn",
- "10":"volcengine-cache-weboffice.wpscdn.cn",
- "11":"ac.wpscdn.cn",
- "12":"honeycomb-emergency.wpscdn.cn",
- "13":"res-honeycomb.wpscdn.cn",
- "14":"kflow.wpscdn.cn",
- "15":"personal-act.wpscdn.cn",
- "16":"official-package.wpscdn.cn"
- },
+ "1": "wpsdns.com",
+ "2": "kdocs-om.wpscdn.cn",
+ "3": "qn.cache.wpscdn.cn",
+ "4": "fe-static.wpscdn.cn",
+ "5": "docer-ks3.wpscdn.cn",
+ "6": "cloudcdn.wpscdn.cn",
+ "7": "vasvip-pub.wpscdn.cn",
+ "8": "ks3.wpsplus.wpscdn.cn",
+ "9": "op-kdocs.wpscdn.cn",
+ "10": "volcengine-cache-weboffice.wpscdn.cn",
+ "11": "ac.wpscdn.cn",
+ "12": "honeycomb-emergency.wpscdn.cn",
+ "13": "res-honeycomb.wpscdn.cn",
+ "14": "kflow.wpscdn.cn",
+ "15": "personal-act.wpscdn.cn",
+ "16": "official-package.wpscdn.cn"
+ },
"icon": "https://wps.cn/favicon.ico"
},
"company": "珠海金山办公科技有限公司"
@@ -1006,7 +1565,7 @@
"icon": "https://sd.360.cn/favicon.ico"
},
"360统计": {
- "360数据统计类(山东)":{
+ "360数据统计类(山东)": {
"name": "360数据统计类(山东)",
"categoryId": 0,
"url": "stat.sd.360.cn",
@@ -1072,10 +1631,10 @@
"name": "360安全云",
"categoryId": 1,
"url": {
- "1": "https://saas.360.cn/",
- "2": "safe.online.360.cn",
- "3": "admin.online.360.cn",
- "4": "client.saas.360.cn"
+ "1": "https://saas.360.cn/",
+ "2": "safe.online.360.cn",
+ "3": "admin.online.360.cn",
+ "4": "client.saas.360.cn"
},
"icon": "https://saas.360.cn/favicon.ico"
},
@@ -1093,13 +1652,13 @@
"company": "360数字安全集团/三六零数字安全科技集团有限公司"
},
"360软件宝库": {
- "360软件宝库":{
+ "360软件宝库": {
"name": "360软件宝库",
"categoryId": 1,
"url": "https://soft.360.cn/",
"icon": "https://soft.360.cn/favicon.ico"
},
- "360软件宝库开放平台":{
+ "360软件宝库开放平台": {
"name": "360软件宝库开放平台",
"categoryId": 1,
"url": "https://open.soft.360.cn/",
@@ -1107,37 +1666,37 @@
}
},
"360CDN": {
- "安全产品专属高速下载":{
+ "安全产品专属高速下载": {
"name": "安全产品专属高速下载",
"categoryId": 2,
"url": "sfdl.360safe.com",
"icon": "https://www.360.cn/favicon.ico"
},
- "安全产品通用下载":{
+ "安全产品通用下载": {
"name": "安全产品通用下载",
"categoryId": 2,
"url": "dl.360safe.com",
"icon": "https://www.360.cn/favicon.ico"
},
- "360 全产品线下载":{
+ "360 全产品线下载": {
"name": "360 全产品线下载",
"categoryId": 2,
"url": "sfdl.360.cn",
"icon": "https://www.360.cn/favicon.ico"
},
- "360 官网统一下载入口":{
+ "360 官网统一下载入口": {
"name": "360 官网统一下载入口",
"categoryId": 2,
"url": "download.360.cn",
"icon": "https://www.360.cn/favicon.ico"
},
- "360静态资源CDN":{
+ "360静态资源CDN": {
"name": "360静态资源CDN",
"categoryId": 2,
"url": "qhres2.com",
"icon": "https://www.360.cn/favicon.ico"
},
- "安全产品下载加速域名":{
+ "安全产品下载加速域名": {
"name": "安全产品下载加速",
"categoryId": 2,
"url": "wsdl.360safe.com",
@@ -1148,165 +1707,164 @@
"name": "360搜索",
"categoryId": 1,
"url": {
- "1":"https://www.so.com/",
- "2":"https://so.com/"
- },
+ "1": "https://www.so.com/",
+ "2": "https://so.com/"
+ },
"icon": "https://www.so.com/favicon.ico",
"company": "天津三六零快看科技有限公司"
},
- "聚越信息":{
- "聚越信息官网":{
+ "聚越信息": {
+ "聚越信息官网": {
"name": "聚越信息",
"categoryId": 0,
"url": {
- "1": "www.mediav.com",
- "2": "mediav.com",
- "3": "mediav.cn"
- },
+ "1": "www.mediav.com",
+ "2": "mediav.com",
+ "3": "mediav.cn"
+ },
"company": "上海聚胜万合广告有限公司/上海漫酷广告有限公司",
"icon": "https://www.mediav.com/favicon.ico"
},
- "广告展示与投放服务":{
+ "广告展示与投放服务": {
"name": "聚胜万合广告展示与投放服务",
"categoryId": 22,
"url": {
- "1": "g.mediav.com",
- "2": "show.g.mediav.com"
- },
+ "1": "g.mediav.com",
+ "2": "show.g.mediav.com"
+ },
"company": "上海聚胜万合广告有限公司/上海漫酷广告有限公司",
"icon": "https://www.mediav.com/favicon.ico"
},
- "数据监测与分析服务":{
+ "数据监测与分析服务": {
"name": "聚胜万合数据监测与分析服务",
"categoryId": 22,
"url": "d.mediav.com",
"company": "上海聚胜万合广告有限公司/上海漫酷广告有限公司",
"icon": "https://www.mediav.com/favicon.ico"
},
- "开放 API 接口":{
+ "开放 API 接口": {
"name": "聚胜万合开放 API 接口",
"categoryId": 24,
"url": "api.mediav.com",
"company": "上海聚胜万合广告有限公司/上海漫酷广告有限公司",
"icon": "https://www.mediav.com/favicon.ico"
},
- "平台管理后台":{
+ "平台管理后台": {
"name": "聚胜万合平台管理后台",
"categoryId": 24,
"url": "admin.mediav.com",
"company": "上海聚胜万合广告有限公司/上海漫酷广告有限公司",
"icon": "https://www.mediav.com/favicon.ico"
},
- "聚越信息CDN":{
- "聚越信息CDN":{
+ "聚越信息CDN": {
+ "聚越信息CDN": {
"name": "聚越信息CDN",
"categoryId": 2,
"url": "mediav.com",
"company": "上海聚胜万合广告有限公司/上海漫酷广告有限公司",
"icon": "https://www.mediav.com/favicon.ico"
},
- "加密静态资源 CDN":{
+ "加密静态资源 CDN": {
"name": "加密静态资源 CDN",
"categoryId": 2,
"url": "static-ssl.mediav.com",
"company": "上海聚胜万合广告有限公司/上海漫酷广告有限公司",
"icon": "https://www.mediav.com/favicon.ico"
},
- "静态资源CDN":{
+ "静态资源CDN": {
"name": "聚越信息静态资源CDN",
"categoryId": 2,
"url": "static.mediav.com",
"company": "上海聚胜万合广告有限公司/上海漫酷广告有限公司",
"icon": "https://www.mediav.com/favicon.ico"
},
- "数据监测CDN":{
+ "数据监测CDN": {
"name": "聚越信息数据监测CDN",
"categoryId": 2,
"url": "d.mediav.com",
"company": "上海聚胜万合广告有限公司/上海漫酷广告有限公司",
"icon": "https://www.mediav.com/favicon.ico"
},
- "开放 API 接口CDN":{
+ "开放 API 接口CDN": {
"name": "聚越信息开放 API 接口CDN",
"categoryId": 2,
"url": "api.mediav.com",
"company": "上海聚胜万合广告有限公司/上海漫酷广告有限公司",
"icon": "https://www.mediav.com/favicon.ico"
},
- "聚效平台CDN":{
+ "聚效平台CDN": {
"name": "聚效平台CDN",
"categoryId": 2,
"url": "mediav.com",
"company": "上海聚胜万合广告有限公司/上海漫酷广告有限公司",
"icon": "https://www.mediav.com/favicon.ico"
},
- "移动端适配CDN":{
+ "移动端适配CDN": {
"name": "移动端适配CDN",
"categoryId": 2,
"url": "m.mediav.com",
"company": "上海聚胜万合广告有限公司/上海漫酷广告有限公司",
"icon": "https://www.mediav.com/favicon.ico"
},
- "图片素材CDN":{
+ "图片素材CDN": {
"name": "图片素材CDN",
"categoryId": 2,
"url": "img.mediav.com",
- "company": "上海聚胜万合广告有限公司/上海漫酷广告有限公司"
+ "company": "上海聚胜万合广告有限公司/上海漫酷广告有限公司"
},
- "聚越信息静态资源":{
+ "聚越信息静态资源": {
"name": "聚越信息静态资源",
"categoryId": 2,
"url": "static.mediav.com",
"company": "上海聚胜万合广告有限公司/上海漫酷广告有限公司",
"icon": "https://www.mediav.com/favicon.ico"
},
- "广告展示备用CDN":{
+ "广告展示备用CDN": {
"name": "广告展示备用CDN",
"categoryId": 2,
"url": "show-3.mediav.com",
"company": "上海聚胜万合广告有限公司/上海漫酷广告有限公司",
"icon": "https://www.mediav.com/favicon.ico"
},
- "广告展示扩展CDN":{
+ "广告展示扩展CDN": {
"name": "广告展示扩展CDN",
"categoryId": 2,
"url": {
- "1": "show-2.mediav.com",
- "2": "show-1.mediav.com"
- },
+ "1": "show-2.mediav.com",
+ "2": "show-1.mediav.com"
+ },
"company": "上海聚胜万合广告有限公司/上海漫酷广告有限公司",
"icon": "https://www.mediav.com/favicon.ico"
},
- "广告展示基础CDN":{
+ "广告展示基础CDN": {
"name": "广告展示基础CDN",
"categoryId": 2,
"url": "show.mediav.com",
"company": "上海聚胜万合广告有限公司/上海漫酷广告有限公司",
"icon": "https://www.mediav.com/favicon.ico"
},
- "广告投放综合CDN":{
+ "广告投放综合CDN": {
"name": "广告投放综合CDN",
"categoryId": 2,
"url": "g.mediav.com",
"company": "上海聚胜万合广告有限公司/上海漫酷广告有限公司",
"icon": "https://www.mediav.com/favicon.ico"
},
- "广告展示主CDN":{
+ "广告展示主CDN": {
"name": "广告展示主CDN",
"categoryId": 2,
"url": {
- "1": "show-g.mediav.com",
- "2": "show.g.mediav.com"
+ "1": "show-g.mediav.com",
+ "2": "show.g.mediav.com"
},
"company": "上海聚胜万合广告有限公司/上海漫酷广告有限公司",
"icon": "https://www.mediav.com/favicon.ico"
}
}
}
-
},
"picsum-photos": {
- "picsum-photos":{
+ "picsum-photos": {
"name": "面向开发者的开源占位图 API 项目",
"categoryId": 0,
"url": "picsum.photos",
@@ -1319,61 +1877,76 @@
"name": "绮梦之家",
"categoryId": 0,
"url": {
- "1": "https://www.amazehome.xyz/",
- "2": "amazehome.xyz",
- "3": "https://www.amazehome.cn/",
- "4": "amazehome.cn"
- },
+ "1": "https://www.amazehome.xyz/",
+ "2": "amazehome.xyz",
+ "3": "https://www.amazehome.cn/",
+ "4": "amazehome.cn"
+ },
"icon": "https://www.amazehome.cn/upload/cf3f6d7f-65b5-4df2-a7af-8289fb5aad81-yagB.png"
},
- "绮梦之家认证中心":{
+ "绮梦之家认证中心": {
"name": "绮梦之家认证中心",
"categoryId": 0,
"url": "https://cas.amazehome.cn/",
"icon": "https://www.amazehome.cn/upload/cf3f6d7f-65b5-4df2-a7af-8289fb5aad81-yagB.png"
},
- "绮梦之家云同步服务":{
+ "绮梦之家云同步服务": {
"name": "绮梦之家云同步服务",
"categoryId": 0,
"url": "https://cloud.amazehome.cn/",
"icon": "https://www.amazehome.cn/upload/cf3f6d7f-65b5-4df2-a7af-8289fb5aad81-yagB.png"
},
- "绮梦之家Git服务":{
+ "绮梦之家Git服务": {
"name": "绮梦之家Git服务",
"categoryId": 0,
"url": "https://gitea.amazehome.xyz/",
- "icon": "https://www.amazehome.cn/upload/cf3f6d7f-65b5-4df2-a7af-8289fb5aad81-yagB.png"
+ "icon": "https://gitea.amazehome.xyz/assets/img/favicon.svg"
},
- "绮梦之家导航":{
+ "绮梦之家导航": {
"name": "绮梦之家导航",
"categoryId": 0,
"url": "https://nav.amazehome.cn/",
- "icon": "https://www.amazehome.cn/upload/cf3f6d7f-65b5-4df2-a7af-8289fb5aad81-yagB.png"
+ "icon": "https://nav.amazehome.cn/icons/logo.png"
},
- "绮梦之家VMWare虚拟化平台":{
+ "绮梦之家VMWare虚拟化平台": {
"name": "绮梦之家VMWare虚拟化平台",
"categoryId": 0,
"url": "https://vcenter.amazehome.xyz/",
"icon": "https://www.amazehome.cn/upload/cf3f6d7f-65b5-4df2-a7af-8289fb5aad81-yagB.png"
},
- "绮梦之家Office协作服务":{
+ "绮梦之家Office协作服务": {
"name": "绮梦之家Office协作服务",
"categoryId": 0,
"url": "https://view.amazehome.cn/",
- "icon": "https://www.amazehome.cn/upload/cf3f6d7f-65b5-4df2-a7af-8289fb5aad81-yagB.png"
+ "icon": "https://view.amazehome.cn/welcome/css/favicon.ico"
},
- "绮梦之家数据中心":{
+ "绮梦之家数据中心": {
"name": "绮梦之家数据中心",
"categoryId": 0,
"url": "https://data.amazehome.xyz/",
"icon": "https://www.amazehome.cn/upload/cf3f6d7f-65b5-4df2-a7af-8289fb5aad81-yagB.png"
},
- "绮梦之家云服务":{
+ "绮梦之家云服务": {
"name": "绮梦之家云服务",
"categoryId": 0,
"url": "https://yun.amazehome.xyz/",
"icon": "https://www.amazehome.cn/upload/cf3f6d7f-65b5-4df2-a7af-8289fb5aad81-yagB.png"
},
+ "绮梦之家域控制器": {
+ "name": "绮梦之家AD域控制器",
+ "categoryId": 0,
+ "url": "dc.amazehome.xyz",
+ "icon": "https://www.amazehome.cn/upload/cf3f6d7f-65b5-4df2-a7af-8289fb5aad81-yagB.png"
+ },
+ "绮梦之家VMWare虚拟化桌面平台": {
+ "name": "绮梦之家VMWare虚拟化桌面平台",
+ "categoryId": 0,
+ "url": {
+ "1": "https://horizon.amazehome.xyz/",
+ "2": "https://horizon1.amazehome.xyz/"
+ },
+ "icon": "https://www.amazehome.cn/upload/cf3f6d7f-65b5-4df2-a7af-8289fb5aad81-yagB.png"
+ },
"company": "绮梦之家"
},
"微步在线": {
@@ -1403,12 +1976,12 @@
"name": "飞致云",
"categoryId": 0,
"url": {
- "1":"https://fit2cloud.com/",
- "2":"https://www.fit2cloud.com/"
- },
+ "1": "https://fit2cloud.com/",
+ "2": "https://www.fit2cloud.com/"
+ },
"icon": "https://fit2cloud.com/images/logo/favicon.ico"
},
- "1Panel":{
+ "1Panel": {
"name": "1Panel",
"categoryId": 0,
"url": "https://1panel.cn/",
@@ -1421,9 +1994,9 @@
"name": "Tailwind CSS",
"categoryId": 0,
"url": {
- "1":"https://tailwindcss.com/",
- "2":"https://cdn.tailwindcss.com/"
- },
+ "1": "https://tailwindcss.com/",
+ "2": "https://cdn.tailwindcss.com/"
+ },
"icon": "https://tailwindcss.com/favicons/favicon-32x32.png?v=4"
},
"company": "Tailwind Labs"
@@ -1475,9 +2048,9 @@
"name": "小米MIUI",
"categoryId": 0,
"url": {
- "1":"https://www.miui.com/",
- "2":"https://home.miui.com/"
- },
+ "1": "https://www.miui.com/",
+ "2": "https://home.miui.com/"
+ },
"icon": "https://www.miui.com/favicon.ico"
},
"小米隐私": {
@@ -1529,8 +2102,8 @@
"name": "Staticfile CDN",
"categoryId": 2,
"url": {
- "1":"cdn.staticfile.org",
- "2":"cdn.staticfile.net"
+ "1": "cdn.staticfile.org",
+ "2": "cdn.staticfile.net"
},
"icon": "https://www.qiniu.com/favicon.ico"
},
@@ -1546,16 +2119,19 @@
"Akamai CDN": {
"name": "Akamai CDN",
"categoryId": 2,
- "url": "lf-rc1.yhgfb-cn-static.com",
+ "url": {
+ "1": "akamaized.net",
+ "2": "lf-rc2.yhgfb-cn-static.com"
+ },
"icon": "https://www.akamai.com/site/favicon/favicon.ico"
},
"核心边缘 DNS 与 CDN 调度根域名": {
"name": "核心边缘 DNS 与 CDN 调度根域名",
"categoryId": 2,
"url": {
- "1":"akamaihd.net",
- "2":"akamaiedge.net",
- "3":"edgesuite.net"
+ "1": "akamaihd.net",
+ "2": "akamaiedge.net",
+ "3": "edgesuite.net"
},
"icon": "https://www.akamai.com/site/favicon/favicon.ico"
},
@@ -1575,13 +2151,13 @@
"icon": "https://passport.weibo.com/favicon.ico"
},
"新浪微博开放平台": {
- "新浪微博开放平台":{
+ "新浪微博开放平台": {
"name": "新浪微博开放平台",
"categoryId": 22,
"url": "https://open.weibo.com/",
"icon": "https://open.weibo.com/favicon.ico"
},
- "新浪微博开放平台API":{
+ "新浪微博开放平台API": {
"name": "新浪微博开放平台API",
"categoryId": 22,
"url": "https://api.weibo.com/",
@@ -1592,7 +2168,7 @@
"name": "新浪微博CDN",
"categoryId": 2,
"url": {
- "1":"sinaimg.cn"
+ "1": "sinaimg.cn"
},
"icon": "https://weibo.com/favicon.ico"
},
@@ -1603,156 +2179,156 @@
"name": "南京市中医院",
"categoryId": 0,
"url": {
- "1":"https://www.njszyy.cn/",
- "2":"https://njszyy.cn/"
- },
- "icon": "#"
+ "1": "https://www.njszyy.cn/",
+ "2": "https://njszyy.cn/"
+ },
+ "icon": "#"
},
"company": "南京市中医院"
},
"软件源相关": {
- "腾讯镜像源":{
+ "腾讯镜像源": {
"name": "腾讯软件源",
"categoryId": 0,
"url": {
"1": "https://mirrors.cloud.tencent.com/"
- },
+ },
"icon": "https://www.tencent.com/favicon.ico",
"company": "深圳腾讯计算机系统有限公司"
},
- "南京大学镜像源":{
+ "南京大学镜像源": {
"name": "南京大学开源镜像站",
"categoryId": 0,
"url": {
"1": "https://mirrors.nju.edu.cn/"
- },
+ },
"icon": "https://www.nju.edu.cn/favicon.ico",
"company": "南京大学"
},
- "高校镜像源联盟":{
+ "高校镜像源联盟": {
"name": "高校镜像源联盟MirrorZ",
"categoryId": 0,
"url": {
"1": "https://mirrorz.org/",
- "2":"https://help.mirrorz.org/"
- },
+ "2": "https://help.mirrorz.org/"
+ },
"icon": "https://mirrorz.org/static/img/mirrorz.svg",
"company": "MirrorZ Project"
},
- "清华大学镜像源":{
+ "清华大学镜像源": {
"name": "清华大学开源镜像站",
"categoryId": 0,
"url": {
"1": "https://mirrors.tuna.tsinghua.edu.cn/"
- },
+ },
"icon": "https://mirrors.tuna.tsinghua.edu.cn/static/img/favicon.png",
"company": "清华大学"
},
- "中国科学技术大学镜像源":{
+ "中国科学技术大学镜像源": {
"name": "中国科学技术大学开源镜像站",
"categoryId": 0,
"url": {
"1": "https://mirrors.ustc.edu.cn/"
- },
+ },
"icon": "https://mirrors.ustc.edu.cn/static/img/favicon.png",
"company": "中国科学技术大学"
},
- "中国科学院软件研究所镜像源":{
+ "中国科学院软件研究所镜像源": {
"name": "中国科学院软件研究所开源镜像站",
"categoryId": 0,
"url": {
"1": "https://mirrors.iscas.ac.cn/"
- },
+ },
"icon": "https://api.cas.cn/favicon/is.ico",
"company": "中国科学院软件研究所"
},
- "北京外国语大学镜像源":{
+ "北京外国语大学镜像源": {
"name": "北京外国语大学开源镜像站",
"categoryId": 0,
"url": {
"1": "https://mirrors.bfsu.edu.cn/"
- },
+ },
"icon": "https://mirrors.bfsu.edu.cn/static/img/favicon.png",
"company": "北京外国语大学"
},
- "哈尔滨工业大学镜像源":{
+ "哈尔滨工业大学镜像源": {
"name": "哈尔滨工业大学开源镜像站",
"categoryId": 0,
"url": {
"1": "https://mirrors.hit.edu.cn/"
- },
+ },
"icon": "https://mirrors.hit.edu.cn/favicon.svg",
"company": "哈尔滨工业大学"
},
- "北京大学镜像源":{
+ "北京大学镜像源": {
"name": "北京大学开源镜像站",
"categoryId": 0,
"url": {
"1": "https://mirrors.pku.edu.cn/"
- },
+ },
"icon": "https://mirrors.pku.edu.cn/static/favicon.ico",
"company": "北京大学"
},
- "南京工业大学镜像源":{
+ "南京工业大学镜像源": {
"name": "南京工业大学开源软件镜像站",
"categoryId": 0,
"url": {
"1": "https://mirrors.njtech.edu.cn/"
- },
+ },
"icon": "https://www.njtech.edu.cn/favicon.ico",
"company": "南京工业大学"
},
- "南阳理工学院镜像源":{
+ "南阳理工学院镜像源": {
"name": "南阳理工学院开源软件镜像站",
"categoryId": 0,
"url": {
"1": "https://mirror.nyist.edu.cn/"
- },
+ },
"icon": "https://cernet.mirror.nyist.edu.cn/static/img/favicon.png",
"company": "南阳理工学院"
},
- "思源镜像站":{
+ "思源镜像站": {
"name": "思源镜像站",
"categoryId": 0,
"url": {
"1": "https://mirror.sjtu.edu.cn/"
- },
+ },
"icon": "https://mirror.sjtu.edu.cn/favicon.ico",
"company": "上海交通大学"
},
- "南方科技大学镜像源":{
+ "南方科技大学镜像源": {
"name": "南方科技大学开源软件镜像站",
"categoryId": 0,
"url": {
"1": "https://mirrors.sustech.edu.cn/"
- },
+ },
"icon": "https://mirrors.sustech.edu.cn/favicon.ico",
"company": "南方科技大学"
},
- "浙江大学开源软件镜像站":{
+ "浙江大学开源软件镜像站": {
"name": "浙江大学开源软件镜像站",
"categoryId": 0,
"url": {
"1": "https://mirrors.zju.edu.cn/"
- },
+ },
"icon": "https://mirrors.zju.edu.cn/index/favicon.svg?v=f28569028f33e6ac676201f84c9d61bf",
"company": "浙江大学"
},
- "兰州大学开源社区镜像站":{
+ "兰州大学开源社区镜像站": {
"name": "兰州大学开源社区镜像站",
"categoryId": 0,
"url": {
"1": "https://mirrors.lzu.edu.cn/"
- },
+ },
"icon": "https://mirrors.lzu.edu.cn/static/img/favicon.png",
"company": "兰州大学"
},
- "重庆邮电大学开源镜像站":{
+ "重庆邮电大学开源镜像站": {
"name": "重庆邮电大学开源镜像站",
"categoryId": 0,
"url": {
"1": "https://mirrors.cqupt.edu.cn/"
- },
+ },
"icon": "https://mirrors.cqupt.edu.cn/favicon.ico",
"company": "重庆邮电大学"
}
@@ -1766,7 +2342,7 @@
},
"icon": "https://www.bilibili.com/favicon.ico"
},
- "哔哩哔哩游戏":{
+ "哔哩哔哩游戏": {
"name": "哔哩哔哩游戏",
"categoryId": 0,
"url": {
@@ -1788,7 +2364,7 @@
"icon": "https://www.bilibili.com/favicon.ico",
"company": "杭州幻电科技有限公司"
},
- "B 站直播的官方 Web 前端域名":{
+ "B 站直播的官方 Web 前端域名": {
"name": "哔哩哔哩直播官方 Web 前端域名",
"categoryId": 24,
"url": {
@@ -1797,8 +2373,8 @@
"icon": "https://www.bilibili.com/favicon.ico",
"company": "杭州幻电科技有限公司"
},
- "哔哩哔哩API":{
- "开放数据接口":{
+ "哔哩哔哩API": {
+ "开放数据接口": {
"name": "哔哩哔哩开放数据接口(API)",
"categoryId": 24,
"url": {
@@ -1807,7 +2383,7 @@
"icon": "https://www.bilibili.com/favicon.ico",
"company": "杭州幻电科技有限公司"
},
- "创作者数据中心":{
+ "创作者数据中心": {
"name": "哔哩哔哩创作者数据中心",
"categoryId": 24,
"url": {
@@ -1816,7 +2392,7 @@
"icon": "https://www.bilibili.com/favicon.ico",
"company": "杭州幻电科技有限公司"
},
- "个性化推荐数据接口":{
+ "个性化推荐数据接口": {
"name": "哔哩哔哩个性化推荐数据接口",
"categoryId": 24,
"url": {
@@ -1825,7 +2401,7 @@
"icon": "https://www.bilibili.com/favicon.ico",
"company": "杭州幻电科技有限公司"
},
- "动态内容核心API":{
+ "动态内容核心API": {
"name": "哔哩哔哩动态内容核心API",
"categoryId": 24,
"url": {
@@ -1834,7 +2410,7 @@
"icon": "https://www.bilibili.com/favicon.ico",
"company": "杭州幻电科技有限公司"
},
- "全局消息推送核心API":{
+ "全局消息推送核心API": {
"name": "哔哩哔哩全局消息推送核心API",
"categoryId": 24,
"url": {
@@ -1843,7 +2419,7 @@
"icon": "https://www.bilibili.com/favicon.ico",
"company": "杭州幻电科技有限公司"
},
- "消息中心核心域名":{
+ "消息中心核心域名": {
"name": "哔哩哔哩消息中心核心",
"categoryId": 24,
"url": {
@@ -1852,7 +2428,7 @@
"icon": "https://www.bilibili.com/favicon.ico",
"company": "杭州幻电科技有限公司"
},
- "全局广播与长连接核心域名":{
+ "全局广播与长连接核心域名": {
"name": "哔哩哔哩全局广播与长连接核心",
"categoryId": 24,
"url": {
@@ -1861,7 +2437,7 @@
"icon": "https://www.bilibili.com/favicon.ico",
"company": "杭州幻电科技有限公司"
},
- "核心业务API域名":{
+ "核心业务API域名": {
"name": "哔哩哔哩核心业务API",
"categoryId": 24,
"url": {
@@ -1870,7 +2446,7 @@
"icon": "https://www.bilibili.com/favicon.ico",
"company": "杭州幻电科技有限公司"
},
- "账号认证与登录核心域名":{
+ "账号认证与登录核心域名": {
"name": "哔哩哔哩账号认证与登录核心",
"categoryId": 24,
"url": {
@@ -1879,7 +2455,7 @@
"icon": "https://www.bilibili.com/favicon.ico",
"company": "杭州幻电科技有限公司"
},
- "核心数据服务":{
+ "核心数据服务": {
"name": "核心数据服务",
"categoryId": 24,
"url": {
@@ -1888,7 +2464,7 @@
"icon": "https://www.bilibili.com/favicon.ico",
"company": "杭州幻电科技有限公司"
},
- "哔哩哔哩游戏用户行为与数据采集核心":{
+ "哔哩哔哩游戏用户行为与数据采集核心": {
"name": "哔哩哔哩游戏用户行为与数据采集核心",
"categoryId": 24,
"url": {
@@ -1901,718 +2477,718 @@
},
"company": "上海宽娱数码科技有限公司"
},
- "政府网站":{
- "国务院":{
- "中华人民共和国中央人民政府":{
+ "政府网站": {
+ "国务院": {
+ "中华人民共和国中央人民政府": {
"name": "中华人民共和国中央人民政府",
"categoryId": 0,
"url": "https://www.gov.cn/",
"icon": "https://www.gov.cn/images/trs_favicon.ico"
},
- "外交部":{
+ "外交部": {
"name": "外交部",
"categoryId": 14,
"url": "https://www.mfa.gov.cn/",
"icon": "https://www.mfa.gov.cn/favicon.ico"
},
- "国防部":{
+ "国防部": {
"name": "国防部",
"categoryId": 14,
"url": "https://www.mod.gov.cn/",
"icon": "https://www.mod.gov.cn/favicon.ico"
},
- "国家发展和改革委员会":{
+ "国家发展和改革委员会": {
"name": "国家发展和改革委员会",
"categoryId": 14,
"url": "https://www.ndrc.gov.cn/",
"icon": "https://www.ndrc.gov.cn/favicon.ico"
},
- "教育部":{
+ "教育部": {
"name": "教育部",
"categoryId": 14,
"url": "https://www.moe.gov.cn/",
"icon": "https://www.moe.gov.cn/favicon.ico"
},
- "科学技术部":{
+ "科学技术部": {
"name": "科学技术部",
"categoryId": 14,
"url": "https://www.most.gov.cn/",
"icon": "https://www.most.gov.cn/favicon.ico"
},
- "工业和信息化部":{
+ "工业和信息化部": {
"name": "工业和信息化部",
"categoryId": 14,
"url": "https://www.miit.gov.cn/",
"icon": "https://www.miit.gov.cn/favicon.ico"
},
- "国家民族事务委员会":{
+ "国家民族事务委员会": {
"name": "国家民族事务委员会",
"categoryId": 14,
"url": "https://www.seac.gov.cn/",
"icon": "https://www.seac.gov.cn/favicon.ico"
},
- "公安部":{
+ "公安部": {
"name": "公安部",
"categoryId": 14,
"url": "https://www.mps.gov.cn/",
"icon": "https://www.mps.gov.cn/favicon.ico"
},
- "民政部":{
+ "民政部": {
"name": "民政部",
"categoryId": 14,
"url": "https://www.mca.gov.cn/",
"icon": "https://www.mca.gov.cn/favicon.ico"
},
- "司法部":{
+ "司法部": {
"name": "司法部",
"categoryId": 14,
"url": "https://www.moj.gov.cn/",
"icon": "https://www.moj.gov.cn/favicon.ico"
},
- "财政部":{
+ "财政部": {
"name": "财政部",
"categoryId": 14,
"url": "https://www.mof.gov.cn/",
"icon": "https://www.mof.gov.cn/favicon.ico"
},
- "人力资源和社会保障部":{
+ "人力资源和社会保障部": {
"name": "人力资源和社会保障部",
"categoryId": 14,
"url": "https://www.mohrss.gov.cn/",
"icon": "https://www.mohrss.gov.cn/favicon.ico"
},
- "自然资源部":{
+ "自然资源部": {
"name": "自然资源部",
"categoryId": 14,
"url": "https://www.mnr.gov.cn/",
"icon": "https://www.mnr.gov.cn/favicon.ico"
},
- "生态环境部":{
+ "生态环境部": {
"name": "生态环境部",
"categoryId": 14,
"url": "https://www.mee.gov.cn/",
"icon": "https://www.mee.gov.cn/favicon.ico"
},
- "住房和城乡建设部":{
+ "住房和城乡建设部": {
"name": "住房和城乡建设部",
"categoryId": 14,
"url": "https://www.mohurd.gov.cn/",
"icon": "https://www.mohurd.gov.cn/favicon.ico"
},
- "交通运输部":{
+ "交通运输部": {
"name": "交通运输部",
"categoryId": 14,
"url": "https://www.mot.gov.cn/",
"icon": "https://www.mot.gov.cn/favicon.ico"
},
- "水利部":{
+ "水利部": {
"name": "水利部",
"categoryId": 14,
"url": "https://www.mwr.gov.cn/",
"icon": "https://www.mwr.gov.cn/favicon.ico"
},
- "农业农村部":{
+ "农业农村部": {
"name": "农业农村部",
"categoryId": 14,
"url": "https://www.moa.gov.cn/",
"icon": "https://www.moa.gov.cn/favicon.ico"
},
- "商务部":{
+ "商务部": {
"name": "商务部",
"categoryId": 14,
"url": "https://www.mofcom.gov.cn/",
"icon": "https://www.mofcom.gov.cn/favicon.ico"
},
- "文化和旅游部":{
+ "文化和旅游部": {
"name": "文化和旅游部",
"categoryId": 14,
"url": "https://www.mct.gov.cn/",
"icon": "https://www.mct.gov.cn/favicon.ico"
},
- "国家卫生健康委员会":{
+ "国家卫生健康委员会": {
"name": "国家卫生健康委员会",
"categoryId": 14,
"url": "https://www.nhc.gov.cn/",
"icon": "https://www.nhc.gov.cn/favicon.ico"
},
- "退役军人事务部":{
+ "退役军人事务部": {
"name": "退役军人事务部",
"categoryId": 14,
"url": "https://www.mva.gov.cn/",
"icon": "https://www.mva.gov.cn/favicon.ico"
},
- "应急管理部":{
+ "应急管理部": {
"name": "应急管理部",
"categoryId": 14,
"url": "https://www.mem.gov.cn/",
"icon": "https://www.mem.gov.cn/favicon.ico"
},
- "中国人民银行":{
+ "中国人民银行": {
"name": "中国人民银行",
"categoryId": 14,
"url": "https://www.pbc.gov.cn/",
"icon": "https://www.pbc.gov.cn/favicon.ico"
},
- "审计署":{
+ "审计署": {
"name": "审计署",
"categoryId": 14,
"url": "https://www.audit.gov.cn/",
"icon": "https://www.audit.gov.cn/favicon.ico"
},
- "国家航天局":{
+ "国家航天局": {
"name": "国家航天局",
"categoryId": 14,
"url": "https://www.cnsa.gov.cn/",
"icon": "https://www.cnsa.gov.cn/favicon.ico"
},
- "国家原子能机构":{
+ "国家原子能机构": {
"name": "国家原子能机构",
"categoryId": 14,
"url": "https://www.caea.gov.cn/",
"icon": "https://www.caea.gov.cn/favicon.ico"
},
- "国家乡村振兴局":{
+ "国家乡村振兴局": {
"name": "国家乡村振兴局",
"categoryId": 14,
"url": "https://www.nrra.gov.cn/",
"icon": "https://www.nrra.gov.cn/favicon.ico"
},
- "国务院国有资产监督管理委员会":{
+ "国务院国有资产监督管理委员会": {
"name": "国务院国有资产监督管理委员会",
"categoryId": 14,
"url": "https://www.sasac.gov.cn/",
"icon": "https://www.sasac.gov.cn/favicon.ico"
},
- "海关总署":{
+ "海关总署": {
"name": "海关总署",
"categoryId": 14,
"url": "https://www.customs.gov.cn/",
"icon": "https://www.customs.gov.cn/favicon.ico"
},
- "国家税务总局":{
+ "国家税务总局": {
"name": "国家税务总局",
"categoryId": 14,
"url": "https://www.chinatax.gov.cn/",
"icon": "https://www.chinatax.gov.cn/favicon.ico"
},
- "国家市场监督管理总局":{
+ "国家市场监督管理总局": {
"name": "国家市场监督管理总局",
"categoryId": 14,
"url": "https://www.samr.gov.cn/",
"icon": "https://www.samr.gov.cn/favicon.ico"
},
- "国家金融监督管理总局":{
+ "国家金融监督管理总局": {
"name": "国家金融监督管理总局",
"categoryId": 14,
"url": "https://www.cbirc.gov.cn/",
"icon": "https://www.cbirc.gov.cn/favicon.ico"
},
- "中国证券监督管理委员会":{
+ "中国证券监督管理委员会": {
"name": "中国证券监督管理委员会",
"categoryId": 14,
"url": "https://www.csrc.gov.cn/",
"icon": "https://www.csrc.gov.cn/favicon.ico"
},
- "国家广播电视总局":{
+ "国家广播电视总局": {
"name": "国家广播电视总局",
"categoryId": 14,
"url": "https://www.nrta.gov.cn/",
"icon": "https://www.nrta.gov.cn/favicon.ico"
},
- "国家体育总局":{
+ "国家体育总局": {
"name": "国家体育总局",
"categoryId": 14,
"url": "https://www.sport.gov.cn/",
"icon": "https://www.sport.gov.cn/favicon.ico"
},
- "国家信访局":{
+ "国家信访局": {
"name": "国家信访局",
"categoryId": 14,
"url": "https://www.gjxfj.gov.cn/",
"icon": "https://www.gjxfj.gov.cn/favicon.ico"
},
- "国家统计局":{
+ "国家统计局": {
"name": "国家统计局",
"categoryId": 14,
"url": "https://www.stats.gov.cn/",
"icon": "https://www.stats.gov.cn/favicon.ico"
},
- "国家知识产权局":{
+ "国家知识产权局": {
"name": "国家知识产权局",
"categoryId": 14,
"url": "https://www.cnipa.gov.cn/",
"icon": "https://www.cnipa.gov.cn/favicon.ico"
},
- "国家国际发展合作署":{
+ "国家国际发展合作署": {
"name": "国家国际发展合作署",
"categoryId": 14,
"url": "https://www.cidca.gov.cn/",
"icon": "https://www.cidca.gov.cn/favicon.ico"
},
- "国家医疗保障局":{
+ "国家医疗保障局": {
"name": "国家医疗保障局",
"categoryId": 14,
"url": "https://www.nhsa.gov.cn/",
"icon": "https://www.nhsa.gov.cn/favicon.ico"
},
- "国务院参事室":{
+ "国务院参事室": {
"name": "国务院参事室",
"categoryId": 14,
"url": "https://www.counsellor.gov.cn/",
"icon": "https://www.counsellor.gov.cn/favicon.ico"
},
- "国家机关事务管理局":{
+ "国家机关事务管理局": {
"name": "国家机关事务管理局",
"categoryId": 14,
"url": "https://www.ggj.gov.cn/",
"icon": "https://www.ggj.gov.cn/favicon.ico"
},
- "国家认证认可监督管理委员会":{
+ "国家认证认可监督管理委员会": {
"name": "国家认证认可监督管理委员会",
"categoryId": 14,
"url": "https://www.cnca.gov.cn/",
"icon": "https://www.cnca.gov.cn/favicon.ico"
},
- "国家标准化管理委员会":{
+ "国家标准化管理委员会": {
"name": "国家标准化管理委员会",
"categoryId": 14,
"url": "https://www.sac.gov.cn/",
"icon": "https://www.sac.gov.cn/favicon.ico"
},
- "国家新闻出版署":{
+ "国家新闻出版署": {
"name": "国家新闻出版署(国家版权局)",
"categoryId": 14,
"url": "https://www.nppa.gov.cn/",
"icon": "https://www.nppa.gov.cn/favicon.ico"
},
- "国家宗教事务局":{
+ "国家宗教事务局": {
"name": "国家宗教事务局",
"categoryId": 14,
"url": "https://www.sara.gov.cn/",
"icon": "https://www.sara.gov.cn/favicon.ico"
},
- "国务院侨务办公室":{
+ "国务院侨务办公室": {
"name": "国务院侨务办公室",
"categoryId": 14,
"url": "https://www.gqb.gov.cn/",
"icon": "https://www.gqb.gov.cn/favicon.ico"
},
- "国务院港澳事务办公室":{
+ "国务院港澳事务办公室": {
"name": "国务院港澳事务办公室",
"categoryId": 14,
"url": "https://www.hmo.gov.cn/",
"icon": "https://www.hmo.gov.cn/favicon.ico"
},
- "国务院台湾事务办公室":{
+ "国务院台湾事务办公室": {
"name": "国务院台湾事务办公室",
"categoryId": 14,
"url": "https://www.gat.gov.cn/",
"icon": "https://www.gat.gov.cn/favicon.ico"
},
- "国家互联网信息办公室":{
+ "国家互联网信息办公室": {
"name": "国家互联网信息办公室",
"categoryId": 14,
"url": "https://www.cac.gov.cn/",
"icon": "https://www.cac.gov.cn/favicon.ico"
},
- "国务院新闻办公室":{
+ "国务院新闻办公室": {
"name": "国务院新闻办公室",
"categoryId": 14,
"url": "https://www.scio.gov.cn/",
"icon": "https://www.scio.gov.cn/favicon.ico"
},
- "新华通讯社":{
+ "新华通讯社": {
"name": "新华通讯社",
"categoryId": 14,
"url": "https://www.xinhuanet.com/",
"icon": "https://www.xinhuanet.com/favicon.ico"
},
- "中国科学院":{
+ "中国科学院": {
"name": "中国科学院",
"categoryId": 14,
"url": "https://www.cas.cn/",
"icon": "https://www.cas.cn/favicon.ico"
},
- "中国社会科学院":{
+ "中国社会科学院": {
"name": "中国社会科学院",
"categoryId": 14,
"url": "https://www.cssn.cn/",
"icon": "https://www.cssn.cn/favicon.ico"
},
- "中国工程院":{
+ "中国工程院": {
"name": "中国工程院",
"categoryId": 14,
"url": "https://www.cae.cn/",
"icon": "https://www.cae.cn/favicon.ico"
},
- "国务院发展研究中心":{
+ "国务院发展研究中心": {
"name": "国务院发展研究中心",
"categoryId": 14,
"url": "https://www.drc.gov.cn/",
"icon": "https://www.drc.gov.cn/favicon.ico"
},
- "中央广播电视总台":{
+ "中央广播电视总台": {
"name": "中央广播电视总台",
"categoryId": 14,
"url": "https://www.cctv.com/",
"icon": "https://www.cctv.com/favicon.ico"
},
- "中国气象局":{
+ "中国气象局": {
"name": "中国气象局",
"categoryId": 14,
"url": "https://www.cma.gov.cn/",
"icon": "https://www.cma.gov.cn/favicon.ico"
},
- "国家行政学院":{
+ "国家行政学院": {
"name": "国家行政学院",
"categoryId": 14,
"url": "https://www.nsa.gov.cn/",
"icon": "https://www.nsa.gov.cn/favicon.ico"
},
- "国家粮食和物资储备局":{
+ "国家粮食和物资储备局": {
"name": "国家粮食和物资储备局",
"categoryId": 14,
"url": "https://www.lswz.gov.cn/",
"icon": "https://www.lswz.gov.cn/favicon.ico"
},
- "国家能源局":{
+ "国家能源局": {
"name": "国家能源局",
"categoryId": 14,
"url": "https://www.nea.gov.cn/",
"icon": "https://www.nea.gov.cn/favicon.ico"
},
- "国家国防科技工业局":{
+ "国家国防科技工业局": {
"name": "国家国防科技工业局",
"categoryId": 14,
"url": "https://www.sastind.gov.cn/",
"icon": "https://www.sastind.gov.cn/favicon.ico"
},
- "国家烟草专卖局":{
+ "国家烟草专卖局": {
"name": "国家烟草专卖局",
"categoryId": 14,
"url": "https://www.tobacco.gov.cn/",
"icon": "https://www.tobacco.gov.cn/favicon.ico"
},
- "国家移民管理局":{
+ "国家移民管理局": {
"name": "国家移民管理局",
"categoryId": 14,
"url": "https://www.nia.gov.cn/",
"icon": "https://www.nia.gov.cn/favicon.ico"
},
- "国家林业和草原局":{
+ "国家林业和草原局": {
"name": "国家林业和草原局",
"categoryId": 14,
"url": "https://www.forestry.gov.cn/",
"icon": "https://www.forestry.gov.cn/favicon.ico"
},
- "国家铁路局":{
+ "国家铁路局": {
"name": "国家铁路局",
"categoryId": 14,
"url": "https://www.nra.gov.cn/",
"icon": "https://www.nra.gov.cn/favicon.ico"
},
- "中国民用航空局":{
+ "中国民用航空局": {
"name": "中国民用航空局",
"categoryId": 14,
"url": "https://www.caac.gov.cn/",
"icon": "https://www.caac.gov.cn/favicon.ico"
},
- "国家邮政局":{
+ "国家邮政局": {
"name": "国家邮政局",
"categoryId": 14,
"url": "https://www.spb.gov.cn/",
"icon": "https://www.spb.gov.cn/favicon.ico"
},
- "国家文物局":{
+ "国家文物局": {
"name": "国家文物局",
"categoryId": 14,
"url": "https://www.ncha.gov.cn/",
"icon": "https://www.ncha.gov.cn/favicon.ico"
},
- "国家中医药管理局":{
+ "国家中医药管理局": {
"name": "国家中医药管理局",
"categoryId": 14,
"url": "https://www.natcm.gov.cn/",
"icon": "https://www.natcm.gov.cn/favicon.ico"
},
- "国家外汇管理局":{
+ "国家外汇管理局": {
"name": "国家外汇管理局",
"categoryId": 14,
"url": "https://www.safe.gov.cn/",
"icon": "https://www.safe.gov.cn/favicon.ico"
},
- "国家药品监督管理局":{
+ "国家药品监督管理局": {
"name": "国家药品监督管理局",
"categoryId": 14,
"url": "https://www.nmpa.gov.cn/",
"icon": "https://www.nmpa.gov.cn/favicon.ico"
},
- "国家政务服务平台":{
+ "国家政务服务平台": {
"name": "国家政务服务平台",
"categoryId": 14,
"url": "https://gjzwfw.www.gov.cn/",
"icon": "https://gjzwfw.www.gov.cn/favicon.ico"
},
- "北京政务服务网":{
+ "北京政务服务网": {
"name": "北京政务服务网",
"categoryId": 14,
"url": "http://banshi.beijing.gov.cn/",
"icon": "http://banshi.beijing.gov.cn/favicon.ico"
},
- "上海政务服务网":{
+ "上海政务服务网": {
"name": "上海政务服务网",
"categoryId": 14,
"url": "http://zwdt.sh.gov.cn/govPortals/index.do",
"icon": "http://zwdt.sh.gov.cn/favicon.ico"
},
- "广东政务服务网":{
+ "广东政务服务网": {
"name": "广东政务服务网",
"categoryId": 14,
"url": "http://www.gdzwfw.gov.cn",
"icon": "http://www.gdzwfw.gov.cn/favicon.ico"
},
- "深圳政务服务网":{
+ "深圳政务服务网": {
"name": "深圳政务服务网",
"categoryId": 14,
"url": "https://zwfw.sz.gov.cn/",
"icon": "https://zwfw.sz.gov.cn/favicon.ico"
},
- "浙江政务服务网":{
+ "浙江政务服务网": {
"name": "浙江政务服务网",
"categoryId": 14,
"url": "http://www.zjzwfw.gov.cn",
"icon": "http://www.zjzwfw.gov.cn/favicon.ico"
},
- "江苏政务服务网":{
+ "江苏政务服务网": {
"name": "江苏政务服务网",
"categoryId": 14,
"url": "http://www.jszwfw.gov.cn",
"icon": "http://www.jszwfw.gov.cn/favicon.ico"
},
- "四川政务服务网":{
+ "四川政务服务网": {
"name": "四川政务服务网",
"categoryId": 14,
"url": "http://www.sczwfw.gov.cn",
"icon": "http://www.sczwfw.gov.cn/favicon.ico"
},
- "湖北政务服务网":{
+ "湖北政务服务网": {
"name": "湖北政务服务网",
"categoryId": 14,
"url": "http://zwfw.hubei.gov.cn",
"icon": "http://zwfw.hubei.gov.cn/favicon.ico"
},
- "湖南政务服务网":{
+ "湖南政务服务网": {
"name": "湖南政务服务网",
"categoryId": 14,
"url": "http://zwfw-new.hunan.gov.cn/",
"icon": "http://zwfw-new.hunan.gov.cn/favicon.ico"
},
- "山东政务服务网":{
+ "山东政务服务网": {
"name": "山东政务服务网",
"categoryId": 14,
"url": "http://www.shandong.gov.cn/",
"icon": "http://www.shandong.gov.cn/favicon.ico"
},
- "河南政务服务网":{
+ "河南政务服务网": {
"name": "河南政务服务网",
"categoryId": 14,
"url": "http://www.hnzwfw.gov.cn",
"icon": "http://www.hnzwfw.gov.cn/favicon.ico"
},
- "河北政务服务网":{
+ "河北政务服务网": {
"name": "河北政务服务网",
"categoryId": 14,
"url": "http://www.hbzwfw.gov.cn/",
"icon": "http://www.hbzwfw.gov.cn/favicon.ico"
},
- "福建政务服务网":{
+ "福建政务服务网": {
"name": "福建政务服务网",
"categoryId": 14,
"url": "http://zwfw.fujian.gov.cn",
"icon": "http://zwfw.fujian.gov.cn/favicon.ico"
},
- "安徽政务服务网":{
+ "安徽政务服务网": {
"name": "安徽政务服务网",
"categoryId": 14,
"url": "https://www.ahzwfw.gov.cn",
"icon": "https://www.ahzwfw.gov.cn/favicon.ico"
},
- "陕西政务服务网":{
+ "陕西政务服务网": {
"name": "陕西政务服务网",
"categoryId": 14,
"url": "https://zwfw.shaanxi.gov.cn/sx/public/index",
"icon": "https://zwfw.shaanxi.gov.cn/favicon.ico"
},
- "重庆政务服务网":{
+ "重庆政务服务网": {
"name": "重庆政务服务网",
"categoryId": 14,
"url": "http://zwykb.cq.gov.cn/",
"icon": "http://zwykb.cq.gov.cn/favicon.ico"
},
- "天津政务服务网":{
+ "天津政务服务网": {
"name": "天津政务服务网",
"categoryId": 14,
"url": "https://zwfw.tj.gov.cn/",
"icon": "https://zwfw.tj.gov.cn/favicon.ico"
},
- "辽宁政务服务网":{
+ "辽宁政务服务网": {
"name": "辽宁政务服务网",
"categoryId": 14,
"url": "http://www.lnzwfw.gov.cn",
"icon": "http://www.lnzwfw.gov.cn/favicon.ico"
},
- "吉林政务服务网":{
+ "吉林政务服务网": {
"name": "吉林政务服务网",
"categoryId": 14,
"url": "http://zwfw.jl.gov.cn/jlszwfw/",
"icon": "http://zwfw.jl.gov.cn/favicon.ico"
},
- "黑龙江政务服务网":{
+ "黑龙江政务服务网": {
"name": "黑龙江政务服务网",
"categoryId": 14,
"url": "http://zwfw.hlj.gov.cn/",
"icon": "http://zwfw.hlj.gov.cn/favicon.ico"
},
- "江西政务服务网":{
+ "江西政务服务网": {
"name": "江西政务服务网",
"categoryId": 14,
"url": "http://www.jxzwfww.gov.cn/",
"icon": "http://www.jxzwfww.gov.cn/favicon.ico"
},
- "广西政务服务网":{
+ "广西政务服务网": {
"name": "广西政务服务网",
"categoryId": 14,
"url": "http://zwfw.gxzf.gov.cn",
"icon": "http://zwfw.gxzf.gov.cn/favicon.ico"
},
- "海南政务服务网":{
+ "海南政务服务网": {
"name": "海南政务服务网",
"categoryId": 14,
"url": "https://wssp.hainan.gov.cn/",
"icon": "https://wssp.hainan.gov.cn/favicon.ico"
},
- "贵州政务服务网":{
+ "贵州政务服务网": {
"name": "贵州政务服务网",
"categoryId": 14,
"url": "https://zwfw.guizhou.gov.cn/index.html",
"icon": "https://zwfw.guizhou.gov.cn/favicon.ico"
},
- "云南政务服务网":{
+ "云南政务服务网": {
"name": "云南政务服务网",
"categoryId": 14,
"url": "https://zwfw.yn.gov.cn/portal/",
"icon": "https://zwfw.yn.gov.cn/favicon.ico"
},
- "西藏政务服务网":{
+ "西藏政务服务网": {
"name": "西藏政务服务网",
"categoryId": 14,
"url": "http://www.xzzwfw.gov.cn",
"icon": "http://www.xzzwfw.gov.cn/favicon.ico"
},
- "甘肃政务服务网":{
+ "甘肃政务服务网": {
"name": "甘肃政务服务网",
"categoryId": 14,
"url": "https://zwfw.gansu.gov.cn/",
"icon": "https://zwfw.gansu.gov.cn/favicon.ico"
},
- "青海政务服务网":{
+ "青海政务服务网": {
"name": "青海政务服务网",
"categoryId": 14,
"url": "https://www.qhzwfw.gov.cn/",
"icon": "https://www.qhzwfw.gov.cn/favicon.ico"
},
- "宁夏政务服务网":{
+ "宁夏政务服务网": {
"name": "宁夏政务服务网",
"categoryId": 14,
"url": "http://zwfw.nx.gov.cn",
"icon": "http://zwfw.nx.gov.cn/favicon.ico"
},
- "新疆政务服务网":{
+ "新疆政务服务网": {
"name": "新疆政务服务网",
"categoryId": 14,
"url": "https://zwfw.xinjiang.gov.cn/",
"icon": "https://zwfw.xinjiang.gov.cn/favicon.ico"
},
- "内蒙古政务服务网":{
+ "内蒙古政务服务网": {
"name": "内蒙古政务服务网",
"categoryId": 14,
"url": "http://zwfw.nmg.gov.cn",
"icon": "http://zwfw.nmg.gov.cn/favicon.ico"
},
- "山西政务服务网":{
+ "山西政务服务网": {
"name": "山西政务服务网",
"categoryId": 14,
"url": "https://www.sxzwfw.gov.cn/",
"icon": "https://www.sxzwfw.gov.cn/favicon.ico"
},
- "新疆生产建设兵团政务服务网":{
+ "新疆生产建设兵团政务服务网": {
"name": "兵团政务服务网",
"categoryId": 14,
"url": "https://zwfw.xjbt.gov.cn/",
"icon": "https://zwfw.xjbt.gov.cn/favicon.ico"
},
- "香港特区政府一站通":{
+ "香港特区政府一站通": {
"name": "香港特区政府一站通",
"categoryId": 14,
"url": "https://www.gov.hk/",
"icon": "https://www.gov.hk/favicon.ico"
},
- "澳门特区政府":{
+ "澳门特区政府": {
"name": "澳门特区政府",
"categoryId": 14,
"url": "https://www.gov.mo/",
"icon": "https://www.gov.mo/favicon.ico"
},
- "台湾地区政府网站":{
+ "台湾地区政府网站": {
"name": "台湾地区政府网站",
"categoryId": 14,
"url": "https://www.ey.gov.tw/",
"icon": "https://www.ey.gov.tw/favicon.ico"
},
- "国家政务服务平台网站":{
+ "国家政务服务平台网站": {
"name": "国家政务服务平台",
"categoryId": 14,
"url": "https://gjzwfw.www.gov.cn/",
"icon": "https://gjzwfw.www.gov.cn/favicon.ico"
}
},
- "company": "国务院办公厅"
+ "company": "国务院办公厅"
},
- "优酷":{
- "优酷":{
- "name": "优酷视频",
- "categoryId": 8,
- "url": "https://www.youku.com/",
- "icon": "https://www.youku.com/favicon.ico"
+ "优酷": {
+ "优酷": {
+ "name": "优酷视频",
+ "categoryId": 8,
+ "url": "https://www.youku.com/",
+ "icon": "https://www.youku.com/favicon.ico"
},
- "统一数据采集域名":{
- "name": "优酷客户端统一数据采集",
- "categoryId": 8,
- "url": {
- "1": "pcapp-data.youku.com",
- "2": "data.youku.com",
- "3": "pcapp-data-collect.youku.com"
- },
- "icon": "https://www.youku.com/favicon.ico"
+ "统一数据采集域名": {
+ "name": "优酷客户端统一数据采集",
+ "categoryId": 8,
+ "url": {
+ "1": "pcapp-data.youku.com",
+ "2": "data.youku.com",
+ "3": "pcapp-data-collect.youku.com"
+ },
+ "icon": "https://www.youku.com/favicon.ico"
},
- "company": "优酷信息技术(北京)有限公司"
+ "company": "优酷信息技术(北京)有限公司"
},
- "Steam":{
+ "Steam": {
"Steam商店官网": {
"name": "Steam商店官网",
- "categoryId": 0,
+ "categoryId": 26,
"url": "store.steampowered.com",
"icon": "https://store.steampowered.com/favicon.ico"
},
"Steam全球官网": {
"name": "Steam全球官网",
- "categoryId": 0,
+ "categoryId": 26,
"url": "www.steampowered.com",
"icon": "https://www.steampowered.com/favicon.ico"
},
"Steam中国官网": {
"name": "Steam中国官网",
- "categoryId": 0,
+ "categoryId": 26,
"url": "store.steamchina.com",
"icon": "https://store.steamchina.com/favicon.ico"
},
@@ -2646,6 +3222,12 @@
"url": "steamgames.com",
"icon": "https://store.steampowered.com/favicon.ico"
},
+ "Steam P2P节点发现服务器": {
+ "name": "Steam P2P节点发现服务器/游戏联机P2P连接路由服务",
+ "categoryId": 2,
+ "url": "discovery.steamserver.net",
+ "icon": "https://store.steampowered.com/favicon.ico"
+ },
"Steam客服帮助中心": {
"name": "Steam客服帮助中心",
"categoryId": 6,
@@ -2689,6 +3271,750 @@
"icon": "https://store.steampowered.com/favicon.ico"
},
"company": "Valve Corporation"
+ },
+ "搜狗": {
+ "搜狗搜索": {
+ "name": "搜狗搜索",
+ "categoryId": 0,
+ "url": "www.sogou.com",
+ "icon": "https://www.sogou.com/favicon.ico"
+ },
+ "云端配置同步与策略下发服务": {
+ "name": "云端配置同步与策略下发服务",
+ "categoryId": 22,
+ "url": "config.pinyin.sogou.com",
+ "icon": "https://www.sogou.com/favicon.ico"
+ },
+ "信息发布与版本 / 词库更新服务": {
+ "name": "信息发布与版本 / 词库更新服务",
+ "categoryId": 22,
+ "url": "info.pinyin.sogou.com",
+ "icon": "https://www.sogou.com/favicon.ico"
+ },
+ "网络连通性检测服务": {
+ "name": "网络连通性检测服务",
+ "categoryId": 22,
+ "url": "ping.pinyin.sogou.com",
+ "icon": "https://pinyin.sogou.com/favicon.ico"
+ },
+ "账号个人信息与用户画像管理服务": {
+ "name": "账号个人信息与用户画像管理服务",
+ "categoryId": 22,
+ "url": {
+ "1": "profile.pinyin.sogou.com",
+ "2": "pc.profile.pinyin.sogou.com"
+ },
+ "icon": "https://pinyin.sogou.com/favicon.ico"
+ },
+ "company": "搜狗科技有限公司"
+ },
+ "天星金融": {
+ "天星金融风控传感器服务": {
+ "name": "天星金融风控传感器服务",
+ "categoryId": 23,
+ "url": "sensor.jr.airstarfinance.net",
+ "icon": "https://lf3-appstore-sign.bytedance.net/obj/rc-client-assets/favicon.ico"
+ },
+ "company": "天星金融"
+ },
+ "InfoQ": {
+ "InfoQ 全球站": {
+ "name": "InfoQ 全球站",
+ "categoryId": 3,
+ "url": "https://www.infoq.com",
+ "icon": "https://static001.infoq.cn/static/infoq/www/img/share-default-5tgbiuhgfefgujjhg.png"
+ },
+ "InfoQ 中文站": {
+ "name": "InfoQ 中文站",
+ "categoryId": 3,
+ "url": "https://www.infoq.cn",
+ "icon": "https://static001.infoq.cn/static/infoq/www/img/share-default-5tgbiuhgfefgujjhg.png"
+ },
+ "InfoQ 中文站备用域名": {
+ "name": "InfoQ 中文站备用域名",
+ "categoryId": 3,
+ "url": "https://www.infoq.com.cn",
+ "icon": "https://static001.infoq.cn/static/infoq/www/img/share-default-5tgbiuhgfefgujjhg.png"
+ },
+ "InfoQ 中文站静态资源 CDN 服务": {
+ "name": "InfoQ 中文站静态资源 CDN 服务",
+ "categoryId": 12,
+ "url": "https://static001.infoq.cn",
+ "icon": "https://static001.infoq.cn/static/infoq/www/img/share-default-5tgbiuhgfefgujjhg.png"
+ },
+ "company": "极客邦控股(北京)有限公司"
+ },
+ "51CTO": {
+ "51CTO 中文站": {
+ "name": "51CTO 中文站",
+ "categoryId": 3,
+ "url": "https://www.51cto.com",
+ "icon": "https://www.51cto.com/favicon.ico"
+ },
+ "51CTO 全站搜索服务": {
+ "name": "51CTO 全站搜索服务",
+ "categoryId": 13,
+ "url": "https://sc.51cto.com",
+ "icon": "https://www.51cto.com/favicon.ico"
+ },
+ "51CTO 云计算频道": {
+ "name": "51CTO 云计算频道",
+ "categoryId": 10,
+ "url": "https://cloud.51cto.com",
+ "icon": "https://www.51cto.com/favicon.ico"
+ },
+ "51CTO 数据库频道": {
+ "name": "51CTO 数据库频道",
+ "categoryId": 11,
+ "url": "https://database.51cto.com",
+ "icon": "https://www.51cto.com/favicon.ico"
+ },
+ "51CTO 认证考试": {
+ "name": "51CTO 认证考试",
+ "categoryId": 22,
+ "url": "https://exam.51cto.com",
+ "icon": "https://www.51cto.com/favicon.ico"
+ },
+ "51CTO 学院": {
+ "name": "51CTO 学院",
+ "categoryId": 22,
+ "url": "https://edu.51cto.com",
+ "icon": "https://www.51cto.com/favicon.ico"
+ },
+ "51CTO 专题活动": {
+ "name": "51CTO 专题活动",
+ "categoryId": 3,
+ "url": "https://special.51cto.com",
+ "icon": "https://www.51cto.com/favicon.ico"
+ },
+ "51CTO 专家博客": {
+ "name": "51CTO 专家博客",
+ "categoryId": 3,
+ "url": "https://blog.51cto.com",
+ "icon": "https://www.51cto.com/favicon.ico"
+ },
+ "51CTO 服务器频道": {
+ "name": "51CTO 服务器频道",
+ "categoryId": 10,
+ "url": "https://server.51cto.com",
+ "icon": "https://www.51cto.com/favicon.ico"
+ },
+ "51CTO 其他业务": {
+ "name": "51CTO 其他业务",
+ "categoryId": 3,
+ "url": "https://other.51cto.com",
+ "icon": "https://www.51cto.com/favicon.ico"
+ },
+ "51CTO 精品班": {
+ "name": "51CTO 精品班",
+ "categoryId": 23,
+ "url": "https://e.51cto.com/",
+ "icon": "https://www.51cto.com/favicon.ico"
+ },
+ "51CTO API 服务": {
+ "name": "51CTO API 服务",
+ "categoryId": 17,
+ "url": "https://apie.51cto.com",
+ "icon": "https://www.51cto.com/favicon.ico"
+ },
+ "51CTO CDN": {
+ "name": "51CTO静态资源CDN",
+ "categoryId": 2,
+ "url": {
+ "1": "51ctocdn.cn",
+ "2": "51cto.com"
+ },
+ "icon": "https://www.51cto.com/favicon.ico"
+ },
+ "company": "北京无忧创想信息技术有限公司"
+ },
+ "知乎": {
+ "知乎主站": {
+ "name": "知乎主站",
+ "categoryId": 1,
+ "url": "https://www.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎移动端H5": {
+ "name": "知乎移动端H5",
+ "categoryId": 1,
+ "url": "https://m.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎学术版": {
+ "name": "知乎学术版",
+ "categoryId": 1,
+ "url": "https://www.zhihu.com/academic",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎公共API": {
+ "name": "知乎公共API",
+ "categoryId": 1,
+ "url": "https://api.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎日报API": {
+ "name": "知乎日报API",
+ "categoryId": 11,
+ "url": "https://news-at.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎内容流API": {
+ "name": "知乎内容流API",
+ "categoryId": 11,
+ "url": "https://api.zhihu.com/feed",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎数据中心API": {
+ "name": "知乎数据中心API",
+ "categoryId": 11,
+ "url": "https://datahub.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎搜索API": {
+ "name": "知乎搜索API",
+ "categoryId": 11,
+ "url": "https://api.zhihu.com/search",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎广告API": {
+ "name": "知乎广告API",
+ "categoryId": 11,
+ "url": "https://api.ad.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎静态资源CDN": {
+ "name": "知乎静态资源CDN",
+ "categoryId": 12,
+ "url": "https://static.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎图片CDN": {
+ "name": "知乎图片CDN",
+ "categoryId": 2,
+ "url": "https://zhimg.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎高清图片CDN": {
+ "name": "知乎高清图片CDN",
+ "categoryId": 12,
+ "url": "https://pica.zhimg.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎npm包CDN": {
+ "name": "知乎npm包CDN",
+ "categoryId": 12,
+ "url": "https://unpkg.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎专栏": {
+ "name": "知乎专栏",
+ "categoryId": 3,
+ "url": "https://zhuanlan.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎创作者平台": {
+ "name": "知乎创作者平台",
+ "categoryId": 3,
+ "url": "https://creator.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎学堂": {
+ "name": "知乎学堂",
+ "categoryId": 9,
+ "url": "https://zhixuetang.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎账号认证": {
+ "name": "知乎账号认证",
+ "categoryId": 23,
+ "url": "https://passport.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎Live直播": {
+ "name": "知乎Live直播",
+ "categoryId": 5,
+ "url": "https://live.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎好物电商": {
+ "name": "知乎好物电商",
+ "categoryId": 10,
+ "url": "https://market.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎广告平台": {
+ "name": "知乎广告平台",
+ "categoryId": 11,
+ "url": "https://ad.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎用户品牌保护": {
+ "name": "知乎用户品牌保护",
+ "categoryId": 25,
+ "url": "https://zhihuer.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎创作者品牌保护": {
+ "name": "知乎创作者品牌保护",
+ "categoryId": 25,
+ "url": "https://zhihuzhe.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎Web端MQTT实时消息网关": {
+ "name": "知乎Web端MQTT实时消息网关",
+ "categoryId": 18,
+ "url": "mqtt-web.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎网页端数据分析服务": {
+ "name": "知乎网页端数据分析服务",
+ "categoryId": 23,
+ "url": "zhihu-web-analytics.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎静态资源备用服务": {
+ "name": "知乎静态资源备用服务",
+ "categoryId": 25,
+ "url": "sugar.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎应用崩溃日志收集服务": {
+ "name": "知乎应用崩溃日志收集服务",
+ "categoryId": 24,
+ "url": "crash2.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎验证码服务": {
+ "name": "知乎验证码服务",
+ "categoryId": 22,
+ "url": "captcha.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "知乎应用性能管理服务": {
+ "name": "知乎应用性能管理服务",
+ "categoryId": 26,
+ "url": "apm.zhihu.com",
+ "icon": "https://static.zhihu.com/heifetz/favicon.ico"
+ },
+ "company": "北京春田知韵科技有限公司"
+ },
+ "epicgames": {
+ "游戏商店": {
+ "name": "Epic游戏商店",
+ "categoryId": 26,
+ "url": "store.epicgames.com",
+ "icon": "https://static-assets-prod.epicgames.com/epic-store/static/favicon.ico"
+ },
+ "EpicGames": {
+ "name": "Epic Games官网",
+ "categoryId": 26,
+ "url": "www.epicgames.com",
+ "icon": "https://static-assets-prod.epicgames.com/epic-store/static/favicon.ico"
+ },
+ "Epic Games用户行为跟踪服务": {
+ "name": "Epic Games用户行为跟踪服务",
+ "categoryId": 25,
+ "url": "tracker.epicgames.com",
+ "icon": "https://static-assets-prod.epicgames.com/epic-store/static/favicon.ico"
+ },
+ "Epic CDN": {
+ "name": "Epic CDN",
+ "categoryId": 2,
+ "url": {
+ "1": "cdn1.epicgames.com",
+ "2": "cdn2.epicgames.com",
+ "3": "cdn.epicgames.com"
+ },
+ "icon": "https://static-assets-prod.epicgames.com/epic-store/static/favicon.ico"
+ },
+ "Epic商店静态后端IPv4 API": {
+ "name": "Epic商店静态后端IPv4 API",
+ "categoryId": 27,
+ "url": "store-site-backend-static-ipv4.ak.epicgames.com",
+ "icon": "https://static-assets-prod.epicgames.com/epic-store/static/favicon.ico"
+ },
+ "Epic Games生产环境静态资源服务器": {
+ "name": "Epic Games生产环境静态资源服务器",
+ "categoryId": 27,
+ "url": "static-assets-prod.epicgames.com",
+ "icon": "https://static-assets-prod.epicgames.com/epic-store/static/favicon.ico"
+ },
+ "Epic ECOSEC Nelly安全服务(Cloudflare防护)": {
+ "name": "Epic ECOSEC Nelly安全服务(Cloudflare防护)",
+ "categoryId": 21,
+ "url": "nelly-service-prod-cloudflare.ecosec.on.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic启动器公共服务(Cloudflare加速)": {
+ "name": "Epic启动器公共服务(Cloudflare加速)",
+ "categoryId": 19,
+ "url": "launcher-public-service-prod-cloudflare.ol.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic CDN Cloudflare专属节点": {
+ "name": "Epic CDN Cloudflare专属节点",
+ "categoryId": 18,
+ "url": "cloudflare.epicgamescdn.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic账号服务接口": {
+ "name": "Epic账号服务接口/账号验证与登录核心API",
+ "categoryId": 24,
+ "url": "account-public-service-prod03.ol.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic启动器排队服务接口": {
+ "name": "Epic启动器排队服务接口/启动器排队限流API",
+ "categoryId": 24,
+ "url": "launcherwaitingroom-public-service-prod06.ol.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic启动器核心服务接口": {
+ "name": "Epic启动器核心服务接口/启动器运行相关API",
+ "categoryId": 24,
+ "url": "launcher-public-service-prod06.ol.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "EpicGames官方网站": {
+ "name": "EpicGames官方网站/Epic主站及品牌展示",
+ "categoryId": 26,
+ "url": "www.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic启动器官网服务": {
+ "name": "Epic启动器官网服务/启动器网页端入口",
+ "categoryId": 26,
+ "url": "launcher-website-prod07.ol.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic用户行为跟踪": {
+ "name": "Epic用户行为跟踪/用户操作数据统计分析",
+ "categoryId": 27,
+ "url": "tracking.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic启动器账号登录页": {
+ "name": "Epic启动器账号登录页/启动器端账号验证入口",
+ "categoryId": 26,
+ "url": "accounts.launcher-website-prod07.ol.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic账号中心": {
+ "name": "Epic账号中心/账号信息管理与安全验证",
+ "categoryId": 26,
+ "url": "accounts.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "虚幻引擎CDN节点1": {
+ "name": "虚幻引擎CDN节点1/虚幻引擎资源分发加速",
+ "categoryId": 2,
+ "url": "cdn1.unrealengine.com",
+ "icon": "https://www.unrealengine.com/favicon.ico"
+ },
+ "虚幻引擎CDN节点2": {
+ "name": "虚幻引擎CDN节点2/虚幻引擎资源分发加速",
+ "categoryId": 2,
+ "url": "cdn2.unrealengine.com",
+ "icon": "https://www.unrealengine.com/favicon.ico"
+ },
+ "Epic数据路由分发": {
+ "name": "Epic数据路由分发/业务数据中转与统计上报",
+ "categoryId": 27,
+ "url": "datarouter.ol.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic授权服务接口": {
+ "name": "Epic授权服务接口/游戏权限与授权验证API",
+ "categoryId": 24,
+ "url": "entitlement-public-service-prod08.ol.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic订单处理接口": {
+ "name": "Epic订单处理接口/商城订单支付结算API",
+ "categoryId": 24,
+ "url": "orderprocessor-public-service-ecomprod01.ol.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic游戏库服务接口": {
+ "name": "Epic游戏库服务接口/游戏商城目录查询API",
+ "categoryId": 24,
+ "url": "catalog-public-service-prod06.ol.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic好友服务接口": {
+ "name": "Epic好友服务接口/好友列表及联机状态API",
+ "categoryId": 24,
+ "url": "friends-public-service-prod06.ol.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic服务器状态接口": {
+ "name": "Epic服务器状态接口/游戏及服务运行状态API",
+ "categoryId": 24,
+ "url": "lightswitch-public-service-prod06.ol.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic账号门户": {
+ "name": "Epic账号门户/账号相关服务网页入口",
+ "categoryId": 26,
+ "url": "accountportal-website-prod07.ol.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic虚幻竞技场服务接口": {
+ "name": "Epic虚幻竞技场服务接口/UT游戏专属API",
+ "categoryId": 24,
+ "url": "ut-public-service-prod10.ol.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic游戏下载节点1": {
+ "name": "Epic游戏下载节点1/游戏安装包主下载地址",
+ "categoryId": 26,
+ "url": "download.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic游戏下载节点2": {
+ "name": "Epic游戏下载节点2/游戏安装包备用下载地址",
+ "categoryId": 26,
+ "url": "download2.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic游戏下载节点3": {
+ "name": "Epic游戏下载节点3/游戏安装包备用下载地址",
+ "categoryId": 26,
+ "url": "download3.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic游戏下载节点4": {
+ "name": "Epic游戏下载节点4/游戏安装包备用下载地址",
+ "categoryId": 26,
+ "url": "download4.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic静态资源存储": {
+ "name": "Epic静态资源存储/网页及游戏静态文件分发",
+ "categoryId": 2,
+ "url": "static-assets-prod.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic商城后端静态资源": {
+ "name": "Epic商城后端静态资源/商城页面静态文件加速",
+ "categoryId": 2,
+ "url": "store-site-backend-static.ak.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic商城内容分发": {
+ "name": "Epic商城内容分发/商城商品图片及素材加速",
+ "categoryId": 2,
+ "url": "store-content.ak.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic游戏库存储服务": {
+ "name": "Epic游戏库存储服务/用户游戏库数据云端存储",
+ "categoryId": 21,
+ "url": "library-service.live.use1a.on.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic数据存储服务": {
+ "name": "Epic数据存储服务/业务数据及用户文件云端存储",
+ "categoryId": 21,
+ "url": "datastorage-public-service-liveegs.live.use1a.on.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic Fastly下载加速": {
+ "name": "Epic Fastly下载加速/FastlyCDN游戏下载节点",
+ "categoryId": 2,
+ "url": "fastly-download.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic游戏商城": {
+ "name": "Epic游戏商城/游戏购买及领取主页面",
+ "categoryId": 26,
+ "url": "store.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "Epic启动器商城入口": {
+ "name": "Epic启动器商城入口/启动器端商城访问地址",
+ "categoryId": 26,
+ "url": "launcher.store.epicgames.com",
+ "icon": "https://www.epicgames.com/favicon.ico"
+ },
+ "company": "Epic Games, Inc"
+ },
+ "Cloudflare": {
+ "Cloudflare官网": {
+ "name": "Cloudflare",
+ "categoryId": 25,
+ "url": {
+ "1": "www.cloudflare.net",
+ "2": "www.cloudflare.com",
+ "3": "cloudflare.com",
+ "4": "cloudflare.net"
+ },
+ "icon": "https://www.cloudflare.com/favicon.ico"
+ },
+ "Cloudflare DNS": {
+ "name": "Cloudflare DNS",
+ "categoryId": 25,
+ "url": {
+ "1": "dns1.cloudflare.com",
+ "2": "dns2.cloudflare.com"
+ },
+ "icon": "https://www.cloudflare.com/favicon.ico"
+ },
+ "Cloudflare权威DNS服务器": {
+ "name": "Cloudflare权威DNS服务器",
+ "categoryId": 2,
+ "url": "ns.cloudflare.com",
+ "icon": "https://www.cloudflare.com/favicon.ico"
+ },
+ "Cloudflare Workers平台": {
+ "name": "Cloudflare Workers平台",
+ "categoryId": 22,
+ "url": "workers.cloudflare.com",
+ "icon": "https://www.cloudflare.com/favicon.ico"
+ },
+ "Cloudflare Pages托管服务": {
+ "name": "Cloudflare Pages托管服务",
+ "categoryId": 18,
+ "url": "pages.cloudflare.com",
+ "icon": "https://www.cloudflare.com/favicon.ico"
+ },
+ "Cloudflare管理API": {
+ "name": "Cloudflare管理API",
+ "categoryId": 22,
+ "url": "api.cloudflare.com",
+ "icon": "https://www.cloudflare.com/favicon.ico"
+ },
+ "Cloudflare One零信任平台": {
+ "name": "Cloudflare One零信任平台",
+ "categoryId": 21,
+ "url": "one.cloudflare.com",
+ "icon": "https://www.cloudflare.com/favicon.ico"
+ },
+ "Cloudflare for Teams服务": {
+ "name": "Cloudflare for Teams服务",
+ "categoryId": 21,
+ "url": "teams.cloudflare.com",
+ "icon": "https://www.cloudflare.com/favicon.ico"
+ },
+ "Cloudflare网络雷达": {
+ "name": "Cloudflare网络雷达",
+ "categoryId": 25,
+ "url": "radar.cloudflare.com",
+ "icon": "https://www.cloudflare.com/favicon.ico"
+ },
+ "Cloudflare辅助DNS服务": {
+ "name": "Cloudflare辅助DNS服务",
+ "categoryId": 17,
+ "url": "secondary.cloudflare.com",
+ "icon": "https://www.cloudflare.com/favicon.ico"
+ },
+ "Cloudflare Workers预览域": {
+ "name": "Cloudflare Workers预览域",
+ "categoryId": 22,
+ "url": "workers.dev",
+ "icon": "https://www.cloudflare.com/favicon.ico"
+ },
+ "Cloudflare官网运营技术支持服务": {
+ "name": "Cloudflare官网运营技术支持服务",
+ "categoryId": 23,
+ "url": "ot.www.cloudflare.com",
+ "icon": "https://www.cloudflare.com/favicon.ico"
+ },
+ "Cloudflare安全挑战服务": {
+ "name": "Cloudflare安全挑战服务",
+ "categoryId": 21,
+ "url": "challenges.cloudflare.com",
+ "icon": "https://www.cloudflare.com/favicon.ico"
+ },
+ "company": "Cloudflare, Inc"
+ },
+ "hcaptcha": {
+ "hCaptcha主站与管理平台": {
+ "name": "hCaptcha主站与管理平台",
+ "categoryId": 21,
+ "url": "hcaptcha.com",
+ "icon": "https://www.hcaptcha.com/images/64da82f6bf67de1b1278925f_hcaptcha-logo-hand.png"
+ },
+ "hCaptcha核心验证API服务": {
+ "name": "hCaptcha核心验证API服务",
+ "categoryId": 21,
+ "url": "api.hcaptcha.com",
+ "icon": "https://www.hcaptcha.com/images/64da82f6bf67de1b1278925f_hcaptcha-logo-hand.png"
+ },
+ "hCaptcha JavaScript API服务": {
+ "name": "hCaptcha JavaScript API服务",
+ "categoryId": 21,
+ "url": "js.hcaptcha.com",
+ "icon": "https://www.hcaptcha.com/favicon.ico"
+ },
+ "hCaptcha静态资源CDN": {
+ "name": "hCaptcha静态资源CDN",
+ "categoryId": 18,
+ "url": "newassets.hcaptcha.com",
+ "icon": "https://www.hcaptcha.com/favicon.ico"
+ },
+ "hCaptcha挑战页面服务": {
+ "name": "hCaptcha挑战页面服务",
+ "categoryId": 21,
+ "url": "challenges.hcaptcha.com",
+ "icon": "https://www.hcaptcha.com/favicon.ico"
+ },
+ "hCaptcha验证图片资源库": {
+ "name": "hCaptcha验证图片资源库",
+ "categoryId": 18,
+ "url": "imgs.hcaptcha.com",
+ "icon": "https://www.hcaptcha.com/favicon.ico"
+ },
+ "hCaptcha管理控制台": {
+ "name": "hCaptcha管理控制台",
+ "categoryId": 21,
+ "url": "dashboard.hcaptcha.com",
+ "icon": "https://www.hcaptcha.com/favicon.ico"
+ },
+ "hCaptcha开发者文档中心": {
+ "name": "hCaptcha开发者文档中心",
+ "categoryId": 21,
+ "url": "docs.hcaptcha.com",
+ "icon": "https://www.hcaptcha.com/favicon.ico"
+ },
+ "company": "Intuition Machines, Inc."
+ },
+ "unpkg": {
+ "Unpkg前端公共CDN": {
+ "name": "Unpkg前端公共CDN/全球NPM包静态资源极速分发加速",
+ "categoryId": 2,
+ "url": "unpkg.com",
+ "icon": "https://unpkg.com/favicon.jpg"
+ },
+ "Unpkg北美节点CDN": {
+ "name": "Unpkg北美节点CDN/海外NPM包资源分发加速",
+ "categoryId": 2,
+ "url": "unpkg-cdn.com",
+ "icon": "https://unpkg.com/favicon.jpg"
+ },
+ "Unpkg国内加速节点": {
+ "name": "Unpkg国内加速节点/大陆地区NPM包资源极速访问",
+ "categoryId": 2,
+ "url": "unpkg.zhimg.com",
+ "icon": "https://unpkg.com/favicon.jpg"
+ },
+ "Unpkg官方子域CDN": {
+ "name": "Unpkg官方子域CDN/静态资源分流加速节点",
+ "categoryId": 2,
+ "url": "cdn.unpkg.com",
+ "icon": "https://unpkg.com/favicon.ico"
+ }
+ },
+ "UserCSS前端资源分享平台": {
+ "name": "UserCSS前端资源分享平台/免费纯CSS UI组件与特效模板库",
+ "categoryId": 1,
+ "url": {
+ "1": "www.userscss.top",
+ "2": "userscss.top",
+ "icon": "#"
+ },
+ "company": "未知/个人"
}
}
}
\ No newline at end of file
diff --git a/static/index.html b/static/index.html
index 73e9509..4626132 100644
--- a/static/index.html
+++ b/static/index.html
@@ -7,7 +7,7 @@
-
+
@@ -1086,20 +1086,36 @@
屏蔽配置
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
GFWList配置
+
+
+
+
+
+
+
diff --git a/static/js/config.js b/static/js/config.js
index 020da0f..8354baa 100644
--- a/static/js/config.js
+++ b/static/js/config.js
@@ -74,6 +74,10 @@ function populateConfigForm(config) {
//setElementValue('shield-hosts-file', getSafeValue(shieldConfig.HostsFile, 'data/hosts.txt'));
// 使用服务器端接受的屏蔽方法值,默认使用NXDOMAIN, 可选值: NXDOMAIN, NULL, REFUSED
setElementValue('shield-block-method', getSafeValue(shieldConfig.BlockMethod, 'NXDOMAIN'));
+ setElementValue('shield-custom-block-ip', getSafeValue(shieldConfig.CustomBlockIP, ''));
+ // GFWList配置
+ setElementValue('shield-gfwlist-ip', getSafeValue(shieldConfig.GFWListIP, ''));
+ setElementValue('shield-gfwlist-content', getSafeValue(shieldConfig.GFWListContent, ''));
}
// 工具函数:安全设置元素值
@@ -197,7 +201,10 @@ function collectFormData() {
},
shield: {
updateInterval: updateInterval,
- blockMethod: getElementValue('shield-block-method') || 'NXDOMAIN'
+ blockMethod: getElementValue('shield-block-method') || 'NXDOMAIN',
+ customBlockIP: getElementValue('shield-custom-block-ip'),
+ gfwListIP: getElementValue('shield-gfwlist-ip'),
+ gfwListContent: getElementValue('shield-gfwlist-content')
}
};
}
@@ -205,9 +212,14 @@ function collectFormData() {
// 工具函数:安全获取元素值
function getElementValue(elementId) {
const element = document.getElementById(elementId);
- if (element && element.tagName === 'INPUT') {
- if (element.type === 'checkbox') {
- return element.checked;
+ if (element) {
+ if (element.tagName === 'INPUT') {
+ if (element.type === 'checkbox') {
+ return element.checked;
+ }
+ return element.value;
+ } else if (element.tagName === 'TEXTAREA') {
+ return element.value;
}
return element.value;
}
diff --git a/static/js/dashboard.js b/static/js/dashboard.js
index b432370..3415346 100644
--- a/static/js/dashboard.js
+++ b/static/js/dashboard.js
@@ -162,7 +162,19 @@ function processRealTimeData(stats) {
// 计算响应时间趋势
let responsePercent = '---';
let trendClass = 'text-gray-400';
- let trendIcon = '---';
+ let trendIcon = '•';
+
+ // 查找箭头元素
+ const responseTimePercentElem = document.getElementById('response-time-percent');
+ let parent = null;
+ let arrowIcon = null;
+
+ if (responseTimePercentElem) {
+ parent = responseTimePercentElem.parentElement;
+ if (parent) {
+ arrowIcon = parent.querySelector('.fa-arrow-up, .fa-arrow-down, .fa-circle');
+ }
+ }
if (stats.avgResponseTime !== undefined && stats.avgResponseTime !== null) {
// 首次加载时初始化历史数据,不计算趋势
@@ -171,6 +183,10 @@ function processRealTimeData(stats) {
responsePercent = '0.0%';
trendIcon = '•';
trendClass = 'text-gray-500';
+ if (arrowIcon) {
+ arrowIcon.className = 'fa fa-circle mr-1 text-xs';
+ parent.className = 'text-gray-500 text-sm flex items-center';
+ }
} else {
const prevResponseTime = window.dashboardHistoryData.prevResponseTime;
@@ -178,16 +194,36 @@ function processRealTimeData(stats) {
const changePercent = ((stats.avgResponseTime - prevResponseTime) / prevResponseTime) * 100;
responsePercent = Math.abs(changePercent).toFixed(1) + '%';
- // 设置趋势图标和颜色
+ // 处理-0.0%的情况
+ if (responsePercent === '-0.0%') {
+ responsePercent = '0.0%';
+ }
+
+ // 根据用户要求:数量下降显示红色箭头,上升显示绿色箭头
if (changePercent > 0) {
- trendIcon = '↓';
- trendClass = 'text-danger';
- } else if (changePercent < 0) {
+ // 响应时间增加,数值上升
trendIcon = '↑';
trendClass = 'text-success';
+ if (arrowIcon) {
+ arrowIcon.className = 'fa fa-arrow-up mr-1';
+ parent.className = 'text-success text-sm flex items-center';
+ }
+ } else if (changePercent < 0) {
+ // 响应时间减少,数值下降
+ trendIcon = '↓';
+ trendClass = 'text-danger';
+ if (arrowIcon) {
+ arrowIcon.className = 'fa fa-arrow-down mr-1';
+ parent.className = 'text-danger text-sm flex items-center';
+ }
} else {
+ // 趋势为0时,显示圆点图标
trendIcon = '•';
trendClass = 'text-gray-500';
+ if (arrowIcon) {
+ arrowIcon.className = 'fa fa-circle mr-1 text-xs';
+ parent.className = 'text-gray-500 text-sm flex items-center';
+ }
}
// 更新历史数据
@@ -196,7 +232,6 @@ function processRealTimeData(stats) {
}
document.getElementById('avg-response-time').textContent = responseTime;
- const responseTimePercentElem = document.getElementById('response-time-percent');
if (responseTimePercentElem) {
responseTimePercentElem.textContent = trendIcon + ' ' + responsePercent;
responseTimePercentElem.className = `text-sm flex items-center ${trendClass}`;
@@ -227,7 +262,19 @@ function processRealTimeData(stats) {
// 计算活跃IP趋势
let ipsPercent = '---';
let trendClass = 'text-gray-400';
- let trendIcon = '---';
+ let trendIcon = '•';
+
+ // 查找箭头元素
+ const activeIpsPercentElem = document.getElementById('active-ips-percent');
+ let parent = null;
+ let arrowIcon = null;
+
+ if (activeIpsPercentElem) {
+ parent = activeIpsPercentElem.parentElement;
+ if (parent) {
+ arrowIcon = parent.querySelector('.fa-arrow-up, .fa-arrow-down, .fa-circle');
+ }
+ }
if (stats.activeIPs !== undefined) {
const prevActiveIPs = window.dashboardHistoryData.prevActiveIPs;
@@ -238,20 +285,43 @@ function processRealTimeData(stats) {
ipsPercent = '0.0%';
trendIcon = '•';
trendClass = 'text-gray-500';
+ if (arrowIcon) {
+ arrowIcon.className = 'fa fa-circle mr-1 text-xs';
+ parent.className = 'text-gray-500 text-sm flex items-center';
+ }
} else {
if (prevActiveIPs > 0) {
const changePercent = ((stats.activeIPs - prevActiveIPs) / prevActiveIPs) * 100;
ipsPercent = Math.abs(changePercent).toFixed(1) + '%';
+ // 处理-0.0%的情况
+ if (ipsPercent === '-0.0%') {
+ ipsPercent = '0.0%';
+ }
+
+ // 根据用户要求:数量下降显示红色箭头,上升显示绿色箭头
if (changePercent > 0) {
trendIcon = '↑';
- trendClass = 'text-primary';
+ trendClass = 'text-success';
+ if (arrowIcon) {
+ arrowIcon.className = 'fa fa-arrow-up mr-1';
+ parent.className = 'text-success text-sm flex items-center';
+ }
} else if (changePercent < 0) {
trendIcon = '↓';
- trendClass = 'text-secondary';
+ trendClass = 'text-danger';
+ if (arrowIcon) {
+ arrowIcon.className = 'fa fa-arrow-down mr-1';
+ parent.className = 'text-danger text-sm flex items-center';
+ }
} else {
+ // 趋势为0时,显示圆点图标
trendIcon = '•';
trendClass = 'text-gray-500';
+ if (arrowIcon) {
+ arrowIcon.className = 'fa fa-circle mr-1 text-xs';
+ parent.className = 'text-gray-500 text-sm flex items-center';
+ }
}
}
@@ -261,7 +331,6 @@ function processRealTimeData(stats) {
}
document.getElementById('active-ips').textContent = activeIPs;
- const activeIpsPercentElem = document.getElementById('active-ips-percentage');
if (activeIpsPercentElem) {
activeIpsPercentElem.textContent = trendIcon + ' ' + ipsPercent;
activeIpsPercentElem.className = `text-sm flex items-center ${trendClass}`;
@@ -627,7 +696,19 @@ async function loadDashboardData() {
// 计算响应时间趋势
let responsePercent = '---';
let trendClass = 'text-gray-400';
- let trendIcon = '---';
+ let trendIcon = '•';
+
+ // 查找箭头元素
+ const responseTimePercentElem = document.getElementById('response-time-percent');
+ let parent = null;
+ let arrowIcon = null;
+
+ if (responseTimePercentElem) {
+ parent = responseTimePercentElem.parentElement;
+ if (parent) {
+ arrowIcon = parent.querySelector('.fa-arrow-up, .fa-arrow-down, .fa-circle');
+ }
+ }
if (stats.avgResponseTime !== undefined && stats.avgResponseTime !== null) {
// 首次加载时初始化历史数据,不计算趋势
@@ -636,6 +717,10 @@ async function loadDashboardData() {
responsePercent = '0.0%';
trendIcon = '•';
trendClass = 'text-gray-500';
+ if (arrowIcon) {
+ arrowIcon.className = 'fa fa-circle mr-1 text-xs';
+ parent.className = 'text-gray-500 text-sm flex items-center';
+ }
} else {
const prevResponseTime = window.dashboardHistoryData.prevResponseTime;
@@ -644,16 +729,38 @@ async function loadDashboardData() {
const changePercent = ((stats.avgResponseTime - prevResponseTime) / prevResponseTime) * 100;
responsePercent = Math.abs(changePercent).toFixed(1) + '%';
- // 设置趋势图标和颜色(响应时间增加是负面的,减少是正面的)
+ // 处理-0.0%的情况
+ if (responsePercent === '-0.0%') {
+ responsePercent = '0.0%';
+ }
+
+ // 根据用户要求:数量下降显示红色箭头,上升显示绿色箭头
+ // 对于响应时间,数值增加表示性能下降,数值减少表示性能提升
+ // 但根据用户要求,我们只根据数值变化方向来设置颜色
if (changePercent > 0) {
- trendIcon = '↓';
- trendClass = 'text-danger';
- } else if (changePercent < 0) {
+ // 响应时间增加(性能下降),数值上升
trendIcon = '↑';
trendClass = 'text-success';
+ if (arrowIcon) {
+ arrowIcon.className = 'fa fa-arrow-up mr-1';
+ parent.className = 'text-success text-sm flex items-center';
+ }
+ } else if (changePercent < 0) {
+ // 响应时间减少(性能提升),数值下降
+ trendIcon = '↓';
+ trendClass = 'text-danger';
+ if (arrowIcon) {
+ arrowIcon.className = 'fa fa-arrow-down mr-1';
+ parent.className = 'text-danger text-sm flex items-center';
+ }
} else {
+ // 趋势为0时,显示圆点图标
trendIcon = '•';
trendClass = 'text-gray-500';
+ if (arrowIcon) {
+ arrowIcon.className = 'fa fa-circle mr-1 text-xs';
+ parent.className = 'text-gray-500 text-sm flex items-center';
+ }
}
}
@@ -663,7 +770,6 @@ async function loadDashboardData() {
}
document.getElementById('avg-response-time').textContent = responseTime;
- const responseTimePercentElem = document.getElementById('response-time-percent');
if (responseTimePercentElem) {
responseTimePercentElem.textContent = trendIcon + ' ' + responsePercent;
responseTimePercentElem.className = `text-sm flex items-center ${trendClass}`;
@@ -697,7 +803,19 @@ async function loadDashboardData() {
// 计算活跃IP趋势
let ipsPercent = '---';
let trendClass = 'text-gray-400';
- let trendIcon = '---';
+ let trendIcon = '•';
+
+ // 查找箭头元素
+ const activeIpsPercentElem = document.getElementById('active-ips-percent');
+ let parent = null;
+ let arrowIcon = null;
+
+ if (activeIpsPercentElem) {
+ parent = activeIpsPercentElem.parentElement;
+ if (parent) {
+ arrowIcon = parent.querySelector('.fa-arrow-up, .fa-arrow-down');
+ }
+ }
if (stats.activeIPs !== undefined && stats.activeIPs !== null) {
// 存储当前值用于下次计算趋势
@@ -713,9 +831,21 @@ async function loadDashboardData() {
if (changePercent > 0) {
trendIcon = '↑';
trendClass = 'text-success';
+
+ // 更新箭头图标和颜色
+ if (arrowIcon) {
+ arrowIcon.className = 'fa fa-arrow-up mr-1';
+ parent.className = 'text-success text-sm flex items-center';
+ }
} else if (changePercent < 0) {
trendIcon = '↓';
trendClass = 'text-danger';
+
+ // 更新箭头图标和颜色
+ if (arrowIcon) {
+ arrowIcon.className = 'fa fa-arrow-down mr-1';
+ parent.className = 'text-danger text-sm flex items-center';
+ }
} else {
trendIcon = '•';
trendClass = 'text-gray-500';
@@ -724,7 +854,6 @@ async function loadDashboardData() {
}
document.getElementById('active-ips').textContent = activeIPs;
- const activeIpsPercentElem = document.getElementById('active-ips-percent');
if (activeIpsPercentElem) {
activeIpsPercentElem.textContent = trendIcon + ' ' + ipsPercent;
activeIpsPercentElem.className = `text-sm flex items-center ${trendClass}`;
@@ -768,29 +897,51 @@ function updateStatsCards(stats) {
console.log('更新统计卡片,收到数据:', stats);
// 适配不同的数据结构
- let totalQueries = 0, blockedQueries = 0, allowedQueries = 0, errorQueries = 0;
+ // 保存当前显示的值,用于在数据缺失时保留
+ let totalQueries, blockedQueries, allowedQueries, errorQueries;
let topQueryType = 'A', queryTypePercentage = 0;
- let activeIPs = 0, activeIPsPercentage = 0;
+ let activeIPs, activeIPsPercentage = 0;
+
+ // 优先从DOM中获取当前显示的值,作为默认值
+ const totalQueriesElem = document.getElementById('total-queries');
+ const blockedQueriesElem = document.getElementById('blocked-queries');
+ const allowedQueriesElem = document.getElementById('allowed-queries');
+ const errorQueriesElem = document.getElementById('error-queries');
+ const activeIPsElem = document.getElementById('active-ips');
+
+ // 解析当前显示的值,作为默认值
+ const getCurrentValue = (elem) => {
+ if (!elem) return 0;
+ const text = elem.textContent.replace(/,/g, '').replace(/[^0-9]/g, '');
+ return parseInt(text) || 0;
+ };
+
+ // 初始化默认值为当前显示的值
+ totalQueries = getCurrentValue(totalQueriesElem);
+ blockedQueries = getCurrentValue(blockedQueriesElem);
+ allowedQueries = getCurrentValue(allowedQueriesElem);
+ errorQueries = getCurrentValue(errorQueriesElem);
+ activeIPs = getCurrentValue(activeIPsElem);
// 检查数据结构,兼容可能的不同格式
if (stats) {
- // 优先使用顶层字段
- totalQueries = stats.totalQueries || 0;
- blockedQueries = stats.blockedQueries || 0;
- allowedQueries = stats.allowedQueries || 0;
- errorQueries = stats.errorQueries || 0;
- topQueryType = stats.topQueryType || 'A';
- queryTypePercentage = stats.queryTypePercentage || 0;
- activeIPs = stats.activeIPs || 0;
- activeIPsPercentage = stats.activeIPsPercentage || 0;
+ // 优先使用顶层字段,只有当值存在时才更新
+ if (stats.totalQueries !== undefined) totalQueries = stats.totalQueries;
+ if (stats.blockedQueries !== undefined) blockedQueries = stats.blockedQueries;
+ if (stats.allowedQueries !== undefined) allowedQueries = stats.allowedQueries;
+ if (stats.errorQueries !== undefined) errorQueries = stats.errorQueries;
+ if (stats.topQueryType !== undefined) topQueryType = stats.topQueryType;
+ if (stats.queryTypePercentage !== undefined) queryTypePercentage = stats.queryTypePercentage;
+ if (stats.activeIPs !== undefined) activeIPs = stats.activeIPs;
+ if (stats.activeIPsPercentage !== undefined) activeIPsPercentage = stats.activeIPsPercentage;
// 如果dns对象存在,优先使用其中的数据
if (stats.dns) {
- totalQueries = stats.dns.Queries || totalQueries;
- blockedQueries = stats.dns.Blocked || blockedQueries;
- allowedQueries = stats.dns.Allowed || allowedQueries;
- errorQueries = stats.dns.Errors || errorQueries;
+ if (stats.dns.Queries !== undefined) totalQueries = stats.dns.Queries;
+ if (stats.dns.Blocked !== undefined) blockedQueries = stats.dns.Blocked;
+ if (stats.dns.Allowed !== undefined) allowedQueries = stats.dns.Allowed;
+ if (stats.dns.Errors !== undefined) errorQueries = stats.dns.Errors;
// 计算最常用查询类型的百分比
if (stats.dns.QueryTypes && stats.dns.Queries > 0) {
@@ -805,14 +956,14 @@ function updateStatsCards(stats) {
}
} else if (Array.isArray(stats) && stats.length > 0) {
// 可能的数据结构3: 数组形式
- totalQueries = stats[0].total || 0;
- blockedQueries = stats[0].blocked || 0;
- allowedQueries = stats[0].allowed || 0;
- errorQueries = stats[0].error || 0;
- topQueryType = stats[0].topQueryType || 'A';
- queryTypePercentage = stats[0].queryTypePercentage || 0;
- activeIPs = stats[0].activeIPs || 0;
- activeIPsPercentage = stats[0].activeIPsPercentage || 0;
+ if (stats[0].total !== undefined) totalQueries = stats[0].total;
+ if (stats[0].blocked !== undefined) blockedQueries = stats[0].blocked;
+ if (stats[0].allowed !== undefined) allowedQueries = stats[0].allowed;
+ if (stats[0].error !== undefined) errorQueries = stats[0].error;
+ if (stats[0].topQueryType !== undefined) topQueryType = stats[0].topQueryType;
+ if (stats[0].queryTypePercentage !== undefined) queryTypePercentage = stats[0].queryTypePercentage;
+ if (stats[0].activeIPs !== undefined) activeIPs = stats[0].activeIPs;
+ if (stats[0].activeIPsPercentage !== undefined) activeIPsPercentage = stats[0].activeIPsPercentage;
}
// 存储正在进行的动画状态,避免动画重叠
@@ -1040,23 +1191,33 @@ function updateStatsCards(stats) {
animateValue('active-ips', activeIPs);
// DNSSEC相关数据
- let dnssecEnabled = false, dnssecQueries = 0, dnssecSuccess = 0, dnssecFailed = 0, dnssecUsage = 0;
+ // 优先从DOM中获取当前显示的值,作为默认值
+ const dnssecSuccessElem = document.getElementById('dnssec-success');
+ const dnssecFailedElem = document.getElementById('dnssec-failed');
+ const dnssecQueriesElem = document.getElementById('dnssec-queries');
+
+ // 从当前显示值初始化,确保数据刷新前保留前一次结果
+ let dnssecEnabled = false;
+ let dnssecQueries = getCurrentValue(dnssecQueriesElem);
+ let dnssecSuccess = getCurrentValue(dnssecSuccessElem);
+ let dnssecFailed = getCurrentValue(dnssecFailedElem);
+ let dnssecUsage = 0;
// 检查DNSSEC数据
if (stats) {
- // 优先使用顶层字段
- dnssecEnabled = stats.dnssecEnabled || false;
- dnssecQueries = stats.dnssecQueries || 0;
- dnssecSuccess = stats.dnssecSuccess || 0;
- dnssecFailed = stats.dnssecFailed || 0;
- dnssecUsage = stats.dnssecUsage || 0;
+ // 优先使用顶层字段,只有当值存在时才更新
+ if (stats.dnssecEnabled !== undefined) dnssecEnabled = stats.dnssecEnabled;
+ if (stats.dnssecQueries !== undefined) dnssecQueries = stats.dnssecQueries;
+ if (stats.dnssecSuccess !== undefined) dnssecSuccess = stats.dnssecSuccess;
+ if (stats.dnssecFailed !== undefined) dnssecFailed = stats.dnssecFailed;
+ if (stats.dnssecUsage !== undefined) dnssecUsage = stats.dnssecUsage;
// 如果dns对象存在,优先使用其中的数据
if (stats.dns) {
- dnssecEnabled = stats.dns.DNSSECEnabled || dnssecEnabled;
- dnssecQueries = stats.dns.DNSSECQueries || dnssecQueries;
- dnssecSuccess = stats.dns.DNSSECSuccess || dnssecSuccess;
- dnssecFailed = stats.dns.DNSSECFailed || dnssecFailed;
+ if (stats.dns.DNSSECEnabled !== undefined) dnssecEnabled = stats.dns.DNSSECEnabled;
+ if (stats.dns.DNSSECQueries !== undefined) dnssecQueries = stats.dns.DNSSECQueries;
+ if (stats.dns.DNSSECSuccess !== undefined) dnssecSuccess = stats.dns.DNSSECSuccess;
+ if (stats.dns.DNSSECFailed !== undefined) dnssecFailed = stats.dns.DNSSECFailed;
}
// 如果没有直接提供使用率,计算使用率
@@ -1102,12 +1263,66 @@ function updateStatsCards(stats) {
if (queryTypePercentageElement) queryTypePercentageElement.textContent = `${Math.round(queryTypePercentage)}%`;
if (activeIpsPercentElement) activeIpsPercentElement.textContent = `${Math.round(activeIPsPercentage)}%`;
- // 计算并平滑更新百分比
+ // 计算并平滑更新百分比,同时更新箭头颜色和方向
+ function updatePercentWithArrow(elementId, percentage, prevValue, currentValue) {
+ const element = document.getElementById(elementId);
+ if (!element) return;
+
+ // 更新百分比数值
+ updatePercentage(elementId, percentage);
+
+ // 查找父元素,获取箭头图标
+ const parent = element.parentElement;
+ if (!parent) return;
+
+ let arrowIcon = parent.querySelector('.fa-arrow-up, .fa-arrow-down, .fa-circle');
+ if (!arrowIcon) return;
+
+ // 计算变化趋势
+ let isIncrease = currentValue > prevValue;
+ let isDecrease = currentValue < prevValue;
+ let isNoChange = currentValue === prevValue;
+
+ // 处理百分比显示,避免-0.0%的情况
+ let formattedPercentage = percentage;
+ if (percentage === '-0.0%') {
+ formattedPercentage = '0.0%';
+ updatePercentage(elementId, formattedPercentage);
+ }
+
+ // 更新箭头图标和颜色
+ if (isIncrease) {
+ arrowIcon.className = 'fa fa-arrow-up mr-1';
+ parent.className = 'text-success text-sm flex items-center';
+ } else if (isDecrease) {
+ arrowIcon.className = 'fa fa-arrow-down mr-1';
+ parent.className = 'text-danger text-sm flex items-center';
+ } else if (isNoChange) {
+ // 趋势为0时,显示圆点图标
+ arrowIcon.className = 'fa fa-circle mr-1 text-xs';
+ parent.className = 'text-gray-500 text-sm flex items-center';
+ }
+ }
+
+ // 保存历史数据,用于计算趋势
+ window.dashboardHistoryData = window.dashboardHistoryData || {
+ totalQueries: 0,
+ blockedQueries: 0,
+ allowedQueries: 0,
+ errorQueries: 0
+ };
+
+ // 计算百分比并更新箭头
if (totalQueries > 0) {
- updatePercentage('blocked-percent', `${Math.round((blockedQueries / totalQueries) * 100)}%`);
- updatePercentage('allowed-percent', `${Math.round((allowedQueries / totalQueries) * 100)}%`);
- updatePercentage('error-percent', `${Math.round((errorQueries / totalQueries) * 100)}%`);
- updatePercentage('queries-percent', '100%');
+ const queriesPercent = '100%';
+ const blockedPercent = `${Math.round((blockedQueries / totalQueries) * 100)}%`;
+ const allowedPercent = `${Math.round((allowedQueries / totalQueries) * 100)}%`;
+ const errorPercent = `${Math.round((errorQueries / totalQueries) * 100)}%`;
+
+ updatePercentWithArrow('queries-percent', queriesPercent, window.dashboardHistoryData.totalQueries, totalQueries);
+ updatePercentWithArrow('blocked-percent', blockedPercent, window.dashboardHistoryData.blockedQueries, blockedQueries);
+ updatePercentWithArrow('allowed-percent', allowedPercent, window.dashboardHistoryData.allowedQueries, allowedQueries);
+ updatePercentWithArrow('error-percent', errorPercent, window.dashboardHistoryData.errorQueries, errorQueries);
} else {
updatePercentage('queries-percent', '---');
updatePercentage('blocked-percent', '---');
@@ -1115,6 +1330,12 @@ function updateStatsCards(stats) {
updatePercentage('error-percent', '---');
}
+ // 更新历史数据
+ window.dashboardHistoryData.totalQueries = totalQueries;
+ window.dashboardHistoryData.blockedQueries = blockedQueries;
+ window.dashboardHistoryData.allowedQueries = allowedQueries;
+ window.dashboardHistoryData.errorQueries = errorQueries;
+
}
diff --git a/static/js/logs.js b/static/js/logs.js
index bfef799..40b3dca 100644
--- a/static/js/logs.js
+++ b/static/js/logs.js
@@ -280,45 +280,45 @@ async function getDomainInfo(domain) {
// 如果有URL属性,直接检查域名
if (website.url) {
// 处理字符串类型的URL
- if (typeof website.url === 'string') {
- console.log(' 检查字符串URL:', website.url);
- if (isDomainMatch(website.url, normalizedDomain, website.categoryId)) {
- console.log(' 匹配成功,返回网站信息');
- return {
- name: website.name,
- icon: website.icon,
- categoryId: website.categoryId,
- categoryName: domainInfoDatabase.categories[website.categoryId] || '未知',
- company: website.company || companyName
- };
- }
+ if (typeof website.url === 'string') {
+ console.log(' 检查字符串URL:', website.url);
+ if (isDomainMatch(website.url, normalizedDomain, website.categoryId)) {
+ console.log(' 匹配成功,返回网站信息');
+ return {
+ name: website.name,
+ icon: website.icon,
+ categoryId: website.categoryId,
+ categoryName: domainInfoDatabase.categories[website.categoryId] || '未知',
+ company: website.company || companyName
+ };
}
- // 处理对象类型的URL
- else if (typeof website.url === 'object') {
- console.log(' 检查对象类型URL,包含', Object.keys(website.url).length, '个URL');
- for (const urlKey in website.url) {
- if (website.url.hasOwnProperty(urlKey)) {
- const urlValue = website.url[urlKey];
- console.log(' 检查URL', urlKey, ':', urlValue);
- if (isDomainMatch(urlValue, normalizedDomain, website.categoryId)) {
- console.log(' 匹配成功,返回网站信息');
- return {
- name: website.name,
- icon: website.icon,
- categoryId: website.categoryId,
- categoryName: domainInfoDatabase.categories[website.categoryId] || '未知',
- company: website.company || companyName
- };
- }
+ }
+ // 处理对象类型的URL
+ else if (typeof website.url === 'object') {
+ console.log(' 检查对象类型URL,包含', Object.keys(website.url).length, '个URL');
+ for (const urlKey in website.url) {
+ if (website.url.hasOwnProperty(urlKey)) {
+ const urlValue = website.url[urlKey];
+ console.log(' 检查URL', urlKey, ':', urlValue);
+ if (isDomainMatch(urlValue, normalizedDomain, website.categoryId)) {
+ console.log(' 匹配成功,返回网站信息');
+ return {
+ name: website.name,
+ icon: website.icon,
+ categoryId: website.categoryId,
+ categoryName: domainInfoDatabase.categories[website.categoryId] || '未知',
+ company: website.company || companyName
+ };
}
}
}
+ }
} else if (typeof website === 'object' && website !== null) {
// 没有URL属性,可能是嵌套的类别
console.log(' 发现嵌套类别,进一步检查');
for (const nestedWebsiteKey in website) {
if (website.hasOwnProperty(nestedWebsiteKey) && nestedWebsiteKey !== 'company') {
- console.log(' 检查嵌套网站:', nestedWebsiteKey);
+ console.log(' 检查嵌套网站/类别:', nestedWebsiteKey);
const nestedWebsite = website[nestedWebsiteKey];
if (nestedWebsite.url) {
@@ -356,8 +356,54 @@ async function getDomainInfo(domain) {
}
}
}
+ } else if (typeof nestedWebsite === 'object' && nestedWebsite !== null) {
+ // 嵌套类别中的嵌套类别,递归检查
+ console.log(' 发现二级嵌套类别,进一步检查');
+ for (const secondNestedWebsiteKey in nestedWebsite) {
+ if (nestedWebsite.hasOwnProperty(secondNestedWebsiteKey) && secondNestedWebsiteKey !== 'company') {
+ console.log(' 检查二级嵌套网站:', secondNestedWebsiteKey);
+ const secondNestedWebsite = nestedWebsite[secondNestedWebsiteKey];
+
+ if (secondNestedWebsite.url) {
+ // 处理字符串类型的URL
+ if (typeof secondNestedWebsite.url === 'string') {
+ console.log(' 检查字符串URL:', secondNestedWebsite.url);
+ if (isDomainMatch(secondNestedWebsite.url, normalizedDomain, secondNestedWebsite.categoryId)) {
+ console.log(' 匹配成功,返回网站信息');
+ return {
+ name: secondNestedWebsite.name,
+ icon: secondNestedWebsite.icon,
+ categoryId: secondNestedWebsite.categoryId,
+ categoryName: domainInfoDatabase.categories[secondNestedWebsite.categoryId] || '未知',
+ company: secondNestedWebsite.company || companyName
+ };
+ }
+ }
+ // 处理对象类型的URL
+ else if (typeof secondNestedWebsite.url === 'object') {
+ console.log(' 检查对象类型URL,包含', Object.keys(secondNestedWebsite.url).length, '个URL');
+ for (const urlKey in secondNestedWebsite.url) {
+ if (secondNestedWebsite.url.hasOwnProperty(urlKey)) {
+ const urlValue = secondNestedWebsite.url[urlKey];
+ console.log(' 检查URL', urlKey, ':', urlValue);
+ if (isDomainMatch(urlValue, normalizedDomain, secondNestedWebsite.categoryId)) {
+ console.log(' 匹配成功,返回网站信息');
+ return {
+ name: secondNestedWebsite.name,
+ icon: secondNestedWebsite.icon,
+ categoryId: secondNestedWebsite.categoryId,
+ categoryName: domainInfoDatabase.categories[secondNestedWebsite.categoryId] || '未知',
+ company: secondNestedWebsite.company || companyName
+ };
+ }
+ }
+ }
+ }
+ }
+ }
+ }
} else {
- console.log(' 嵌套网站没有URL属性');
+ console.log(' 嵌套网站没有URL属性且不是对象类型');
}
}
}
@@ -1891,7 +1937,7 @@ async function showLogDetailModal(log) {
${domainInfo.categoryName || '未知'}
- 所属单位:
+ 所属单位/公司:
${domainInfo.company || '未知'}
diff --git a/temp_config.json b/temp_config.json
new file mode 100644
index 0000000..237b7c2
--- /dev/null
+++ b/temp_config.json
@@ -0,0 +1,52 @@
+{
+ "dns": {
+ "port": 5353,
+ "upstreamDNS": [
+ "223.5.5.5:53",
+ "223.6.6.6:53",
+ "117.50.10.10:53",
+ "10.35.10.200:53"
+ ],
+ "dnssecUpstreamDNS": [
+ "117.50.10.10:53",
+ "101.226.4.6:53",
+ "218.30.118.6:53",
+ "208.67.220.220:53",
+ "208.67.222.222:53"
+ ],
+ "timeout": 5000,
+ "statsFile": "data/stats.json",
+ "saveInterval": 300,
+ "cacheTTL": 30,
+ "enableDNSSEC": true,
+ "queryMode": "parallel",
+ "domainSpecificDNS": {
+ "amazehome.xyz": ["10.35.10.200:53"]
+ }
+ },
+ "http": {
+ "port": 8081,
+ "host": "0.0.0.0",
+ "enableAPI": true,
+ "username": "admin",
+ "password": "admin"
+ },
+ "shield": {
+ "localRulesFile": "data/rules.txt",
+ "blacklists": [],
+ "updateInterval": 3600,
+ "hostsFile": "data/hosts.txt",
+ "blockMethod": "NXDOMAIN",
+ "customBlockIP": "",
+ "statsFile": "./data/shield_stats.json",
+ "statsSaveInterval": 60,
+ "remoteRulesCacheDir": "data/remote_rules"
+ },
+ "log": {
+ "file": "logs/dns-server-5353.log",
+ "level": "debug",
+ "maxSize": 100,
+ "maxBackups": 10,
+ "maxAge": 30
+ }
+}
\ No newline at end of file
diff --git a/test-domain-info.js b/test-domain-info.js
new file mode 100644
index 0000000..d53dc39
--- /dev/null
+++ b/test-domain-info.js
@@ -0,0 +1,261 @@
+// 测试脚本,用于调试 getDomainInfo 函数
+const fs = require('fs');
+const path = require('path');
+
+// 模拟浏览器环境的 console.log
+console.log = function() {
+ process.stdout.write(Array.from(arguments).join(' ') + '\n');
+};
+
+// 读取域名信息数据库
+const domainInfoPath = path.join(__dirname, 'static/domain-info/domains/domain-info.json');
+const domainInfoDatabase = JSON.parse(fs.readFileSync(domainInfoPath, 'utf8'));
+
+// 模拟已加载的数据库
+let domainInfoLoaded = true;
+
+// 检查域名是否匹配
+function isDomainMatch(urlValue, targetDomain, categoryId) {
+ console.log(' 开始匹配URL:', urlValue, '目标域名:', targetDomain, '类别ID:', categoryId);
+
+ // 规范化目标域名,去除末尾的点
+ const normalizedTargetDomain = targetDomain.replace(/\.$/, '').toLowerCase();
+
+ try {
+ // 尝试将URL值解析为完整URL
+ console.log(' 尝试解析URL为完整URL');
+ const url = new URL(urlValue);
+ let hostname = url.hostname.toLowerCase();
+ // 规范化主机名,去除末尾的点
+ hostname = hostname.replace(/\.$/, '');
+ console.log(' 解析成功,主机名:', hostname, '规范化目标域名:', normalizedTargetDomain);
+
+ // 根据类别ID选择匹配方式
+ if (categoryId === 2) {
+ // CDN类别,使用域名后缀匹配
+ if (normalizedTargetDomain.endsWith('.' + hostname) || normalizedTargetDomain === hostname) {
+ console.log(' CDN域名后缀匹配成功');
+ return true;
+ } else {
+ console.log(' CDN域名后缀不匹配');
+ return false;
+ }
+ } else {
+ // 其他类别,使用完整域名匹配
+ if (hostname === normalizedTargetDomain) {
+ console.log(' 完整域名匹配成功');
+ return true;
+ } else {
+ console.log(' 完整域名不匹配');
+ return false;
+ }
+ }
+ } catch (e) {
+ console.log(' 解析URL失败,将其视为纯域名处理,错误信息:', e.message);
+ // 如果是纯域名而不是完整URL
+ let urlDomain = urlValue.toLowerCase();
+ // 规范化纯域名,去除末尾的点
+ urlDomain = urlDomain.replace(/\.$/, '');
+ console.log(' 处理为纯域名:', urlDomain, '规范化目标域名:', normalizedTargetDomain);
+
+ // 根据类别ID选择匹配方式
+ if (categoryId === 2) {
+ // CDN类别,使用域名后缀匹配
+ if (normalizedTargetDomain.endsWith('.' + urlDomain) || normalizedTargetDomain === urlDomain) {
+ console.log(' CDN域名后缀匹配成功');
+ return true;
+ } else {
+ console.log(' CDN域名后缀不匹配');
+ return false;
+ }
+ } else {
+ // 其他类别,使用完整域名匹配
+ if (urlDomain === normalizedTargetDomain) {
+ console.log(' 完整域名匹配成功');
+ return true;
+ } else {
+ console.log(' 完整域名不匹配');
+ return false;
+ }
+ }
+ }
+}
+
+// 根据域名查找对应的网站信息
+async function getDomainInfo(domain) {
+ console.log('开始查找域名信息,域名:', domain);
+
+ if (!domainInfoDatabase || !domainInfoDatabase.domains) {
+ console.error('域名信息数据库无效或为空');
+ return null;
+ }
+
+ // 规范化域名,移除可能的端口号
+ const normalizedDomain = domain.replace(/:\d+$/, '').toLowerCase();
+ console.log('规范化后的域名:', normalizedDomain);
+
+ // 遍历所有公司
+ console.log('开始遍历公司,总公司数:', Object.keys(domainInfoDatabase.domains).length);
+ for (const companyKey in domainInfoDatabase.domains) {
+ if (domainInfoDatabase.domains.hasOwnProperty(companyKey)) {
+ console.log('检查公司:', companyKey);
+ const companyData = domainInfoDatabase.domains[companyKey];
+ const companyName = companyData.company || companyKey;
+
+ // 遍历公司下的所有网站和类别
+ for (const websiteKey in companyData) {
+ if (companyData.hasOwnProperty(websiteKey) && websiteKey !== 'company') {
+ console.log(' 检查网站/类别:', websiteKey);
+ const website = companyData[websiteKey];
+
+ // 如果有URL属性,直接检查域名
+ if (website.url) {
+ // 处理字符串类型的URL
+ if (typeof website.url === 'string') {
+ console.log(' 检查字符串URL:', website.url);
+ if (isDomainMatch(website.url, normalizedDomain, website.categoryId)) {
+ console.log(' 匹配成功,返回网站信息');
+ return {
+ name: website.name,
+ icon: website.icon,
+ categoryId: website.categoryId,
+ categoryName: domainInfoDatabase.categories[website.categoryId] || '未知',
+ company: website.company || companyName
+ };
+ }
+ }
+ // 处理对象类型的URL
+ else if (typeof website.url === 'object') {
+ console.log(' 检查对象类型URL,包含', Object.keys(website.url).length, '个URL');
+ for (const urlKey in website.url) {
+ if (website.url.hasOwnProperty(urlKey)) {
+ const urlValue = website.url[urlKey];
+ console.log(' 检查URL', urlKey, ':', urlValue);
+ if (isDomainMatch(urlValue, normalizedDomain, website.categoryId)) {
+ console.log(' 匹配成功,返回网站信息');
+ return {
+ name: website.name,
+ icon: website.icon,
+ categoryId: website.categoryId,
+ categoryName: domainInfoDatabase.categories[website.categoryId] || '未知',
+ company: website.company || companyName
+ };
+ }
+ }
+ }
+ }
+ } else if (typeof website === 'object' && website !== null) {
+ // 没有URL属性,可能是嵌套的类别
+ console.log(' 发现嵌套类别,进一步检查');
+ for (const nestedWebsiteKey in website) {
+ if (website.hasOwnProperty(nestedWebsiteKey) && nestedWebsiteKey !== 'company') {
+ console.log(' 检查嵌套网站/类别:', nestedWebsiteKey);
+ const nestedWebsite = website[nestedWebsiteKey];
+
+ if (nestedWebsite.url) {
+ // 处理字符串类型的URL
+ if (typeof nestedWebsite.url === 'string') {
+ console.log(' 检查字符串URL:', nestedWebsite.url);
+ if (isDomainMatch(nestedWebsite.url, normalizedDomain, nestedWebsite.categoryId)) {
+ console.log(' 匹配成功,返回网站信息');
+ return {
+ name: nestedWebsite.name,
+ icon: nestedWebsite.icon,
+ categoryId: nestedWebsite.categoryId,
+ categoryName: domainInfoDatabase.categories[nestedWebsite.categoryId] || '未知',
+ company: nestedWebsite.company || companyName
+ };
+ }
+ }
+ // 处理对象类型的URL
+ else if (typeof nestedWebsite.url === 'object') {
+ console.log(' 检查对象类型URL,包含', Object.keys(nestedWebsite.url).length, '个URL');
+ for (const urlKey in nestedWebsite.url) {
+ if (nestedWebsite.url.hasOwnProperty(urlKey)) {
+ const urlValue = nestedWebsite.url[urlKey];
+ console.log(' 检查URL', urlKey, ':', urlValue);
+ if (isDomainMatch(urlValue, normalizedDomain, nestedWebsite.categoryId)) {
+ console.log(' 匹配成功,返回网站信息');
+ return {
+ name: nestedWebsite.name,
+ icon: nestedWebsite.icon,
+ categoryId: nestedWebsite.categoryId,
+ categoryName: domainInfoDatabase.categories[nestedWebsite.categoryId] || '未知',
+ company: nestedWebsite.company || companyName
+ };
+ }
+ }
+ }
+ }
+ } else if (typeof nestedWebsite === 'object' && nestedWebsite !== null) {
+ // 嵌套类别中的嵌套类别,递归检查
+ console.log(' 发现二级嵌套类别,进一步检查');
+ for (const secondNestedWebsiteKey in nestedWebsite) {
+ if (nestedWebsite.hasOwnProperty(secondNestedWebsiteKey) && secondNestedWebsiteKey !== 'company') {
+ console.log(' 检查二级嵌套网站:', secondNestedWebsiteKey);
+ const secondNestedWebsite = nestedWebsite[secondNestedWebsiteKey];
+
+ if (secondNestedWebsite.url) {
+ // 处理字符串类型的URL
+ if (typeof secondNestedWebsite.url === 'string') {
+ console.log(' 检查字符串URL:', secondNestedWebsite.url);
+ if (isDomainMatch(secondNestedWebsite.url, normalizedDomain, secondNestedWebsite.categoryId)) {
+ console.log(' 匹配成功,返回网站信息');
+ return {
+ name: secondNestedWebsite.name,
+ icon: secondNestedWebsite.icon,
+ categoryId: secondNestedWebsite.categoryId,
+ categoryName: domainInfoDatabase.categories[secondNestedWebsite.categoryId] || '未知',
+ company: secondNestedWebsite.company || companyName
+ };
+ }
+ }
+ // 处理对象类型的URL
+ else if (typeof secondNestedWebsite.url === 'object') {
+ console.log(' 检查对象类型URL,包含', Object.keys(secondNestedWebsite.url).length, '个URL');
+ for (const urlKey in secondNestedWebsite.url) {
+ if (secondNestedWebsite.url.hasOwnProperty(urlKey)) {
+ const urlValue = secondNestedWebsite.url[urlKey];
+ console.log(' 检查URL', urlKey, ':', urlValue);
+ if (isDomainMatch(urlValue, normalizedDomain, secondNestedWebsite.categoryId)) {
+ console.log(' 匹配成功,返回网站信息');
+ return {
+ name: secondNestedWebsite.name,
+ icon: secondNestedWebsite.icon,
+ categoryId: secondNestedWebsite.categoryId,
+ categoryName: domainInfoDatabase.categories[secondNestedWebsite.categoryId] || '未知',
+ company: secondNestedWebsite.company || companyName
+ };
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ console.log(' 嵌套网站没有URL属性且不是对象类型');
+ }
+ }
+ }
+ } else {
+ console.log(' 网站没有URL属性');
+ }
+ }
+ }
+ }
+ }
+
+ console.log('未找到匹配的域名信息');
+ return null;
+}
+
+// 测试 mcs.doubao.com
+getDomainInfo('mcs.doubao.com').then(result => {
+ console.log('\n=== 测试结果 ===');
+ if (result) {
+ console.log('匹配成功:', JSON.stringify(result, null, 2));
+ } else {
+ console.log('匹配失败');
+ }
+});