Compare commits

...

724 Commits

Author SHA1 Message Date
刘祥超
7dbf5add34 增加API文档注释 2023-11-24 09:06:36 +08:00
刘祥超
7c5c6267d4 节点配置中增加节点IP信息 2023-11-18 12:10:16 +08:00
刘祥超
8fdc98cde9 CA证书也支持IP地址 2023-11-18 10:39:32 +08:00
刘祥超
d4413b8afe SSL证书支持IP地址 2023-11-18 09:50:34 +08:00
刘祥超
826bf7bf7d 发送短信时加入验证码(code)参数 2023-11-17 15:00:11 +08:00
刘祥超
c6e95eda15 实现用户系统手机号码绑定和登录(商业版) 2023-11-17 11:51:02 +08:00
刘祥超
539016c2f6 源站支持404内容自动重试其他源站 2023-11-15 19:06:51 +08:00
刘祥超
92f4ea0dc4 WAF人机识别实现点击验证和滑动解锁验证/单个网站可以设置默认的人机识别方式 2023-11-15 15:12:08 +08:00
刘祥超
d430eebe8c 增加用户列表菜单 2023-11-13 11:53:36 +08:00
刘祥超
30ff3ecde5 自定义页面增加例外URL和限制URL设置 2023-11-13 10:45:23 +08:00
刘祥超
f0b5f6ed76 自定义页面增加“跳转URL”功能 2023-11-10 16:36:14 +08:00
刘祥超
4c7de6bdc2 URL跳转中增加例外域名和仅限域名 2023-11-10 11:05:21 +08:00
刘祥超
412704c9f3 优化节点阈值设置 2023-11-03 11:57:56 +08:00
刘祥超
f264426099 优化商业版验证 2023-11-02 17:20:16 +08:00
刘祥超
03a6e9ccb9 访问日志存储策略增加“停止默认数据库存储”选项 2023-10-30 19:03:48 +08:00
刘祥超
9b899dff74 实现网络数据包相关统计(商业版本) 2023-10-26 17:17:48 +08:00
刘祥超
5bfd5de90d 删除指标统计项目中的“请求协议Proto“(因为和HTTP协议一样) 2023-10-25 20:56:04 +08:00
刘祥超
2177f97bdb WAF记录IP动作中IP名单如果为空时,默认为全局黑名单 2023-10-15 09:34:33 +08:00
刘祥超
5340e5ad52 取消反向代理中默认自动50x尝试 2023-10-15 09:02:48 +08:00
刘祥超
06a3db911c 增加修改消息任务状态API 2023-10-14 18:16:13 +08:00
刘祥超
a7ae7ea2db 优化消息通知相关代码 2023-10-14 17:15:52 +08:00
刘祥超
f949e31c0f 优化消息发送API 2023-10-12 20:11:34 +08:00
刘祥超
d0e1dfe1af 支持批量复制WAF设置 2023-10-09 19:52:44 +08:00
刘祥超
083ccfed7d 申请证书任务列表区分管理员和用户 2023-10-09 16:18:40 +08:00
刘祥超
347bbe1b96 证书列表区分管理员和用户证书 2023-10-09 15:54:11 +08:00
刘祥超
16cf11b366 完善API文档 2023-10-09 08:45:44 +08:00
刘祥超
01e211560d WAF策略中默认不启用SynFlood检测 2023-10-08 16:01:50 +08:00
刘祥超
96ce664202 集群设置中增加“自动调节系统参数”选项 2023-10-08 16:01:21 +08:00
刘祥超
2b8191001b 将全局设置的TCP相关设置移到“集群设置--网站设置”中 2023-09-18 16:55:30 +08:00
刘祥超
6799f3f9ae 将全局的通用设置--域名审核设置移到“集群设置--网站设置”中 2023-09-18 16:09:46 +08:00
刘祥超
55e2c38cd8 访客IP设置中支持多个请求报头 2023-09-17 19:14:28 +08:00
刘祥超
534d8e471d 全局网站设置中增加Ln请求调度方法 2023-09-17 18:37:08 +08:00
刘祥超
3c81bd6d5d 优化访客IP地址设置 2023-09-07 18:01:58 +08:00
刘祥超
f2d087a6ce 套餐增加请求数(日/月)限制 2023-09-07 11:46:20 +08:00
刘祥超
2497a81e09 重新实现套餐相关功能 2023-09-06 16:31:14 +08:00
刘祥超
972b487db8 爬虫规则中将php改为php\b,避免屏蔽某些安卓WebView 2023-08-30 10:13:16 +08:00
刘祥超
170e6ebfb8 增加API注释 2023-08-29 14:22:53 +08:00
刘祥超
9e80750309 网站设置增加是否支持${serverAddr}选项 2023-08-25 15:31:13 +08:00
刘祥超
f056a9c5bd IP库增加多个辅助查询函数 2023-08-24 12:21:26 +08:00
刘祥超
6a42827ebd 增加检查中国特殊区域的函数 2023-08-24 11:50:47 +08:00
刘祥超
2d25c385fa 改进若干API注释 2023-08-21 15:54:44 +08:00
刘祥超
8a6c20db01 反向代理增加是否重试50X选项,默认为启用 2023-08-20 15:49:49 +08:00
刘祥超
22d00a6c92 WAF增加通配符匹配/不匹配操作符 2023-08-13 10:37:31 +08:00
刘祥超
51f8fd0f31 WAF操作符元数据中增加数据类型选项,以便于优化交互界面 2023-08-13 10:01:24 +08:00
刘祥超
15708ec1bb 编译proto文件时捕捉错误提示 2023-08-12 20:48:53 +08:00
刘祥超
b6667fc323 在README.md中增加多语言配置langs说明 2023-08-12 20:33:50 +08:00
刘祥超
43cf3b57dd 优化API错误相关提示 2023-08-12 20:33:28 +08:00
刘祥超
58dcab31ae 调整WAF通配符顺序 2023-08-12 08:16:54 +08:00
刘祥超
712a9397a1 优化错误处理相关代码 2023-08-11 16:14:47 +08:00
刘祥超
fe00446003 优化错误提示相关代码 2023-08-11 15:26:59 +08:00
刘祥超
c098732e51 静态分发增加例外URL、限制URL、排除隐藏文件等选项 2023-08-10 11:27:00 +08:00
刘祥超
5c1a9fb8ff WAF策略可以自定义默认的区域/省份封禁提示 2023-08-10 10:30:05 +08:00
刘祥超
4b52a9703b 修复proto文件不能编译的问题 2023-08-10 10:29:43 +08:00
刘祥超
1aebbc8e39 减少默认的WAF检查尺寸 2023-08-09 17:56:15 +08:00
刘祥超
9df5a409e6 Update .golangci.yaml 2023-08-09 08:11:47 +08:00
刘祥超
c9bb7fb8a5 添加golangci-lint配置 2023-08-08 18:50:12 +08:00
刘祥超
6a2803187e 优化代码 2023-08-08 15:12:28 +08:00
刘祥超
3c5c961cd5 使用最新的protoc-gen-go和protoc-gen-go-grpc插件编译proto文件 2023-08-07 10:26:22 +08:00
刘祥超
be5c6b12bb 增加脚本使用指南 2023-08-07 09:46:10 +08:00
刘祥超
e6af6b6575 使用最新的protoc-gen-go和protoc-gen-go-grpc插件 2023-08-06 23:36:50 +08:00
刘祥超
65be70d015 增加一些API注释 2023-08-06 19:33:45 +08:00
刘祥超
117c89ff62 缓存策略增加“缓存磁盘最小空余空间”选项 2023-08-06 18:07:33 +08:00
刘祥超
6d8156b10b 缓存策略增加预热超时时间设置(默认20分钟) 2023-08-06 17:06:39 +08:00
刘祥超
9cc9c4e2b3 增加PROXY Protocol版本号校验 2023-08-06 09:55:16 +08:00
刘祥超
7271046cb3 将一些比较耗时的WAF规则分组设为默认不开启 2023-08-02 17:14:24 +08:00
刘祥超
2a67b2b2d6 修复一处编译错误 2023-08-02 17:02:55 +08:00
刘祥超
31de0d5112 WAF策略增加“最多检查内容尺寸“选项 2023-08-02 17:00:40 +08:00
刘祥超
e80ea8a0e2 节点状态中加入磁盘预估写入速度 2023-08-02 14:48:42 +08:00
刘祥超
c08d7c48f2 缓存条件增加“强制Range回源选项” 2023-07-31 17:31:58 +08:00
刘祥超
e44e54fbf4 缓存条件增加是否允许异步读取源站选项 2023-07-31 15:47:24 +08:00
刘祥超
4a11ecb7b4 修复一处编译问题 2023-07-25 14:08:26 +08:00
刘祥超
5c8dff120a 增加 DefaultTCPLinger 值 2023-07-25 09:35:28 +08:00
刘祥超
df148bf027 删除TOA相关不需要的内容 2023-07-24 15:33:30 +08:00
刘祥超
d5bb623339 优化TOA相关API 2023-07-24 09:56:49 +08:00
刘祥超
58ec21d676 优化HTTP报头策略是否为空的判断 2023-07-22 15:00:15 +08:00
刘祥超
279e4025fd 默认自动检查版本更新 2023-07-20 09:37:47 +08:00
刘祥超
bedb18dd71 修复无法读取鉴权方法列表的问题 2023-07-17 09:32:39 +08:00
刘祥超
102b5859b3 5秒盾增加QPS限制选项 2023-07-14 11:01:14 +08:00
刘祥超
8426bf21be WAF-区域封禁增加提示内容设置 2023-07-14 11:01:01 +08:00
刘祥超
04d4895b7b 集群设置--网站设置增加“服务器旗标”设置 2023-07-12 17:39:26 +08:00
刘祥超
a328f75d89 生成IP库的时候自动指定省略的城市名 2023-07-12 15:34:40 +08:00
刘祥超
a9b28d55c1 增加页面优化相关API、配置、消息代号 2023-07-11 19:47:05 +08:00
刘祥超
fd4aff5ab5 增加是否在手机浏览器上也支持HTTP/3的选项 2023-07-09 16:17:03 +08:00
刘祥超
8639bd213e 增加部分API注释 2023-07-09 10:15:33 +08:00
刘祥超
1c192050c7 缓存策略移除“容纳Key数量”选项 2023-07-08 18:51:10 +08:00
刘祥超
83f5166d76 网站列表增加QPS和攻击QPS信息 2023-07-07 18:51:41 +08:00
刘祥超
05cd622456 优化WAF区域/省份封禁:增加仅允许、增加中国香港澳台、内地等特殊区域 2023-07-07 09:51:30 +08:00
刘祥超
64bac81b56 初始化访客IP配置时去除首尾可能的空格 2023-07-07 08:31:17 +08:00
刘祥超
f3cc573a2c 增加部分多语言代号 2023-07-06 10:56:08 +08:00
刘祥超
6d28a67fd4 官网URL放入到多语言消息中 2023-07-05 20:21:41 +08:00
刘祥超
1151eb6c92 优化硬盘空间不足的提示 2023-07-05 16:42:56 +08:00
刘祥超
ceceda22a6 “集群设置 -- 网站设置”增加“允许记录访问日志”选项 2023-07-05 15:29:04 +08:00
刘祥超
916cd0bf67 增加部分消息代号 2023-07-04 20:29:33 +08:00
刘祥超
28458d9cfc 优化代码 2023-07-03 17:12:00 +08:00
刘祥超
0b2e9fa864 优化全局设置的超时设置 2023-07-03 16:21:52 +08:00
刘祥超
ea35ad2dac 修改部分文字 2023-07-03 15:36:59 +08:00
刘祥超
c66adf989c 增加清空节点同步任务、清空DNS同步任务API 2023-07-02 17:29:41 +08:00
刘祥超
81af1caa6c 优化OSS相关代码 2023-07-02 11:04:59 +08:00
刘祥超
52ed467580 增加API注释 2023-07-01 19:27:49 +08:00
刘祥超
705ffcd087 增加部分数据清理周期设置 2023-07-01 17:55:10 +08:00
刘祥超
04d8a1db33 查找当前API节点版本中增加角色 2023-07-01 15:10:07 +08:00
刘祥超
1ef1a7d49e 优化代码 2023-06-30 19:02:31 +08:00
刘祥超
a720ea0373 优化代码 2023-06-30 19:00:53 +08:00
刘祥超
fd9849c9ff 部分中文转换为多语言代号 2023-06-30 18:09:43 +08:00
刘祥超
141dd89f6f 部分中文转换为多语言代号 2023-06-28 19:06:16 +08:00
刘祥超
9db82b0bea 部分中文转换为多语言代号 2023-06-28 16:19:05 +08:00
刘祥超
bd57d35d63 添加多语言最基础代码 2023-06-28 09:12:13 +08:00
刘祥超
4853363765 源站支持HTTP/2 2023-06-23 11:42:51 +08:00
刘祥超
d9233713e4 补充API注释 2023-06-21 08:45:06 +08:00
刘祥超
7f8dd70c9b 增加简化版的创建TCP网站API 2023-06-18 17:14:29 +08:00
刘祥超
1748637c31 增加简化版的创建HTTP网站API 2023-06-18 16:19:52 +08:00
刘祥超
90c06c4175 把“服务”改成“网站” 2023-06-18 10:57:07 +08:00
刘祥超
fe9bc332c1 增加API注释 2023-06-17 21:05:03 +08:00
刘祥超
f436763c8d 默认不启用性能优化 2023-06-17 19:19:40 +08:00
刘祥超
6bf017ee22 完善API注释 2023-06-16 18:27:16 +08:00
刘祥超
70bc6e7d8e 缓存条件类型增加“URL通配符” 2023-06-16 11:34:59 +08:00
刘祥超
e42b13f278 用户设置中增加登录状态下检查客户端区域的选项 2023-06-16 10:59:30 +08:00
刘祥超
cd545b25c9 80/443端口的源站自动修正HTTP和HTTPS协议,防止有些小白用户不知道HTTP和HTTPS的区别 2023-06-16 08:47:47 +08:00
刘祥超
574b264a37 缓存条件增加"强制返回区间内容"选项 2023-06-15 15:14:25 +08:00
刘祥超
12abb714e0 优化OSS源站相关代码 2023-06-13 15:44:43 +08:00
刘祥超
8bef580c2e 智能DNS实现DoH功能 2023-06-11 17:57:41 +08:00
刘祥超
b08064c1de 初步实现对象存储源站 2023-06-07 17:26:34 +08:00
刘祥超
555673b87d 允许在集群设置 -- “网站设置” 中设置节点IP访问显示的内容 2023-06-05 19:27:05 +08:00
刘祥超
e9145ec22c 网站全局设置增加“强制Ln请求“选项 2023-06-05 17:05:49 +08:00
刘祥超
ffd261b285 改进智能DNS验证相关API 2023-06-05 12:34:51 +08:00
刘祥超
2ff4c67e57 增加一处测试用例 2023-06-02 14:58:08 +08:00
刘祥超
1e1e6e7e81 查找HTTP3端口时只计算已经启用的 2023-06-01 19:42:40 +08:00
刘祥超
71e03f5e07 初步实现HTTP3 2023-06-01 17:48:01 +08:00
刘祥超
6a1a2640d3 Update rpc.json 2023-05-28 19:26:18 +08:00
刘祥超
be32bff7db 提供用户某日刷新/预热缓存数量查询API 2023-05-28 18:00:42 +08:00
刘祥超
afd9e3134d WAF增加“跳转”动作 2023-05-28 17:45:39 +08:00
刘祥超
a9dd5e3ba3 URL跳转精准匹配/匹配前缀支持URL查询参数 2023-05-27 21:00:34 +08:00
刘祥超
5caafcb99a 增加读取节点数配额接口 2023-05-27 15:30:05 +08:00
刘祥超
f34633f22a WAF国家/地区封禁、省份封禁增加例外URL、限制URL 2023-05-25 12:02:25 +08:00
刘祥超
2966240a5c 网站全局设置中增加“自动匹配证书”选项 2023-05-25 11:00:25 +08:00
刘祥超
05255c18d8 智能调度动作可以排序 2023-05-24 15:38:37 +08:00
刘祥超
a74a751a50 删除不需要的文件 2023-05-24 15:06:22 +08:00
刘祥超
c24fa291bd 改进流量相关的智能调度过期时间设置 2023-05-24 15:04:07 +08:00
刘祥超
e33f98f869 优化代码 2023-05-23 19:51:28 +08:00
刘祥超
e3ada6da81 实现集群CC防护策略设置 2023-05-23 19:16:09 +08:00
刘祥超
47c0ade078 实现集群自定义页面 2023-05-22 17:30:08 +08:00
刘祥超
2af8e6c7f8 HTTP Header中支持设置非标Header 2023-05-19 19:52:22 +08:00
刘祥超
e909d4c59a 优化代码 2023-05-19 16:44:02 +08:00
刘祥超
ee744d6c70 HTTP Header - CORS跨域设置增加多个选项 2023-05-19 16:33:13 +08:00
刘祥超
dbea336fee 增加复制节点动作API 2023-05-19 11:12:09 +08:00
刘祥超
5961d955d7 实现基础的智能调度 2023-05-17 18:42:35 +08:00
刘祥超
529a98e694 域名解析功能实现健康检查 2023-05-03 17:09:36 +08:00
刘祥超
48aeffba7e 防盗链增加”同时检查Origin选项“ 2023-05-02 17:05:56 +08:00
刘祥超
a7ac768709 优化编译脚本 2023-04-25 10:26:34 +08:00
刘祥超
ca8226fe9d 远程升级API节点时自动上传边缘节点安装文件 2023-04-23 19:43:01 +08:00
刘祥超
a6395b34a3 5秒盾“加入IP白名单”默认为true 2023-04-23 16:04:59 +08:00
刘祥超
97f56c7bb1 创建ACME用户、ACME任务时可以指定平台用户 2023-04-23 15:01:10 +08:00
刘祥超
c0856d3b46 安全设置中增加“检查客户端指纹”、“检查客户端区域”选项 2023-04-19 18:21:44 +08:00
刘祥超
4e80f62837 IP库查询增加更多信息 2023-04-19 18:20:34 +08:00
刘祥超
753552d4a4 修复证书数据可能获取不到的问题 2023-04-10 09:13:05 +08:00
刘祥超
1f7e60d062 修复证书更新后服务配置无法更新的问题 2023-04-09 21:31:31 +08:00
刘祥超
9dc9cdb28f 修复一个单词拼写错误 2023-04-09 20:12:00 +08:00
刘祥超
90bba69ee5 去除不需要的接口 2023-04-09 17:04:45 +08:00
刘祥超
498f388cf2 增加服务之间拷贝配置的API(开源版本只有定义,没有实现) 2023-04-09 16:00:21 +08:00
刘祥超
8adb09b95f 提供批量更新服务配置API(阶段性提交) 2023-04-06 20:49:47 +08:00
刘祥超
521eee5f2f 审计日志列表增加级别筛选 2023-04-06 10:06:37 +08:00
刘祥超
caf544eff1 自动检查管理员弱密码并提醒 2023-04-04 17:26:22 +08:00
刘祥超
c9ae3df3d3 5秒盾策略变化时只更新策略配置 2023-04-03 15:59:45 +08:00
刘祥超
3485db9a4a 可以使用用户ID查询IP名单 2023-04-03 10:02:47 +08:00
刘祥超
1b368ee490 默认支持低版本HTTP 2023-04-01 21:46:31 +08:00
刘祥超
9ec79adac6 集群服务设置增加“支持低版本HTTP”选项 2023-04-01 09:52:01 +08:00
刘祥超
c3e43503a0 可以批量上传IP名单 2023-03-31 21:37:32 +08:00
刘祥超
eb5d47935c 提供修改网站名称的接口 2023-03-31 15:30:31 +08:00
刘祥超
5521c42cb6 优化代码 2023-03-31 14:05:18 +08:00
刘祥超
226e91a106 IP库增加释放方法 2023-03-31 12:40:13 +08:00
刘祥超
917f8d76a1 增加查询授权版本接口 2023-03-31 12:39:48 +08:00
刘祥超
cfc1a7461c IP库加密/其他对IP库的改进 2023-03-30 20:02:46 +08:00
刘祥超
67941b5379 更新IP库文件 2023-03-29 21:19:11 +08:00
刘祥超
e35caae373 优化IP库模板分析程序 2023-03-29 21:19:01 +08:00
刘祥超
ad84614b2e 优化IP库生成文件的尺寸 2023-03-29 20:08:08 +08:00
刘祥超
32c89d3ae7 IP库制品列表中显示文件尺寸 2023-03-29 20:07:15 +08:00
刘祥超
22bf132efb 修复IP库模板可能无法正确解析数据的问题 2023-03-29 20:06:56 +08:00
刘祥超
67aafeee30 修复IP查询结果显示时可能不显示县级单位的问题 2023-03-29 20:06:15 +08:00
刘祥超
033a3a624d 增加默认CC设置 2023-03-26 12:40:50 +08:00
刘祥超
8d067b6efc 上传单个证书时可以选择所属用户 2023-03-26 12:24:58 +08:00
刘祥超
602a89602e 服务基本信息API中增加用户ID信息 2023-03-25 20:51:28 +08:00
刘祥超
0036b0730b 增加多个接口定义 2023-03-24 19:07:04 +08:00
刘祥超
4b0fe4b78d 上传流量数据时同时上传服务所属用户ID 2023-03-22 19:33:31 +08:00
刘祥超
ca580e820b 合并部分流量查询和带宽查询 2023-03-22 17:53:01 +08:00
刘祥超
9681b29f2c 用户界面设置增加“自动检查CNAME”选项 2023-03-21 11:59:17 +08:00
刘祥超
b2224aa4ae 更新相关库 2023-03-19 17:45:25 +08:00
刘祥超
fad1c3c6ae DNS集群增加自动检测端口选项 2023-03-19 17:45:15 +08:00
刘祥超
995acfe133 修复DataMap无法在多个节点之间共享的问题 2023-03-19 10:23:32 +08:00
刘祥超
9b943f7f03 节点组合配置时服务间可以共用证书数据 2023-03-18 22:15:13 +08:00
刘祥超
d43cbc4f90 查询节点DNS信息时可以区分节点是否已安装 2023-03-18 16:04:27 +08:00
刘祥超
914070f7cc 删除不必要的文件内容 2023-03-17 12:15:30 +08:00
刘祥超
46dc42ffe3 当HTTP和HTTPS端口冲突时提示用户 2023-03-17 11:12:23 +08:00
刘祥超
e938bb34f1 用户系统增加页面背景颜色设置 2023-03-16 16:58:14 +08:00
刘祥超
3ad6c00956 读取节点列表时可以按照连接数排序 2023-03-15 17:58:01 +08:00
刘祥超
e6efbacb98 节点看板数据中增加当月、昨日、今日流量 2023-03-15 17:02:14 +08:00
刘祥超
37e05101be 集群看板数据中增加当月流量 2023-03-15 16:25:22 +08:00
刘祥超
a6830af28e 调整统计相关配置默认值 2023-03-12 10:56:53 +08:00
刘祥超
65973c65ac 上传统计数据时使用ID取代名称 2023-03-12 10:21:14 +08:00
刘祥超
01890aa83d 在集群服务设置中增加服务统计相关选项 2023-03-11 10:42:59 +08:00
刘祥超
34f33a0979 WAF cc2模板中增加启用指纹选项 2023-03-10 15:16:29 +08:00
刘祥超
dd3b6b0fe8 优化旧cc统计注释说明 2023-03-10 10:16:44 +08:00
刘祥超
4c7618f623 提供删除运行日志的接口 2023-03-09 16:20:24 +08:00
刘祥超
c73a05eceb 增加CC防护相关API定义、配置 2023-03-09 12:10:31 +08:00
刘祥超
f4a690a5c3 优化URL匹配方法 2023-03-07 17:21:38 +08:00
刘祥超
866697ddd0 集群服务设置增加“记录找不到网站日志”选项 2023-03-07 10:30:44 +08:00
刘祥超
22b945ec40 5秒盾支持例外URL和限制URL 2023-03-06 21:49:11 +08:00
刘祥超
d835b36660 远程升级API节点(部分实现) 2023-03-04 21:22:17 +08:00
刘祥超
b375a40340 智能DNS增加记录权重 2023-03-02 20:06:54 +08:00
刘祥超
a068ce9a4f 对边缘节点配置缓存进行加密,提升安全性 2023-03-02 10:28:15 +08:00
刘祥超
4379acbeea WAF拦截动作可以设置最大封禁时间,从而实现封禁时间随机 2023-03-01 18:59:47 +08:00
刘祥超
a086496d7c WAF支持忽略全局WAF规则 2023-03-01 16:52:30 +08:00
刘祥超
7d80c58871 节点IP地址可以设置专属集群 2023-03-01 11:39:01 +08:00
刘祥超
0933ae903b 实现峰值带宽和平均带宽两种带宽算法 2023-02-27 10:47:31 +08:00
刘祥超
727ac23e17 增加多个配置项 2023-02-23 21:28:42 +08:00
刘祥超
99acf99a48 验证相关和一些注释 2023-02-22 19:46:50 +08:00
刘祥超
23db1c4b77 增加高防相应对象 2023-02-22 17:36:41 +08:00
刘祥超
d03775957e 增加${requestPathLowerExtension}变量 2023-02-10 10:53:05 +08:00
刘祥超
ec166af0c3 修复客户端CA证书设置不起作用的Bug 2023-02-09 11:31:33 +08:00
刘祥超
be29ba72a0 增加管理员和用户登录SESSION API 2023-02-04 15:17:27 +08:00
刘祥超
ce72dcf4dc 增加生成文档的测试用例 2023-01-13 19:36:10 +08:00
刘祥超
c024049778 优化域名匹配,现在 example.com:* 可以匹配 example.com 2023-01-10 10:03:43 +08:00
刘祥超
0863912e0c 用户界面设置中增加“在流量图表中显示缓存相关信息”选项 2023-01-08 11:49:45 +08:00
刘祥超
b19c7f5aa6 用户Dashboard中增加缓存、攻击相关信息 2023-01-08 11:49:30 +08:00
刘祥超
f5bdf33618 WAF增加“在IP列表内”操作符/优化部分操作符代号 2023-01-08 10:15:57 +08:00
刘祥超
c6face62b1 集群服务设置增加自动读超时选项 2023-01-07 20:04:13 +08:00
刘祥超
d3c8d8fd04 调整WAF操作符顺序 2023-01-07 09:35:09 +08:00
刘祥超
16e418447a WAF增加包含任一字符串、包含所有字符串操作符 2023-01-06 20:06:57 +08:00
刘祥超
c77724cf9e IP范围支持多行 2023-01-06 19:13:37 +08:00
刘祥超
606a73f6dd 优化请求条件操作符描述 2023-01-06 16:12:14 +08:00
刘祥超
f48a441f23 集群服务设置中增加性能设置 2023-01-01 19:27:09 +08:00
刘祥超
8d55c38459 优化证书加载速度 2022-12-31 18:34:09 +08:00
刘祥超
37e045876e 优化证书加载速度 2022-12-31 17:22:10 +08:00
刘祥超
bd5a1c5eda 优化证书数量很多时的页面加载速度 2022-12-31 17:12:55 +08:00
刘祥超
bd4cc6f5f5 实现UA名单功能 2022-12-30 20:48:44 +08:00
刘祥超
d417f6f751 内容压缩支持例外扩展名 2022-12-30 12:04:23 +08:00
刘祥超
d48697f4ce 默认情况下内容压缩不支持Partial Content 2022-12-30 11:43:53 +08:00
刘祥超
841a98349d 增加CORS自适应跨域 2022-12-29 17:16:11 +08:00
刘祥超
24d710d39c 用户系统增加自定义页脚 2022-12-28 16:36:37 +08:00
刘祥超
1ed754f285 增加修改服务所属用户API 2022-12-22 11:42:44 +08:00
刘祥超
243ceab3db DNS完善实现SRV和CAA记录 2022-12-15 16:18:00 +08:00
刘祥超
eaca98ee3e 智能DNS初步支持搜索引擎线路 2022-12-13 18:39:11 +08:00
刘祥超
11b0a12304 实现用户通过邮件重置密码功能 2022-12-10 15:54:39 +08:00
刘祥超
ce7911c3fe 初步完成用户电子邮箱绑定(激活) 2022-12-08 20:26:20 +08:00
刘祥超
44a3952f05 实现线路优先级/多个NS接口增加用户ID参数 2022-12-03 20:49:10 +08:00
刘祥超
a8340b8a8a 增加查询某个集群已验证域名API 2022-12-01 19:39:53 +08:00
刘祥超
b4f56c044c 优化WAF操作符提示 2022-12-01 15:20:03 +08:00
刘祥超
8b2088d498 用户可以使用管理员设置的公用线路 2022-11-24 17:20:26 +08:00
刘祥超
e7bf608552 导出API列表时对数据进行排序,以保证每次输出的稳定性 2022-11-22 16:45:14 +08:00
刘祥超
2c06c8932d 优化API角色提取 2022-11-22 14:36:06 +08:00
刘祥超
789aeab579 节点状态中增加API调用统计 2022-11-22 14:35:31 +08:00
刘祥超
a95d202ef0 在节点详情中显示API节点地址/DNS节点也支持自定义API节点地址 2022-11-21 21:08:17 +08:00
刘祥超
64f54d462e 节点可以单独设置所使用的API节点地址 2022-11-21 19:55:09 +08:00
刘祥超
52a20b9915 DNS API支持查询多个同名记录 2022-11-17 17:34:10 +08:00
刘祥超
69ab70b750 优化代码 2022-11-17 14:45:41 +08:00
刘祥超
890296c88b 请求变量增加${cname},WAF checkpoint增加cname和isCNAME 2022-11-16 15:01:00 +08:00
刘祥超
e5ba474419 改进变量分析测试用例 2022-11-16 14:27:20 +08:00
刘祥超
0f7482511e 只有边缘节点有自定义缓存目录时才会更新缓存策略 2022-11-15 20:40:59 +08:00
刘祥超
ea58e6094c 边缘节点支持设置多个缓存目录 2022-11-15 20:35:39 +08:00
刘祥超
4d5fd28f97 增加操作系统和浏览器信息维护接口 2022-11-11 17:46:27 +08:00
刘祥超
7a139b43a9 域名匹配增加通配符端口 2022-11-09 18:21:05 +08:00
刘祥超
0d1108387d 智能DNS:用户添加域名时,可以不需要经过TXT验证 2022-11-09 15:47:17 +08:00
刘祥超
e67af97487 MX记录支持优先级设置 2022-11-06 20:21:11 +08:00
刘祥超
629a2a5a8e 获取单节点同步任务接口增加版本号(version)参数 2022-11-06 11:51:58 +08:00
刘祥超
884776fb85 域名跳转增加忽略跳转前端口选项 2022-11-04 20:59:29 +08:00
刘祥超
f0e9b5c7e1 带宽相关数据增加百分位 2022-11-04 20:29:56 +08:00
刘祥超
3849b651af 用户看板增加带宽百分位 2022-11-04 17:39:33 +08:00
刘祥超
4d550577ee 删除不必要的文件 2022-11-04 16:56:14 +08:00
刘祥超
b3e64f7241 时钟同步增加是否检查chrony选项 2022-11-03 14:58:25 +08:00
刘祥超
f315b7b1d6 NSDomainService.FindNSDomainWithNameRequest()增加userId参数 2022-11-01 09:47:13 +08:00
刘祥超
2de1a8aa4e 增加默认集群DNS设置 2022-10-28 15:26:44 +08:00
刘祥超
cb3bf14df8 节点SSH登录自动使用集群设置 2022-10-26 19:23:29 +08:00
刘祥超
1d66029ef0 集群全局服务配置中增加多个访问日志相关选项 2022-10-26 17:49:45 +08:00
刘祥超
b795f60672 URL跳转中增加域名跳转、端口跳转 2022-10-26 16:07:32 +08:00
刘祥超
76e98174ee 节点设置中增加“通过IP名单”选项 2022-10-26 10:42:02 +08:00
刘祥超
18765d29f2 优化从API实现代码中提取角色的方法 2022-10-25 16:32:16 +08:00
刘祥超
9a38d40937 IP名单增加模糊搜索 2022-10-25 12:27:23 +08:00
刘祥超
43ad1ff67e proto文件增加注释 2022-10-24 18:33:51 +08:00
刘祥超
9f4e838f1d WAF参数定义增加优先级 2022-10-24 17:56:37 +08:00
刘祥超
e95909e293 修复域名解析--集群中单节点多IP时无法修改IP的Bug 2022-10-24 16:34:12 +08:00
刘祥超
759189bca1 添加、修改、删除HTTP Header时增加通用Header提示 2022-10-24 15:42:23 +08:00
刘祥超
3dd24dc5e7 集群服务设置--访问日志中可以设置是否只记录通用Header 2022-10-24 14:39:27 +08:00
刘祥超
156172dacf 防盗链功能增加禁止的来源域名 2022-10-24 10:21:13 +08:00
刘祥超
b6e0740b27 更新用户服务可用状态时同时返回状态 2022-10-23 20:12:33 +08:00
刘祥超
57f3c72058 修改部分小数数字精度(float->double),增加用户服务状态API 2022-10-23 16:29:08 +08:00
刘祥超
a9789c2f22 用户账单接口增加多个参数 2022-10-21 15:44:36 +08:00
刘祥超
ca02782ba5 增加操作节点区域相关接口 2022-10-20 15:14:22 +08:00
刘祥超
1e75f61464 增加流量包相关接口 2022-10-20 10:24:52 +08:00
刘祥超
e98de100b9 增加修改和读取用户计费方式和计费周期接口 2022-10-15 19:15:36 +08:00
刘祥超
387ee8d9c9 增加流量带宽子账单接口/把 regionId 改为 nodeRegionId 2022-10-14 16:17:18 +08:00
刘祥超
a5af5e0597 可以使用账单号获取账单信息 2022-10-14 10:56:01 +08:00
刘祥超
9af8721d8d 优化代码 2022-10-14 10:03:49 +08:00
刘祥超
0d8c8064bf 增加多个服务带宽、流量统计接口 2022-10-03 19:26:47 +08:00
刘祥超
010a9b61d5 删除不必要的代码 2022-10-01 07:17:41 +08:00
刘祥超
b277af4ac2 优化服务列表API 2022-09-30 19:04:36 +08:00
刘祥超
b85016d6c9 完善订单、DNS套餐服务API 2022-09-29 19:21:35 +08:00
刘祥超
10ea8911bc 完善订单、支付相关接口定义 2022-09-29 10:22:51 +08:00
刘祥超
fbfcc8d2c0 阶段性提交 2022-09-28 17:39:28 +08:00
刘祥超
11c2682f59 删除不必要的文件 2022-09-28 09:03:52 +08:00
刘祥超
8a005850db 查询5分钟流量接口增加timeFrom和timeTo参数 2022-09-24 18:55:25 +08:00
刘祥超
a0acc54565 服务流量接口增加5分钟查询接口 2022-09-24 18:36:34 +08:00
刘祥超
8234d0301f 优化智能DNS相关接口 2022-09-24 14:07:59 +08:00
刘祥超
59de46d34f 智能DNS支持应答模式配置 2022-09-23 19:02:21 +08:00
刘祥超
0dcdde06e0 删除不必要的文件 2022-09-23 14:42:52 +08:00
刘祥超
b9b7ca4c41 智能DNS访问日志增加只记录失败查询选项 2022-09-22 18:41:51 +08:00
刘祥超
5244b53270 增加防盗链功能 2022-09-22 16:33:42 +08:00
刘祥超
dd460bc40d 每个服务只初始化一次 2022-09-22 11:09:11 +08:00
刘祥超
d9fb6a2c84 优化代码 2022-09-21 15:09:23 +08:00
刘祥超
c05072d12e 增加查找NS集群主机地址的API 2022-09-21 15:06:01 +08:00
刘祥超
caba96bfeb DNS集群设置时区 2022-09-19 17:00:27 +08:00
刘祥超
a36344f3bb 同步域名解析时不再强制要求修复节点问题 2022-09-18 10:40:43 +08:00
刘祥超
5db4568d7e 优化代码/DNS域名增加分页 2022-09-18 10:22:47 +08:00
刘祥超
4e219b09e0 集群增加自动安装nftables配置 2022-09-18 09:26:01 +08:00
刘祥超
1d7986289d 集群增加是否远程启动选项 2022-09-17 15:11:25 +08:00
刘祥超
710ec09b14 检查域名是否存在时同时检查泛域名 2022-09-17 11:38:51 +08:00
刘祥超
324d24fe5a 创建集群的时候初始化全局服务配置 2022-09-16 19:34:24 +08:00
刘祥超
0f060002e3 集群设置中增加服务设置 2022-09-16 18:41:10 +08:00
刘祥超
2eb19348cc 访问日志中增加源站状态码 2022-09-16 10:07:29 +08:00
刘祥超
a92d8887e3 集群增加自动同步时钟选项 2022-09-15 15:57:16 +08:00
刘祥超
f914802b06 可以修改界面上显示的组件 2022-09-13 19:55:07 +08:00
刘祥超
a2f21e8d58 用户系统可以切换CDN和智能DNS 2022-09-13 19:04:07 +08:00
刘祥超
cf4b9a49ed 增加DNS套餐相关API 2022-09-13 10:50:26 +08:00
刘祥超
da02e6df4a 优化代码 2022-09-11 14:41:14 +08:00
刘祥超
a092f496dd NS域名可以使用状态筛选 2022-09-10 17:00:24 +08:00
刘祥超
98ad80e1cb 实现DNS域名验证 2022-09-10 16:13:26 +08:00
刘祥超
6ed4e2315f NS域名增加状态 2022-09-09 15:25:46 +08:00
刘祥超
55ce6570f8 增加一些NS相关API 2022-09-08 19:36:13 +08:00
刘祥超
70fa03e93b 创建集群的时候可以设置DNS记录的默认TTL 2022-09-08 11:02:36 +08:00
刘祥超
8fe1480d8d 增加获取NS节点最新版本API 2022-09-08 10:42:33 +08:00
刘祥超
f796dc5921 支持中文域名(转换punycode后)访问 2022-09-07 17:08:59 +08:00
刘祥超
f725f3bb07 增加简化的缓存条件设置 2022-09-03 18:15:56 +08:00
刘祥超
f3db0d5b69 NS记录可以按照名称、类型、TTL排序 2022-09-03 16:00:20 +08:00
刘祥超
b6fe3df3d6 DDoS防护增加秒级连接速率限制 2022-08-31 10:01:00 +08:00
刘祥超
5c885050fa 优化鉴权 2022-08-30 11:24:07 +08:00
刘祥超
b54c44bfb9 用户系统也可以申请ACME证书 2022-08-28 20:02:20 +08:00
刘祥超
78664a9218 增加修改全体用户功能API 2022-08-28 17:00:59 +08:00
刘祥超
def814ff5c 提供按小时、按天查询带宽峰值的API 2022-08-28 15:56:33 +08:00
刘祥超
1bf13d381d 服务列表带宽使用新的算法 2022-08-27 18:37:51 +08:00
刘祥超
2975ba8701 WAF默认不启用SQL注释,减少错误检测 2022-08-26 21:08:24 +08:00
刘祥超
14759c3621 可以修改服务的CNAME 2022-08-26 19:51:12 +08:00
刘祥超
5ae65edd0a DDoS防护增加单IP TCP新连接速率黑名单 2022-08-26 11:31:50 +08:00
刘祥超
184d6f97bf Ln节点可以指定访问IP 2022-08-25 20:37:15 +08:00
刘祥超
8972acdfd7 集群DNS设置中增加”包含Ln节点“选项 2022-08-25 19:18:25 +08:00
刘祥超
a957903b98 节点运行日志可以按照节点ID设置为已读 2022-08-25 18:26:40 +08:00
刘祥超
b029c1e95a 优化JS Cookie验证文字提示 2022-08-25 16:03:18 +08:00
刘祥超
095ff97149 增加Javascript Cookie验证 2022-08-25 15:36:01 +08:00
刘祥超
d00ebb31aa NS批量操作时增加用户ID选项 2022-08-25 11:36:45 +08:00
刘祥超
1c7f51c5e0 优化代码 2022-08-24 16:13:16 +08:00
刘祥超
acc70669f1 修复从API代码中提取角色时无法查找子目录的Bug 2022-08-23 21:55:59 +08:00
刘祥超
2add42a284 从API代码中提取节点角色时可以保留多个 2022-08-23 21:41:54 +08:00
刘祥超
225d03a65f 实现IP库制品管理API、自动更新程序 2022-08-23 14:54:53 +08:00
刘祥超
f851212a55 NS节点基本的DDoS防护 2022-08-22 15:10:48 +08:00
刘祥超
199794a63a 优化IP库内存 2022-08-22 08:31:53 +08:00
刘祥超
49129ea227 优化IP库内存使用 2022-08-21 23:09:25 +08:00
刘祥超
91b865dd23 在Daemon模式下不初始化IP库 2022-08-21 21:13:03 +08:00
刘祥超
0e311496b6 实现集成的IP库/初步完成IP库制作API 2022-08-21 20:36:56 +08:00
刘祥超
ed0f627ae4 延长WAF默认封禁时间:阻止动作从60秒增加到300秒;SYN Flood从600秒延长到1800秒 2022-08-20 22:38:12 +08:00
刘祥超
569f27918a 智能DNS增加若干查询记录统计接口 2022-08-20 19:58:38 +08:00
刘祥超
ec02d83ee6 IP库阶段性提交(未完成) 2022-08-14 19:42:05 +08:00
刘祥超
debf4a5249 IP库阶段性提交(未完成) 2022-08-14 16:33:11 +08:00
刘祥超
4ddc1ac89f 新版IP库管理阶段性提交(未完成) 2022-08-13 23:55:59 +08:00
刘祥超
507457dec1 增加批量操作NS域名和记录相关接口 2022-08-09 21:03:04 +08:00
刘祥超
525429bbb0 访问日志ES存储增加Data Stream模式 2022-08-09 18:12:15 +08:00
刘祥超
25e33ac1ee 缓存条件增加If-None-Match和If-Modified-Since是否回源选项 2022-08-07 16:37:03 +08:00
刘祥超
f428cb2de9 优化代码 2022-08-07 15:09:39 +08:00
刘祥超
86070bc8aa 增加NS域名分组、批量添加域名和记录接口 2022-08-06 20:29:26 +08:00
刘祥超
afef00b473 增加查找使用某个证书的NS集群数量的API 2022-08-04 16:25:00 +08:00
刘祥超
812e9482af 自动从代码中分析接口方法所适用的角色 2022-08-04 16:01:57 +08:00
刘祥超
fa5dc80426 优化代码 2022-08-04 11:52:28 +08:00
刘祥超
76b6a5c847 优化代码 2022-08-04 11:24:50 +08:00
刘祥超
a19b738d73 源站读取错误时自动尝试下一个源站 2022-08-03 20:33:31 +08:00
刘祥超
526d72fd99 增加工单相关接口定义 2022-08-03 10:45:23 +08:00
刘祥超
7ea8189c19 服务带宽API增加按月、按日查询接口 2022-08-01 15:41:07 +08:00
刘祥超
c453222774 实现用户订单、在线支付接口(商业版可用) 2022-07-31 19:57:28 +08:00
刘祥超
905163880c EdgeDNS:访问日志增加集群和记录类型筛选 2022-07-27 20:19:19 +08:00
刘祥超
ac106ca4f0 NS节点也增加DDoS配置 2022-07-27 19:41:51 +08:00
刘祥超
262dcf0c8d 智能DNS支持自定义端口 2022-07-27 16:56:32 +08:00
刘祥超
f6b3a0b829 修改文字 2022-07-26 11:28:09 +08:00
刘祥超
ac7ab51dbb 智能DNS增加中国香港、中国澳门、中国台湾、中国大陆、海外等线路 2022-07-26 11:15:05 +08:00
刘祥超
41ea2d0dda 用户实现OTP登录 2022-07-24 16:45:07 +08:00
刘祥超
a4b68cd21c 用户列表中显示实名审核状态 2022-07-24 11:57:38 +08:00
刘祥超
70d864191e 相关接口增加实名认证参数 2022-07-24 11:29:28 +08:00
刘祥超
9fdbaf92cc 可以重置用户实名认证状态 2022-07-24 10:08:13 +08:00
刘祥超
a75e57a8f7 实现基础的实名认证功能 2022-07-24 09:56:34 +08:00
刘祥超
9b187dadbe API节点信息增加instanceCode(实例代号)字段 2022-07-21 19:20:06 +08:00
刘祥超
3ab783bbd0 节点状态中增加主程序位置字段 2022-07-21 15:17:42 +08:00
刘祥超
0e95a5a4bc 增加proto相关注释 2022-07-20 18:14:57 +08:00
刘祥超
dcec62a1a2 服务配置中增加CNAME相关信息 2022-07-18 09:12:58 +08:00
刘祥超
9117af3021 增加用户身份认证相关接口 2022-07-17 10:59:55 +08:00
刘祥超
65095d1290 WAF策略增加记录区域封禁日志选项 2022-07-16 18:45:50 +08:00
刘祥超
380bf3e1c2 集群DNS设置增加允许通过CNAME访问网站服务选项 2022-07-14 09:49:41 +08:00
刘祥超
0090cf1e30 安全设置中增加禁止搜索引擎、禁止爬虫、允许访问的域名等选项 2022-07-07 19:59:00 +08:00
刘祥超
a24cbe43b6 服务看板中增加峰值带宽数据 2022-07-07 17:07:24 +08:00
刘祥超
0976b35357 用户界面设置中增加流量、带宽相关设置 2022-07-07 12:40:19 +08:00
刘祥超
84f8a7192e 增加按用户统计带宽/升级Go为1.18 2022-07-07 09:19:15 +08:00
刘祥超
22479278b9 增加服务带宽统计 2022-07-05 20:09:19 +08:00
刘祥超
f27a55c2b6 增加UAM(5秒盾)集群设置 2022-07-03 22:10:18 +08:00
刘祥超
106bd2ae87 反向代理设置中增加移除回源主机名端口功能 2022-06-30 12:11:30 +08:00
刘祥超
ef4eb23226 实现源站端口跟随功能 2022-06-29 21:55:37 +08:00
刘祥超
d9a2284936 DNS自定义线路中增加CIDR、区域以及排除功能 2022-06-28 20:26:44 +08:00
刘祥超
62027e95b2 支持ZSTD压缩 2022-06-27 22:41:11 +08:00
刘祥超
551cb2f3c1 增加修改服务所在分组API/proto文件增加若干注释 2022-06-25 19:22:19 +08:00
刘祥超
75dcaa7e7e 优化rpc json生成程序 2022-06-22 14:36:33 +08:00
刘祥超
c6952e794a 编译proto文件时自动生成RPC列表JSON 2022-06-21 21:26:22 +08:00
刘祥超
7ad94bff2f fix typo 2022-06-21 09:58:56 +08:00
刘祥超
20469e0487 修复源站设置专属域名导致其他源站不可用的Bug 2022-06-20 07:46:41 +08:00
刘祥超
4834cb2886 增加统计服务某日、某月流量API 2022-06-18 14:25:33 +08:00
刘祥超
1f33bcfee7 创建IPList时可以指定服务ID 2022-06-15 19:22:56 +08:00
刘祥超
8f8e769ead 优化缓存任务Key状态增加执行中状态 2022-06-15 15:18:22 +08:00
刘祥超
03296c7530 城市API增加省份信息 2022-06-14 17:37:32 +08:00
刘祥超
fecfb0f888 WAF规则中增加${requestURL}参数 2022-06-09 19:43:52 +08:00
刘祥超
5e277de82b API相关提示更详细 2022-06-08 15:17:21 +08:00
刘祥超
de4366bbc3 节点状态中增加时间戳字段 2022-06-07 11:45:45 +08:00
刘祥超
a8d38937e3 可以设置用户每天执行缓存任务的额度 2022-06-05 21:15:44 +08:00
刘祥超
0ea04c9883 增加刷新、预热缓存任务管理 2022-06-05 17:14:14 +08:00
刘祥超
85553bf5d9 优化源站有专属域名情况下的算法 2022-05-23 19:59:05 +08:00
刘祥超
24f8ccb166 NodeService.FindAllEnabledNodesWithNodeClusterIdRequest()增加参数includeSecondary 2022-05-23 19:56:11 +08:00
刘祥超
fb0b1d42ef 增加LICENSE和README 2022-05-22 11:36:49 +08:00
刘祥超
38005406b0 WAF默认验证码页面显示请求ID 2022-05-21 20:00:34 +08:00
刘祥超
6aee09b22b 新创建WAF时增加默认选项 2022-05-21 18:57:59 +08:00
刘祥超
8c450a9b43 WAF策略中增加验证码相关定制设置 2022-05-20 22:10:58 +08:00
刘祥超
a660815df7 健康检查增加是否记录访问日志选项 2022-05-19 17:13:26 +08:00
刘祥超
2b6d749566 实现基础的DDoS防护 2022-05-18 21:02:58 +08:00
刘祥超
dd900700e9 增加通过IP删除IP名单中IP参数 2022-05-10 15:12:03 +08:00
刘祥超
032b171025 增加注释 2022-05-08 19:33:33 +08:00
刘祥超
ab005caa4a DNS服务商增加厂家筛选 2022-05-07 20:41:28 +08:00
刘祥超
8e91af92ae 路由规则可以单独设置UAM(仅企业版可用) 2022-05-04 20:32:03 +08:00
刘祥超
97575e29a1 节点增加DNS解析库类型设置 2022-05-04 16:40:43 +08:00
刘祥超
146b57216a WebP判断内容长度时,忽略ChunkEncoding传输的内容 2022-04-29 10:50:04 +08:00
刘祥超
2cdb0b9758 API节点增加主节点概念 2022-04-24 15:25:08 +08:00
刘祥超
a6355914b2 集群概要信息中增加系统服务状态 2022-04-22 22:04:25 +08:00
刘祥超
fdcb83cc5f IP地址信息中增加isHealthy字段 2022-04-22 21:56:41 +08:00
刘祥超
57bffa2cda 访问日志策略增加只记录WAF访问日志选项 2022-04-22 17:14:17 +08:00
刘祥超
e7189948f2 节点状态中增加本地防火墙名称 2022-04-22 14:58:50 +08:00
刘祥超
59a3a90d99 增加WAF日志配置 2022-04-21 19:44:33 +08:00
刘祥超
5619d0e026 节点状态记录是否检查到本地防火墙 2022-04-21 18:14:58 +08:00
刘祥超
21d58764cc IP列表增加名单类型筛选 2022-04-21 15:09:11 +08:00
刘祥超
2e17fc4368 WAF策略默认开启记录日志 2022-04-21 09:38:56 +08:00
刘祥超
4e921330db 服务修改所属集群时可以选择是否保留旧的配置 2022-04-18 21:11:52 +08:00
刘祥超
83423c1450 访问日志可以使用分表查询 2022-04-17 16:18:58 +08:00
刘祥超
9d011f4982 服务列表增加下行带宽 2022-04-15 12:14:42 +08:00
刘祥超
6369458877 将“HTTP反向代理”改为“CDN加速”/调整部分文字描述 2022-04-14 15:59:21 +08:00
刘祥超
f855996d53 改进单元测试 2022-04-14 15:21:20 +08:00
刘祥超
e75248ad6b 服务列表中分组信息中增加UserId字段 2022-04-13 15:01:55 +08:00
刘祥超
8ccbe99fbc 按天统计流量接口可以预估某日同时间流量 2022-04-10 21:25:29 +08:00
刘祥超
977358a3ab 增加当日统计接口 2022-04-07 19:46:54 +08:00
刘祥超
67154237fb 优化节点列表 2022-04-07 18:31:51 +08:00
刘祥超
d71b545971 增加Sendfile相关选项 2022-04-04 19:45:26 +08:00
刘祥超
7908d70750 优化代码 2022-04-04 19:44:45 +08:00
刘祥超
8722c8a34a 优化代码 2022-04-04 16:42:56 +08:00
刘祥超
7488d0b334 支持L2节点配置 2022-04-04 16:42:11 +08:00
刘祥超
d86f2bc1a4 集群可以设置WebP策略 2022-04-01 16:20:36 +08:00
刘祥超
6c1514cad7 用户权限增加5秒盾、WebP 2022-03-31 15:52:23 +08:00
刘祥超
c132bd9c1e 优化代码 2022-03-31 15:19:13 +08:00
刘祥超
f733282b48 可以用域名搜索DNS账号 2022-03-30 11:15:42 +08:00
刘祥超
4396b0fe74 IP列表可以使用级别筛选 2022-03-30 09:39:21 +08:00
刘祥超
04ab37ce23 商业版增加UAM功能 2022-03-29 21:24:36 +08:00
刘祥超
01ca35f6a9 可以自行设定指标数据保留时间 2022-03-28 09:36:56 +08:00
刘祥超
1c337ec40e 按日统计的指标数据保留时间改为8个周期 2022-03-28 09:03:01 +08:00
刘祥超
aebafacda2 优化代码 2022-03-28 08:48:35 +08:00
刘祥超
59b93bfbe1 优化看板打开速度 2022-03-27 16:39:41 +08:00
刘祥超
eccc56369b 时区增加UTC 2022-03-26 10:32:56 +08:00
刘祥超
75221eb855 管理界面和用户界面增加时区设置 2022-03-26 10:26:24 +08:00
刘祥超
b8d45ab42e 增加脚本相关配置和RPC接口 2022-03-25 14:12:12 +08:00
刘祥超
bc2c6a56ed CA证书支持只有一级证书 2022-03-24 09:25:48 +08:00
刘祥超
1becfdcd67 可以修复单页或者全部服务日志 2022-03-23 17:31:11 +08:00
刘祥超
b90e684580 更新相关库 2022-03-20 10:43:52 +08:00
刘祥超
a3bd4b1b0a 改进SQL注入检测 2022-03-19 15:41:25 +08:00
刘祥超
b6f4e5ce13 OCSP支持过期时间 2022-03-18 20:20:28 +08:00
刘祥超
5fd12b809a 动态更新OCSP 2022-03-18 17:04:53 +08:00
刘祥超
573f1fe22f 源站支持自定义回源主机名 2022-03-17 15:48:16 +08:00
刘祥超
024f30ec36 增加置顶集群功能 2022-03-17 11:13:04 +08:00
刘祥超
87a3df3645 IPSet支持IPv6 2022-03-16 20:48:00 +08:00
刘祥超
470314c32e 节点可以单独设置缓存目录 2022-03-16 15:24:56 +08:00
刘祥超
6bf49e4532 初始化文件缓存选项时同时也初始化内存策略 2022-03-15 21:34:12 +08:00
刘祥超
b7dfd4390a 缓存策略可以使用存储类型筛选 2022-03-14 15:42:20 +08:00
刘祥超
454e2ad4fe 实现回源跟随功能 2022-03-14 15:07:33 +08:00
刘祥超
9d859f3c27 增加证书OCSP错误日志管理 2022-03-11 20:27:57 +08:00
刘祥超
25061495d0 增加OCSP Stapling功能 2022-03-10 11:54:58 +08:00
刘祥超
8eca3165df 支持使用小时筛选访问日志 2022-03-09 11:01:00 +08:00
刘祥超
97690f4dfd 增加对访问日志自动分表配置 2022-03-09 10:01:29 +08:00
刘祥超
757d316a53 当缓存条件状态码为206时,自动支持区间缓存 2022-03-04 17:00:37 +08:00
刘祥超
1d681d5029 更新protoc版本 2022-03-04 15:44:39 +08:00
刘祥超
9a8dc8b73e 更新TeaGo 2022-03-04 15:11:58 +08:00
刘祥超
ff6b526747 添加 yaml.v3 2022-03-04 12:28:09 +08:00
刘祥超
4c8081c52b 更新相关依赖库 2022-03-04 12:21:23 +08:00
刘祥超
d93f9b84b5 增加206 partial content相关缓存配置 2022-03-03 19:36:58 +08:00
刘祥超
c9ae4c4289 管理界面设置增加每页显示数设置 2022-02-26 21:02:34 +08:00
刘祥超
274c4fe122 缓存可以设置是否使用系统默认设置 2022-02-24 20:39:17 +08:00
刘祥超
6be39294d0 增加是否同步写入压缩缓存设置 2022-02-24 20:13:16 +08:00
刘祥超
08b9db2f96 URL跳转可以设置是否保留参数 2022-02-20 09:17:38 +08:00
刘祥超
5a781d8d17 支持默认价格设置 2022-01-23 20:16:11 +08:00
刘祥超
690ddb99b8 实现带宽计费套餐 2022-01-23 19:16:58 +08:00
刘祥超
9ef1882a79 支持单个服务更新 2022-01-19 22:15:52 +08:00
刘祥超
dc001cc06c 增加API方法调用耗时统计 2022-01-19 16:54:03 +08:00
刘祥超
487ecbc1a5 源站支持客户端证书 2022-01-16 19:51:30 +08:00
刘祥超
406d5dcc63 CAPTCHA增加多个选项 2022-01-16 16:53:34 +08:00
刘祥超
89e8ef41fc 实现open file cache 2022-01-12 21:09:23 +08:00
刘祥超
fa4ba857bd 可以使用集群搜索WAF策略、缓存策略 2022-01-11 15:46:21 +08:00
刘祥超
b52dd3a2dc 运行日志可以使用集群、节点筛选 2022-01-11 14:59:45 +08:00
刘祥超
94d2867129 访问日志可以使用集群和节点搜索 2022-01-11 12:02:40 +08:00
刘祥超
86f10b8ec2 增加读取当日拦截数API 2022-01-11 09:47:05 +08:00
刘祥超
7aff314e72 增加默认的SYN Flood配置 2022-01-10 20:07:43 +08:00
刘祥超
b2d477345f 实现自动SYN Flood防护 2022-01-10 19:54:18 +08:00
刘祥超
b4f5f28102 WAF模板--爬虫工具默认不封禁搜索引擎 2022-01-10 10:56:08 +08:00
刘祥超
1ddb90ef74 WAF模板--爬虫工具增加白名单 2022-01-10 10:27:08 +08:00
刘祥超
78456d2205 WAF模板中特殊目录增加.env 2022-01-09 20:29:46 +08:00
刘祥超
e6aa738435 WAF策略增加是否使用本地防火墙设置 2022-01-09 17:05:24 +08:00
刘祥超
7f53af08e6 节点配置增加产品信息 2022-01-09 10:44:26 +08:00
刘祥超
8517eec344 IP名单增加未读状态 2022-01-08 16:48:01 +08:00
刘祥超
5fcdc76b8c 增加geo、isp、browser相关变量说明 2022-01-06 17:05:24 +08:00
刘祥超
23830c977c 增加城市/ISP查询接口;WAF增加国家/地区、省份、城市、ISP等参数 2022-01-06 16:24:33 +08:00
刘祥超
572d41985a 用户创建服务时刻自动添加到分组、自动添加统计功能、设置是否需要绑定套餐 2022-01-05 15:55:25 +08:00
刘祥超
b03970f578 用户列表可以使用待审核、关键词搜索 2022-01-05 11:11:54 +08:00
刘祥超
36974e3eb4 实现用户审核功能 2022-01-05 10:44:58 +08:00
刘祥超
6eec55f3c3 优化脚本代码配置 2022-01-03 21:51:24 +08:00
刘祥超
d5e21f2c95 尝试自动在firewalld中开放端口 2022-01-03 16:28:02 +08:00
刘祥超
94e9ab5a0e 节点日志可以使用标签筛选 2022-01-03 11:03:43 +08:00
刘祥超
30f53d50f0 增加边缘脚本相关配置 2022-01-01 21:51:15 +08:00
刘祥超
2bf0c40328 增加常量 2021-12-22 16:43:26 +08:00
刘祥超
4c2156a4ac 增加自动检查系统更新设置 2021-12-21 15:17:04 +08:00
刘祥超
a913996000 优化内置WAF模板 2021-12-21 12:08:49 +08:00
刘祥超
a74f930d42 访问日志限制字段 2021-12-19 20:39:27 +08:00
刘祥超
4e22f4954e 修改相关域名 2021-12-17 14:19:43 +08:00
刘祥超
73d049b380 增加stale cache配置 2021-12-16 17:28:02 +08:00
刘祥超
9171242c37 HTTP Header:实现请求方法、域名、状态码等限制,实现内容替换功能 2021-12-14 21:26:43 +08:00
刘祥超
12985e1bd9 定义访问日志队列 2021-12-14 12:42:47 +08:00
刘祥超
d5af0a740a 优化代码 2021-12-13 15:36:46 +08:00
刘祥超
28e93c05a2 优化代码 2021-12-13 15:31:03 +08:00
刘祥超
173175a248 优化代码 2021-12-13 14:58:45 +08:00
刘祥超
abd5c6dbb1 WAF策略:可以修改分组代号/导入时可以根据名称合并 2021-12-12 20:24:41 +08:00
刘祥超
b333a90532 服务分组可以设置请求限制 2021-12-12 17:07:12 +08:00
刘祥超
309f0ea85b 路由规则增加专属域名设置 2021-12-12 16:38:18 +08:00
刘祥超
f080088e19 请求条件增加不区分大小写选项 2021-12-12 16:11:11 +08:00
刘祥超
15c952e6eb 实现请求连接数等限制 2021-12-12 11:44:58 +08:00
刘祥超
31a04b32b1 支持设置单节点最大线程数、单节点TCP最大连接数 2021-12-09 18:49:44 +08:00
刘祥超
252b8af422 可以在缓存条件里设置Expires Header 2021-12-08 17:40:27 +08:00
刘祥超
00e46c6e3d 增加批量增加节点IP接口 2021-12-07 18:21:39 +08:00
刘祥超
fe000f23bd 访问日志实现记录requestBody 2021-12-07 14:57:33 +08:00
刘祥超
fe9a942f4c 访问日志暂时去除响应Body字段 2021-12-07 14:57:14 +08:00
刘祥超
50893b34c6 缓存默认支持所有请求方法 2021-12-07 10:48:11 +08:00
刘祥超
01c7f2b714 缓存支持请求方法设置 2021-12-07 10:43:34 +08:00
刘祥超
50bd3fd1a9 增加${isArgs}变量说明 2021-12-07 10:04:03 +08:00
刘祥超
709e499de6 SSH认证支持sudo 2021-12-06 19:23:18 +08:00
刘祥超
d56b1c2f83 SSL证书自动设置Leaf字段以提升性能 2021-12-06 10:54:43 +08:00
刘祥超
0c2215ec74 自动将API节点的IP加入到白名单,防止误封 2021-12-06 10:09:44 +08:00
刘祥超
acffd92fad 服务看板增加区域地图 2021-12-06 09:16:12 +08:00
刘祥超
b2af030559 商业版WAF看板增加地图 2021-12-05 19:38:23 +08:00
刘祥超
effd44c53b 国家/地区统计时上传流量、攻击量等信息 2021-12-05 18:57:42 +08:00
刘祥超
6545dfa3e3 优化看板 2021-12-03 15:49:10 +08:00
刘祥超
ac8752ec4f 优化指标统计数据清理 2021-12-03 15:49:04 +08:00
刘祥超
c396a8e7f8 支持规则集忽略局域网IP 2021-12-02 16:08:29 +08:00
刘祥超
6a22ee94d5 反向代理如果只有一个源站时,则快速返回,避免因为状态的改变而不停地转换 2021-12-02 15:04:39 +08:00
刘祥超
9184986717 多个提示页面增加请求ID 2021-12-02 14:44:56 +08:00
刘祥超
b90115ba2e 缓存配置增加是否支持Cache-Control: max-age=... 2021-12-02 10:17:55 +08:00
刘祥超
fefe52a56f 增加AddAgeHeader 2021-12-02 10:03:14 +08:00
刘祥超
f0d3cc7d4d 查询源站时加互斥锁 2021-12-01 21:20:42 +08:00
刘祥超
06523bf61f 增加是否记录499选项 2021-12-01 21:14:07 +08:00
刘祥超
615711924a 审核中服务增加提交审核时间 2021-12-01 17:06:32 +08:00
刘祥超
211d3c5067 服务分模块加载,防止某个模块加载失败时相互受影响 2021-12-01 15:59:15 +08:00
刘祥超
545aa771bf 节点配置初始化时返回服务相关错误 2021-12-01 15:51:05 +08:00
刘祥超
99af79c9c2 优化节点日志 2021-11-30 16:42:27 +08:00
刘祥超
02420aacc5 优化代码 2021-11-29 20:36:02 +08:00
刘祥超
f7e8345f88 用户账单增加多个API 2021-11-29 14:34:05 +08:00
刘祥超
5bd21861ab 增加对map进行base64编解码的函数 2021-11-29 10:49:56 +08:00
刘祥超
01d575edb0 完善套餐 2021-11-28 20:11:54 +08:00
刘祥超
557576cafc 节点任务查询时增加排除的任务类型 2021-11-27 17:07:06 +08:00
刘祥超
12a2dde2e6 优化检查端口是否已被使用API 2021-11-24 19:47:48 +08:00
刘祥超
ccb326603c 优化API命名 2021-11-24 12:01:00 +08:00
刘祥超
c5d47ae35d 优化RPC相关错误的文字提示 2021-11-21 15:55:52 +08:00
刘祥超
d2c99b4db4 增加批量从IP名单中删除IP的API 2021-11-21 09:42:39 +08:00
刘祥超
561150ab28 增加获取当前API节点信息API/增加修改节点API节点地址消息类型 2021-11-21 09:41:38 +08:00
刘祥超
d60e0164db IP地址“健康检查失败”阈值改为“健康检查结果” 2021-11-18 14:47:34 +08:00
刘祥超
886969f4ee 节点IP阈值增加节点健康检查失败 2021-11-18 14:30:58 +08:00
刘祥超
422b71d9b1 修复查找域名对应服务时可能造成的死锁 2021-11-17 21:57:03 +08:00
刘祥超
89fa27a883 IP名单增加是否只显示自动拦截名单选项 2021-11-17 20:25:40 +08:00
刘祥超
a67cf6b596 增加全局查看、检索IP功能 2021-11-17 19:51:04 +08:00
刘祥超
80b21ea362 IP名单增加是否全局 2021-11-17 16:15:01 +08:00
刘祥超
35b69141c1 IP名单中IP创建时保存相关节点、服务、WAF策略信息 2021-11-16 16:10:52 +08:00
刘祥超
f4edd45886 提升通过域名查找服务的性能,轻松支持海量域名 2021-11-15 16:56:31 +08:00
刘祥超
a95fe17ebc IP名单API增加IP添加时间 2021-11-15 11:31:20 +08:00
刘祥超
f178c19348 缓存策略增加若干选项 2021-11-14 16:21:31 +08:00
刘祥超
a46ba472bb 缓存策略增加若干选项 2021-11-13 21:02:09 +08:00
刘祥超
3c10fa6b0a 节点配置支持压缩传输 2021-11-11 14:17:45 +08:00
刘祥超
2d9bb319fe 增加账单、套餐相关配置 2021-11-11 08:31:32 +08:00
刘祥超
698d1b697c 改进流量限制 2021-11-10 14:39:13 +08:00
刘祥超
fd4dbb6fe6 将带宽限制改为流量限制 2021-11-09 17:36:34 +08:00
刘祥超
a1ac68ccf0 支持套餐相关操作 2021-11-09 15:36:31 +08:00
刘祥超
d92e987f7a 支持购买套餐/续费套餐/用户账户操作等 2021-11-08 20:52:21 +08:00
刘祥超
b82732af42 增加删除/恢复DNS域名API 2021-11-06 16:23:06 +08:00
刘祥超
2fff7568b9 SSH登录支持Passphrase 2021-11-06 15:31:11 +08:00
刘祥超
9c4c6cc660 增加多个API/规范命名 2021-11-05 17:56:30 +08:00
刘祥超
ae4a8dfa5f 增加APINodeService.CountAllEnabledAndOnAPINodes()/规范命名 2021-11-05 15:35:35 +08:00
刘祥超
f90c8db556 增加套餐相关代码 2021-10-29 14:02:49 +08:00
刘祥超
81a17aaad9 增加在IP名单中使用ipFrom和ipTo查找IP的API 2021-10-26 09:17:13 +08:00
刘祥超
9b709515da WAF增加显示网页动作 2021-10-25 19:40:22 +08:00
刘祥超
f502309198 WAF模板增加防盗链 2021-10-25 19:02:44 +08:00
刘祥超
f5c3affc5f WAF模板中增加空Agent和随机字符拦截规则 2021-10-25 11:57:25 +08:00
刘祥超
3a30a65264 HTTP客户端IP获取方式增加是否自定义 2021-10-22 14:40:14 +08:00
刘祥超
b040e966ca 可以在IP名单中搜索IP 2021-10-22 12:18:58 +08:00
刘祥超
36bf9e2fab 实现单个服务的带宽限制(商业版) 2021-10-21 17:09:59 +08:00
刘祥超
5c896bbf22 修改Edge-Health-Check-Key为X-Edge-Health-Check-Key 2021-10-19 16:41:28 +08:00
刘祥超
4d57a12a29 健康检查支持UserAgent和是否基础请求设置 2021-10-19 16:31:33 +08:00
刘祥超
cc42217c0c 增加防盗链规则参数 2021-10-19 11:38:39 +08:00
刘祥超
286174202e WAF各个动作增加作用范围 2021-10-19 10:28:18 +08:00
刘祥超
c708d83af0 内容压缩支持对已压缩内容重新压缩 2021-10-18 16:49:24 +08:00
刘祥超
1044cfc17c 默认压缩算法改为brotli优先 2021-10-18 11:56:58 +08:00
刘祥超
6d99535850 增加PURGE某个URL缓存功能 2021-10-17 20:22:01 +08:00
刘祥超
3a798f09ed 增加清除服务缓存API 2021-10-17 17:12:36 +08:00
刘祥超
e41ce4bb4c 增加服务CNAME相关配置和服务 2021-10-16 12:03:44 +08:00
刘祥超
226051e555 运行日志显示未读的日志数量 2021-10-15 12:54:38 +08:00
刘祥超
887a83cf9d 节点日志增加是否已读标记 2021-10-14 17:29:58 +08:00
刘祥超
be5c54d903 支持PROXY Protocol 2021-10-12 20:18:02 +08:00
刘祥超
90d71811dc 可以在集群中指定节点时区 2021-10-12 11:45:28 +08:00
刘祥超
3b20cea1b8 TCP、TLS、UDP支持端口范围 2021-10-10 16:30:09 +08:00
刘祥超
98b20164a9 服务分组增加特殊页面设置 2021-10-10 10:53:09 +08:00
刘祥超
cc7a64189b 特殊页面可以直接使用HTML 2021-10-10 10:35:09 +08:00
刘祥超
b5ef0d33b5 智能DNS记录的TTL增加多个秒、分钟、年等档位 2021-10-09 16:13:02 +08:00
刘祥超
e61e81b485 WAF看板增加节点拦截排行和域名拦截排行 2021-10-09 16:01:42 +08:00
刘祥超
4220913c28 增加查看单个服务附近服务API 2021-10-08 14:36:22 +08:00
刘祥超
491ad3ae44 支持更多的分组全局设置功能 2021-10-07 16:47:31 +08:00
刘祥超
48678e9533 WAF模式从pass改为bypass 2021-10-07 13:51:35 +08:00
刘祥超
45921f4b50 服务支持自定义访客IP地址获取方式 2021-10-06 11:42:52 +08:00
刘祥超
a95ac1599f 优化WebP+缓存 2021-10-03 18:01:12 +08:00
刘祥超
30f84532b7 ACME使用EAB申请的账号只能绑定一个用户 2021-10-03 14:43:00 +08:00
刘祥超
4da092eb2b ACME证书增加ZeroSSL支持 2021-10-03 13:09:55 +08:00
刘祥超
f01a45a746 支持自动转换图像文件为WebP 2021-10-01 16:25:31 +08:00
刘祥超
702c631460 WAF策略增加观察模式和通过模式 2021-09-30 11:30:16 +08:00
刘祥超
bfa672dd37 压缩匹配条件时也检查请求条件 2021-09-29 20:11:54 +08:00
刘祥超
04f47fe8ed 支持brotli和deflate压缩方式 2021-09-29 19:37:32 +08:00
刘祥超
b488c76204 CC超时时间默认为600秒 2021-09-29 11:17:18 +08:00
刘祥超
0e8ad10f61 看板增加离线节点数字 2021-09-27 09:23:30 +08:00
刘祥超
d660258bb6 缓存条件增加最小内容尺寸配置 2021-09-26 15:02:03 +08:00
刘祥超
bb20773f2a ReadMe增加reporterconfigs说明 2021-09-26 10:10:20 +08:00
刘祥超
3a5ef923f4 优化WAF规则错误提示 2021-09-25 19:58:17 +08:00
刘祥超
f2a91018f6 WAF规则校验错误不阻断执行 2021-09-25 19:41:10 +08:00
刘祥超
16c729085e 调整CC2参数,增加单IP请求数限制 2021-09-23 15:01:12 +08:00
刘祥超
6b33fb52e0 修复负载均衡算法中可能因参数缺失而崩溃的Bug 2021-09-23 11:43:21 +08:00
刘祥超
96ddf1ded8 可以使用服务ID来查询服务分组配置信息 2021-09-22 22:40:38 +08:00
刘祥超
7c530680c0 增加配置分组配置 2021-09-22 19:39:55 +08:00
刘祥超
e8429b8f74 域名解析任务可以使用集群ID筛选 2021-09-21 10:56:47 +08:00
刘祥超
175dfd06a0 可以设置集群的DNS记录TTL 2021-09-20 20:01:27 +08:00
刘祥超
b770713a28 在集群中可以设置自动加入DNS的CNAME记录 2021-09-20 16:38:00 +08:00
刘祥超
1f6be53d3d 反向代理源站实现使用域名分组 2021-09-20 11:54:21 +08:00
刘祥超
bf57bc5a1c 实现连通性变化发送通知功能 2021-09-18 14:22:17 +08:00
刘祥超
728061c66c 增加商业版认证校验API 2021-09-16 10:34:00 +08:00
刘祥超
1392dce1c5 增加查找区域监控对象相关结果 2021-09-15 17:53:26 +08:00
刘祥超
4a79878dde 节点IP地址可以修改在线状态 2021-09-15 11:47:15 +08:00
刘祥超
c3bdacbf3d 调整部分命名 2021-09-14 19:38:24 +08:00
刘祥超
fe59c6850d IP阈值动作增加WebHook 2021-09-14 15:27:59 +08:00
刘祥超
d17d1a8ffd IP阈值增加节点分组和集群相关统计项目 2021-09-14 11:36:14 +08:00
刘祥超
c73b8cef16 优化节点设置交互 2021-09-13 16:47:46 +08:00
刘祥超
ff5dc41db3 IP地址支持手动上线和从备用IP中恢复 2021-09-13 13:46:32 +08:00
刘祥超
1e41e3b65b 实现节点自动切换到备用IP 2021-09-13 10:51:09 +08:00
刘祥超
3c8c4e6747 实现基础的IP地址阈值 2021-09-12 20:21:27 +08:00
刘祥超
699ed00e40 WAF中添加正则相关规则时校验正则表达式 2021-09-10 10:31:56 +08:00
刘祥超
6284db1bc8 实现基本的监控终端管理 2021-09-08 19:35:28 +08:00
刘祥超
8e49d4a5f3 区域监控可以上传监控数据 2021-09-06 09:16:41 +08:00
刘祥超
fad5f16abf 实现基本的区域监控终端管理功能 2021-09-05 11:11:04 +08:00
刘祥超
72369199e1 增加独立的IP地址管理功能 2021-08-31 17:24:41 +08:00
刘祥超
dba446a651 企业认证信息中增加节点数显示 2021-08-30 18:56:56 +08:00
刘祥超
d64f8cdf0f 优化数据库节点管理 2021-08-30 10:57:03 +08:00
刘祥超
0c014ced0c DNS服务商支持搜索 2021-08-25 18:47:06 +08:00
刘祥超
2acdb9349d 节点如果没有设置DNS线路就使用默认线路 2021-08-25 17:16:43 +08:00
刘祥超
61cb2f8cf8 Admin看板增加默认集群ID 2021-08-25 11:41:13 +08:00
刘祥超
4afe12cd46 增加忽略相似消息周期设置 2021-08-24 20:45:16 +08:00
刘祥超
0b91e9d084 媒介可以设置发送频率/消息接收人可以设置接收消息时间段 2021-08-24 17:46:48 +08:00
刘祥超
ae85d39275 通知媒介增加任务队列查看功能 2021-08-24 14:23:21 +08:00
刘祥超
66af31330e 消息接收者区分角色 2021-08-23 16:46:34 +08:00
刘祥超
663c3dfbd4 阶段性提交 2021-08-23 09:59:43 +08:00
刘祥超
503c177a29 DNS访问日志显示匹配的线路 2021-08-20 11:27:22 +08:00
刘祥超
5edfd9e4c9 添加DNS账号时自动读取DNS服务商下域名 2021-08-19 14:26:19 +08:00
刘祥超
433df9dddb 增加configutils/ParseCIDR()函数 2021-08-19 10:50:40 +08:00
刘祥超
ca85f71f2b 节点IP可以设置阈值 2021-08-18 17:09:34 +08:00
刘祥超
928d8c3ace 节点IP增加是否启用、是否在线状态 2021-08-18 09:25:03 +08:00
刘祥超
9c8e740a1e 增加通过IP来搜索IP名单的API 2021-08-15 15:42:26 +08:00
刘祥超
e18b3a01bb 增加SSH认证建议接口 2021-08-14 21:33:26 +08:00
刘祥超
1bee81bbab 增加SSH登录建议端口接口 2021-08-14 18:06:46 +08:00
刘祥超
45c63a5831 可以远程停止和启动DNS节点 2021-08-12 11:47:47 +08:00
刘祥超
7172ca84d6 实现DNS节点远程安装 2021-08-11 21:00:35 +08:00
刘祥超
29b25f968d 安全设置中增加允许记住登录选项 2021-08-11 09:36:21 +08:00
刘祥超
ae0bd85ecc 访问日志显示节点信息 2021-08-10 11:14:46 +08:00
刘祥超
4291cf1b30 自建DNS增加解析测试 2021-08-09 18:42:06 +08:00
刘祥超
618d1060bf EdgeDNS支持内置线路 2021-08-09 13:59:55 +08:00
刘祥超
7d604a0ed7 DNS节点增加在线状态通知 2021-08-08 10:29:27 +08:00
刘祥超
f4f68ae13c 将grpc.sh改为build.sh 2021-08-08 08:48:26 +08:00
刘祥超
9644ec0ff8 访问日志搜索增加域名和IP搜索 2021-08-07 22:02:18 +08:00
刘祥超
473b7432e8 自建DNS增加全局配置 2021-08-07 20:40:36 +08:00
刘祥超
d2527abd17 优化源站调度 2021-08-01 21:56:15 +08:00
刘祥超
7caf0c2f68 WAF策略和缓存策略跟随集群 2021-08-01 14:52:10 +08:00
刘祥超
9a037680cf 服务配置增加集群ID 2021-08-01 11:14:13 +08:00
刘祥超
b908a8de60 初步实现多集群共享节点 2021-07-31 22:23:16 +08:00
刘祥超
c49ec306d4 实现基本的访问日志策略 2021-07-29 16:51:11 +08:00
刘祥超
f405e42d39 指标图表可以设置忽略空值和其他对象值 2021-07-26 16:44:39 +08:00
刘祥超
5413a792d5 增加referer.host请求变量 2021-07-26 15:37:38 +08:00
刘祥超
01a1690720 修改文字 2021-07-26 14:34:02 +08:00
刘祥超
5ae053965b 自动跳转到HTTPS可以设置允许和排除的域名 2021-07-26 11:23:39 +08:00
刘祥超
f14f5f0606 修复一处指标变量说明错误 2021-07-26 10:59:10 +08:00
刘祥超
32d542aa6c DNS支持TSIG 2021-07-25 15:08:38 +08:00
刘祥超
f1da942189 DNS服务支持密钥管理 2021-07-25 09:43:47 +08:00
刘祥超
2b1483f857 DNS节点自动升级 2021-07-22 18:45:36 +08:00
刘祥超
5ae86305f9 域名记录增加停用/启用 2021-07-20 19:03:56 +08:00
刘祥超
f83be52482 增加API令牌相关API 2021-07-20 17:15:51 +08:00
刘祥超
a241b9a9df 在各个地方支持IPv6 2021-07-20 10:55:52 +08:00
刘祥超
b9e01b7738 Dashboard增加指标图表 2021-07-19 17:58:09 +08:00
刘祥超
587cd4484a 实现公用的统计指标 2021-07-19 15:22:49 +08:00
刘祥超
8fd93ffbbe 修改一处文字 2021-07-19 14:28:56 +08:00
刘祥超
616b855d61 将WAF模板中的cc修改为cc2 2021-07-19 11:01:43 +08:00
刘祥超
3a12813da3 WAF增加多个动作 2021-07-18 16:26:45 +08:00
刘祥超
66f04de8c1 WAF支持更多动作 2021-07-14 22:46:31 +08:00
刘祥超
b4c72dc8f4 修复绑定的IP名单无法生效的Bug 2021-07-13 15:50:02 +08:00
刘祥超
4e84c7b376 优化看板 2021-07-13 15:49:34 +08:00
刘祥超
50d8a575a6 实现数据看板--WAF 2021-07-12 16:57:06 +08:00
刘祥超
499ec2306c 管理界面可以切换风格 2021-07-12 10:21:45 +08:00
刘祥超
aa150a1ae1 实现数据看板-DNS 2021-07-11 21:43:59 +08:00
刘祥超
0ad1782d42 实现数据看板--用户 2021-07-11 18:06:07 +08:00
刘祥超
4ceac4d9c9 节点看板增加缓存目录用量 2021-07-08 19:43:20 +08:00
刘祥超
30f8bfe703 实现服务看板(企业版) 2021-07-07 19:55:52 +08:00
刘祥超
be9bde507a 实现集群看板 2021-07-05 11:37:10 +08:00
刘祥超
53ee50fa38 指标增加一些易读的名称等信息 2021-07-03 18:05:45 +08:00
刘祥超
8cde433c37 实现基本的图表管理 2021-07-03 15:45:16 +08:00
刘祥超
fe88c581c7 指标数据增加占比数据 2021-07-01 10:39:19 +08:00
刘祥超
32cb66db4c 实现基础的统计指标 2021-06-30 20:50:17 +08:00
刘祥超
e4b59c5e85 阶段性提交 2021-06-27 22:00:02 +08:00
刘祥超
be404ea98d 实现公用的IP名单 2021-06-23 13:13:58 +08:00
1226 changed files with 226373 additions and 41576 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*_plus.go
*_plus_test.go
*_plus.json

75
.golangci.yaml Normal file
View File

@@ -0,0 +1,75 @@
# https://golangci-lint.run/usage/configuration/
linters:
enable-all: true
disable:
- ifshort
- exhaustivestruct
- golint
- nosnakecase
- scopelint
- varcheck
- structcheck
- interfacer
- maligned
- deadcode
- dogsled
- wrapcheck
- wastedassign
- varnamelen
- testpackage
- thelper
- nilerr
- sqlclosecheck
- paralleltest
- nonamedreturns
- nlreturn
- nakedret
- ireturn
- interfacebloat
- gosmopolitan
- gomnd
- goerr113
- gochecknoglobals
- exhaustruct
- errorlint
- depguard
- exhaustive
- containedctx
- wsl
- cyclop
- dupword
- errchkjson
- contextcheck
- tagalign
- dupl
- forbidigo
- funlen
- goconst
- godox
- gosec
- lll
- nestif
- revive
- unparam
- stylecheck
- gocritic
- gofumpt
- gomoddirectives
- godot
- gofmt
- gocognit
- mirror
- gocyclo
- gochecknoinits
- gci
- maintidx
- prealloc
- goimports
- errname
- musttag
- forcetypeassert
- whitespace
- noctx
- tagliatelle
- nilnil

29
LICENSE Normal file
View File

@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2020, LiuXiangChao
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

27
README.md Normal file
View File

@@ -0,0 +1,27 @@
GoEdge公共配置项。
目录结构:
~~~
pkg/
dnsconfigs/ - 域名解析和NameServer相关配置
langs/ 多语言配置
messageconfigs/ - 消息通知相关配置
monitorconfigs/ - 监控相关配置
nodeconfigs/ - 边缘节点相关配置
nodeutils/ - 边缘节点相关函数
serverconfigs/ - 网站服务相关配置
systemconfigs/ - 系统全局配置
reporterconfigs/ - 区域监控终端配置
userconfigs/ - 用户相关配置
configutils/ - 配置公共函数等
iplibrary/ - IP库
errors/ - 错误处理
rpc/ - RPC通讯
protos/ RPC数据和接口定义
sevice_*.proto RPC接口定义
models/
model_*.proto RPC数据定义
~~~
开发时需要将 `rpc/protos/``rpc/protos/models/` 两个目录放入到Proto Buffer检查工具可以找到的位置。

View File

@@ -1 +1,31 @@
每次 `.proto` 文件有更新的时候,请运行 `grpc.sh` 重新生成相应的源代码。
# 脚本使用指南
## 编译多语言相关源文件
~~~bash
./build-messages.sh
~~~
## 编译API相关源文件
在使用 `build.sh` 编译 `.proto` 文件之前,你需要确保已经为 `protoc` 安装了对应的插件:
~~~bash
# install protoc-gen-go plugin
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
# install protoc-gen-go-grpc plugin
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
~~~
之后每次 `.proto` 文件有更新的时候,请运行 `build.sh` 重新生成相应的Go源代码和`rpc.json`文件:
~~~bash
./build.sh
~~~
如果文件名有更改,请清空 `pkg/rpc/pb/*.go` 文件,然后再次运行 `build.sh`
## 生成RPC列表文件
运行:
~~~bash
./proto-json.sh
~~~
可以重新生成 `rpc.json` 文件。

4
build/build-messages.sh Executable file
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env bash
ROOT=$(dirname "$0")
/usr/local/bin/go run "${ROOT}"/../cmd/langs/main.go generate

28
build/build.sh Executable file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env bash
echo "starting ..."
#rm -f ../pkg/rpc/pb/*.pb.go
protoc --go_out=../pkg/rpc --proto_path=../pkg/rpc/protos ../pkg/rpc/protos/*.proto
RESULT=$?
if [ "${RESULT}" != "0" ]; then
exit
fi
protoc --go-grpc_out=../pkg/rpc --go-grpc_opt=require_unimplemented_servers=false --proto_path=../pkg/rpc/protos ../pkg/rpc/protos/*.proto
RESULT=$?
if [ "${RESULT}" != "0" ]; then
exit
fi
protoc --go_out=../pkg/rpc --proto_path=../pkg/rpc/protos ../pkg/rpc/protos/models/*.proto
RESULT=$?
if [ "${RESULT}" != "0" ]; then
exit
fi
# generate rpc.json
./proto-json.sh --quiet
echo "ok"

View File

@@ -1,5 +0,0 @@
#!/usr/bin/env bash
#rm -f ../pkg/rpc/pb/*.pb.go
protoc --go_out=plugins=grpc:../pkg/rpc --proto_path=../pkg/rpc/protos ../pkg/rpc/protos/*.proto
protoc --go_out=plugins=grpc:../pkg/rpc --proto_path=../pkg/rpc/protos ../pkg/rpc/protos/models/*.proto

3
build/proto-json.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/usr/bin/env bash
go run ../cmd/proto-json/main.go $1

23098
build/rpc.json Normal file

File diff suppressed because it is too large Load Diff

454
cmd/langs/main.go Normal file
View File

@@ -0,0 +1,454 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package main
import (
"bytes"
"encoding/json"
"fmt"
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
"github.com/iwind/TeaGo/Tea"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/types"
"go/format"
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
)
func main() {
var args = os.Args
if len(args) >= 2 {
switch args[1] {
case "generate":
// generate go codes from json files
runGenerate()
case "search":
// search hans from dir path
runSearch()
}
} else {
fmt.Println("Usage: langs [generate|search]")
}
}
func runGenerate() {
var rootDir = filepath.Clean(Tea.Root + "/../pkg/langs/protos")
dir, err := os.Open(rootDir)
if err != nil {
fmt.Println("[ERROR]read dir failed: " + err.Error())
return
}
defer func() {
_ = dir.Close()
}()
files, err := dir.Readdir(0)
if err != nil {
fmt.Println("[ERROR]read dir failed: " + err.Error())
return
}
var dirRegexp = regexp.MustCompile(`^[a-z]+-[a-z]+$`)
var jsonFileNameRegexp = regexp.MustCompile(`^([a-zA-Z0-9]+)(_([a-zA-Z0-9]+))*\.json$`)
var messageCodeRegexp = regexp.MustCompile(`^[a-zA-Z0-9_]+$`)
var jsonCommentRegexp = regexp.MustCompile(`//\s+.+`)
var messageCodes = []string{}
var langMaps = map[string]*langs.Lang{} // lang => *langs.Lang
var defaultLang = langs.DefaultManager().DefaultLang()
const maxMessageCodeLen = 128
for _, file := range files {
var dirName = file.Name()
if !file.IsDir() || !dirRegexp.MatchString(dirName) {
continue
}
var langCode = dirName
var isBaseLang = langCode == defaultLang
var processOk = func() bool {
jsonFiles, err := filepath.Glob(rootDir + "/" + dirName + "/*.json")
if err != nil {
fmt.Println("[ERROR]list json files failed: " + err.Error())
return false
}
for _, jsonFile := range jsonFiles {
var jsonFileName = filepath.Base(jsonFile)
if len(jsonFileName) == 0 || !jsonFileNameRegexp.MatchString(jsonFileName) {
continue
}
var module = strings.TrimSuffix(jsonFileName, ".json")
data, err := os.ReadFile(jsonFile)
if err != nil {
fmt.Println("[ERROR]read json file '" + jsonFile + "' failed: " + err.Error())
return false
}
// remove comments in json
data = jsonCommentRegexp.ReplaceAll(data, []byte{})
var m = map[string]string{} // code => value
err = json.Unmarshal(data, &m)
if err != nil {
fmt.Println("[ERROR]decode json file '" + jsonFile + "' failed: " + err.Error())
return false
}
var newM = map[string]string{}
for code, value := range m {
if !messageCodeRegexp.MatchString(code) {
fmt.Println("[ERROR]invalid message code '" + code + "'")
return false
}
var fullCode = module + "@" + code
if len(fullCode) > maxMessageCodeLen {
fmt.Println("[ERROR]message code '" + fullCode + "' too long, max length: " + types.String(maxMessageCodeLen))
return false
}
if isBaseLang {
messageCodes = append(messageCodes, fullCode)
}
newM[fullCode] = value
}
finalLang, ok := langMaps[langCode]
if !ok {
finalLang = langs.NewLang(langCode)
langMaps[langCode] = finalLang
}
for code, value := range newM {
if finalLang.Has(langs.MessageCode(code)) {
fmt.Println("[ERROR]message code '" + code + "' duplicated")
return false
}
finalLang.Set(langs.MessageCode(code), value)
}
}
return true
}()
if !processOk {
return
}
}
// compile
for langCode, lang := range langMaps {
err = lang.Compile()
if err != nil {
fmt.Println("[ERROR]compile '" + langCode + "' failed: " + err.Error())
return
}
}
// check message codes
fmt.Println("checking message codes ...")
var defaultMessageMap = map[langs.MessageCode]string{}
for langCode, messageLang := range langMaps {
if langCode == defaultLang { // only check lang not equal to 'en-us'
defaultMessageMap = messageLang.GetAll()
continue
}
for messageCode := range messageLang.GetAll() {
if !lists.ContainsString(messageCodes, messageCode.String()) {
fmt.Println("[ERROR]message code '" + messageCode.String() + "' in lang '" + langCode + "' not exist in default lang file ('" + defaultLang + "')")
return
}
}
}
fmt.Println("found '" + types.String(len(messageCodes)) + "' message codes")
// generate codes/codes.go
sort.Strings(messageCodes)
var codesSource = `
// generated by run 'langs generate'
package codes
import(
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
)
const (
`
for index, messageCode := range messageCodes {
// add comment to message code
comment, _, _ := strings.Cut(defaultMessageMap[langs.MessageCode(messageCode)], "\n")
codesSource += upperWords(messageCode) + " langs.MessageCode = " + strconv.Quote(messageCode) + " // " + comment
// add NL
if index != len(messageCodes)-1 {
codesSource += "\n"
}
}
codesSource += `
)
`
formattedCodesSource, err := format.Source([]byte(codesSource))
if err != nil {
fmt.Println("[ERROR]format 'codes.go' failed: " + err.Error())
return
}
fmt.Println("generating 'codes/codes.go' ...")
err = os.WriteFile(Tea.Root+"/../pkg/langs/codes/codes.go", formattedCodesSource, 0666)
if err != nil {
fmt.Println("[ERROR]write to 'codes.go' failed: " + err.Error())
return
}
// generate messages_LANG.go
for langCode, messageLang := range langMaps {
var langFile = strings.ReplaceAll("messages/messages_"+langCode+".go", "-", "_")
fmt.Println("generating '" + langFile + "' ...")
var source = `
// generated by run 'langs generate'
package messages
import(
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
)
func init() {
langs.Load("` + langCode + `", map[langs.MessageCode]string{
`
for _, code := range messageCodes {
var value = messageLang.Get(langs.MessageCode(code))
source += strconv.Quote(code) + ": " + strconv.Quote(value) + ",\n"
}
source += `
})
}
`
formattedSource, err := format.Source([]byte(source))
if err != nil {
fmt.Println("[ERROR]format '" + langFile + "' failed: " + err.Error())
return
}
err = os.WriteFile(Tea.Root+"/../pkg/langs/"+langFile, formattedSource, 0666)
if err != nil {
fmt.Println("[ERROR]write file '" + langFile + "' failed: " + err.Error())
return
}
}
// generate language javascript files for EdgeAdmin and EdgeUser (commercial versions)
for lang, messageLang := range langMaps {
if lang != defaultLang {
// TODO merge messageMap with default message map
}
for _, component := range []string{"EdgeAdmin", "EdgeUser"} {
fmt.Println("generating '" + lang + ".js' for " + component + " ...")
var targetFile = filepath.Clean(Tea.Root + "/../../" + component + "/web/public/js/langs/" + lang + ".js")
var targetDir = filepath.Dir(targetFile)
dirStat, _ := os.Stat(targetDir)
if dirStat != nil {
var prefix = ""
switch component {
case "EdgeAdmin":
prefix = "admin_"
case "EdgeUser":
prefix = "user_"
}
if len(prefix) == 0 {
continue
}
var filteredMessages = map[langs.MessageCode]string{}
for code, value := range messageLang.GetAll() {
if strings.HasPrefix(code.String(), prefix) && strings.Contains(code.String(), "@ui_") /** must contains 'ui' **/ {
filteredMessages[code] = value
}
}
messageMapJSON, err := json.Marshal(filteredMessages)
if err != nil {
fmt.Println("[ERROR]marshal message map failed: " + err.Error())
return
}
err = os.WriteFile(targetFile, []byte(`// generated by 'langs generate'
window.LANG_MESSAGES = `+string(messageMapJSON)+";\n"), 0666)
if err != nil {
fmt.Println("[ERROR]write file '" + targetFile + "' failed: " + err.Error())
return
}
}
}
}
fmt.Println("success")
}
func upperWords(s string) string {
var pieces = strings.Split(s, "@")
for pieceIndex, piece := range pieces {
var words = strings.Split(piece, "_")
for index, word := range words {
words[index] = upperWord(word)
}
pieces[pieceIndex] = strings.Join(words, "")
}
return strings.Join(pieces, "_")
}
func upperWord(word string) string {
var l = len(word)
if l == 0 {
return ""
}
if l == 1 {
return strings.ToUpper(word)
}
// special words
switch word {
case "api", "http", "https", "tcp", "tls", "ssl", "udp", "ip", "dns", "ns",
"waf", "acme", "ssh", "toa", "http2", "http3", "uam", "cc",
"db", "isp", "sni", "ui", "soa", "ocsp", "en", "zh", "ad", "tsig",
"rpc", "dao":
return strings.ToUpper(word)
case "ipv6":
return "IPv6"
case "ddos":
return "DDoS"
case "webp":
return "WebP"
case "doh":
return "DoH"
}
return strings.ToUpper(word[:1]) + word[1:]
}
func runSearch() {
if len(os.Args) < 3 {
fmt.Println("Usage: langs search DIR")
return
}
var dir = os.Args[2]
stat, err := os.Stat(dir)
if err != nil {
fmt.Println("[ERROR]could not find dir '" + dir + "': " + err.Error())
return
}
if !stat.IsDir() {
fmt.Println("[ERROR]could not find dir '" + dir + "'")
return
}
fmt.Println("searching '" + dir + "' ...")
var ext = ".go"
var resultFiles = []string{}
for _, pattern := range []string{
"*" + ext,
strings.Repeat("*/", 1) + "*" + ext,
strings.Repeat("*/", 2) + "*" + ext,
strings.Repeat("*/", 3) + "*" + ext,
strings.Repeat("*/", 4) + "*" + ext,
strings.Repeat("*/", 5) + "*" + ext,
strings.Repeat("*/", 6) + "*" + ext,
strings.Repeat("*/", 7) + "*" + ext,
strings.Repeat("*/", 8) + "*" + ext,
strings.Repeat("*/", 9) + "*" + ext,
strings.Repeat("*/", 10) + "*" + ext,
} {
goFiles, err := filepath.Glob(dir + "/" + pattern)
if err != nil {
fmt.Println("[ERROR]search error: " + err.Error())
return
}
resultFiles = append(resultFiles, goFiles...)
}
if len(resultFiles) == 0 {
fmt.Println("no files found in the dir")
return
}
if err != nil {
fmt.Println("[ERROR]search dir '" + dir + "' failed: " + err.Error())
return
}
var hansRegexp = regexp.MustCompile(`\p{Han}+`)
var countMatches = 0
for _, goFile := range resultFiles {
if strings.HasSuffix(goFile, "_test.go") ||
strings.HasSuffix(goFile, "_plus_test.go") ||
strings.Contains(goFile, "/messages/messages_") {
continue
}
data, err := os.ReadFile(goFile)
if err != nil {
fmt.Println("[ERROR]read file '" + goFile + "' failed: " + err.Error())
return
}
var matches = hansRegexp.FindAllSubmatchIndex(data, -1)
if len(matches) > 0 {
for _, match := range matches {
// ignore comment
switch ext {
case ".go":
if checkIsInGoComment(data, match[0]) {
continue
}
}
countMatches++
fmt.Printf("%s %s\n", goFile+":"+types.String(bytes.Count(data[:match[0]], []byte{'\n'})+1), string(data[match[0]:match[1]]))
}
}
}
fmt.Println(countMatches, "matches")
}
func checkIsInGoComment(data []byte, start int) bool {
if start <= 1 {
return false
}
for {
start--
if start <= 1 || data[start] == '\n' {
return false
}
// 'SPACE //'
if data[start] == '/' && data[start-1] == '/' {
return true
}
// '/** SOMETHING **/'
if data[start] == '*' && data[start-1] == '/' {
return true
}
}
}

448
cmd/proto-json/main.go Normal file
View File

@@ -0,0 +1,448 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"github.com/iwind/TeaGo/Tea"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/types"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
)
type ServiceInfo struct {
Name string `json:"name"`
Methods []*MethodInfo `json:"methods"`
Filename string `json:"filename"`
Doc string `json:"doc"`
}
type MethodInfo struct {
Name string `json:"name"`
RequestMessageName string `json:"requestMessageName"`
ResponseMessageName string `json:"responseMessageName"`
Code string `json:"code"`
Doc string `json:"doc"`
Roles []string `json:"roles"`
IsDeprecated bool `json:"isDeprecated"`
}
type MessageInfo struct {
Name string `json:"name"`
Code string `json:"code"`
Doc string `json:"doc"`
}
type LinkInfo struct {
Name string `json:"name"`
Content string `json:"content"`
}
type RPCList struct {
Services []*ServiceInfo `json:"services"`
Messages []*MessageInfo `json:"messages"`
Links []*LinkInfo `json:"links"`
}
func readComments(data []byte) string {
var lines = bytes.Split(data, []byte{'\n'})
var comments = [][]byte{}
for i := len(lines) - 1; i >= 0; i-- {
var line = bytes.TrimLeft(lines[i], " \t")
if len(line) == 0 {
comments = append([][]byte{{' '}}, comments...)
continue
}
if bytes.HasPrefix(line, []byte("//")) {
line = bytes.TrimSpace(bytes.TrimLeft(line, "/"))
comments = append([][]byte{line}, comments...)
} else {
break
}
}
return string(bytes.TrimSpace(bytes.Join(comments, []byte{'\n'})))
}
func removeDuplicates(s []string) []string {
if len(s) == 0 {
return s
}
var m = map[string]bool{}
var result = []string{}
for _, item := range s {
_, ok := m[item]
if ok {
continue
}
result = append(result, item)
m[item] = true
}
return result
}
// 生成JSON格式API列表
func main() {
var quiet = false
flag.BoolVar(&quiet, "quiet", false, "")
flag.Parse()
var methodRolesMap = map[string][]string{} // method => roles
{
var rootDir = filepath.Clean(Tea.Root + "/../../EdgeAPI/internal/rpc/services")
entries, err := os.ReadDir(rootDir)
if err != nil {
logs.Println("[ERROR]read api services from '" + rootDir + "' failed: " + err.Error())
return
}
var rootDirs = []string{rootDir}
for _, entry := range entries {
if entry.IsDir() {
rootDirs = append(rootDirs, rootDir+string(os.PathSeparator)+entry.Name())
}
}
// 排序以保证输出内容的稳定性
sort.Strings(rootDirs)
for _, rootDir := range rootDirs {
files, err := filepath.Glob(rootDir + "/service_*.go")
if err != nil {
fmt.Println("[ERROR]list service implementation files failed: " + err.Error())
return
}
// 排序以保证输出内容的稳定性
sort.Strings(files)
var methodNameReg = regexp.MustCompile(`func\s*\(\w+\s+\*\s*(\w+Service)\)\s*(\w+)\s*\(`) // $1: serviceName, $2 methodName
for _, file := range files {
data, err := os.ReadFile(file)
if err != nil {
fmt.Println("[ERROR]read file '" + file + "' failed: " + err.Error())
return
}
var sourceCode = string(data)
var locList = methodNameReg.FindAllStringIndex(sourceCode, -1)
for index, loc := range locList {
var methodSource = ""
if index == len(locList)-1 { // last one
methodSource = sourceCode[loc[0]:]
} else {
methodSource = sourceCode[loc[0]:locList[index+1][0]]
}
// 方法名
var submatch = methodNameReg.FindStringSubmatch(methodSource)
if len(submatch) == 0 {
continue
}
var serviceName = submatch[1]
if serviceName == "BaseService" {
continue
}
var methodName = submatch[2]
if methodName[0] < 'A' || methodName[0] > 'Z' {
continue
}
var roles = []string{}
if strings.Contains(methodSource, ".ValidateNode(") {
roles = append(roles, "node")
}
if strings.Contains(methodSource, ".ValidateUserNode(") {
var hasRoles = false
var wordIndex = strings.Index(methodSource, ".ValidateUserNode(")
if wordIndex > 0 {
if len(methodSource[wordIndex:]) > 100 {
if strings.Contains(methodSource[wordIndex:wordIndex+100], ".ValidateUserNode(ctx, false)") {
hasRoles = true
}
}
if !hasRoles {
roles = append(roles, "user")
}
}
}
if strings.Contains(methodSource, ".ValidateAdmin(") {
roles = append(roles, "admin")
}
if strings.Contains(methodSource, ".ValidateAdminAndUser(") {
var hasRoles = false
var wordIndex = strings.Index(methodSource, ".ValidateAdminAndUser(")
if wordIndex > 0 {
if len(methodSource[wordIndex:]) > 100 {
if strings.Contains(methodSource[wordIndex:wordIndex+100], ".ValidateAdminAndUser(ctx, false)") {
roles = append(roles, "admin")
hasRoles = true
}
}
if !hasRoles {
roles = append(roles, "admin", "user")
}
}
}
if strings.Contains(methodSource, ".ValidateNSNode(") {
roles = append(roles, "dns")
}
if strings.Contains(methodSource, ".ValidateMonitorNode(") {
roles = append(roles, "monitor")
}
if strings.Contains(methodSource, "rpcutils.UserTypeDNS") {
roles = append(roles, "dns")
}
if strings.Contains(methodSource, "rpcutils.UserTypeUser") {
roles = append(roles, "user")
}
if strings.Contains(methodSource, "rpcutils.UserTypeNode") {
roles = append(roles, "node")
}
if strings.Contains(methodSource, "rpcutils.UserTypeMonitor") {
roles = append(roles, "monitor")
}
if strings.Contains(methodSource, "rpcutils.UserTypeReport") {
roles = append(roles, "report")
}
if strings.Contains(methodSource, "rpcutils.UserTypeCluster") {
roles = append(roles, "cluster")
}
if strings.Contains(methodSource, "rpcutils.UserTypeAdmin") {
roles = append(roles, "admin")
}
methodRolesMap[strings.ToLower(methodName)] = removeDuplicates(roles)
}
}
}
}
var services = []*ServiceInfo{}
var messages = []*MessageInfo{}
{
var dirs = []string{Tea.Root + "/../pkg/rpc/protos/", Tea.Root + "/../pkg/rpc/protos/models"}
for _, dir := range dirs {
func(dir string) {
dir = filepath.Clean(dir)
files, err := filepath.Glob(dir + "/*.proto")
if err != nil {
fmt.Println("[ERROR]list proto files failed: " + err.Error())
return
}
// 排序以保持稳定性
sort.Strings(files)
for _, path := range files {
func(path string) {
var filename = filepath.Base(path)
if filename == "service_authority_key.proto" || filename == "service_authority_node.proto" {
return
}
data, err := os.ReadFile(path)
if err != nil {
fmt.Println("[ERROR]" + err.Error())
return
}
// 先将rpc代码替换成临时代码
var methodCodeMap = map[string][]byte{} // code => method
var methodIndex = 0
var methodReg = regexp.MustCompile(`(?s)rpc\s+(\w+)\s*\(\s*(\w+)\s*\)\s*returns\s*\(\s*(\w+)\s*\)\s*(\{.+})?\s*;`)
data = methodReg.ReplaceAllFunc(data, func(methodData []byte) []byte {
methodIndex++
var code = "METHOD" + types.String(methodIndex)
methodCodeMap[code] = methodData
return []byte("\n" + code)
})
// 服务列表
// TODO 这里需要改进一下,当前实现方法如果方法注释里有括号(}),就会导致部分方法解析不到
var serviceNameReg = regexp.MustCompile(`(?sU)\n\s*service\s+(\w+)\s*\{(.+)}`)
var serviceMatches = serviceNameReg.FindAllSubmatch(data, -1)
var serviceNamePositions = serviceNameReg.FindAllIndex(data, -1)
for serviceMatchIndex, serviceMatch := range serviceMatches {
var serviceName = string(serviceMatch[1])
var serviceNamePosition = serviceNamePositions[serviceMatchIndex][0]
var comment = readComments(data[:serviceNamePosition])
// 方法列表
var methods = []*MethodInfo{}
var serviceData = serviceMatch[2]
var methodCodeReg = regexp.MustCompile(`\b(METHOD\d+)\b`)
var methodCodeMatches = methodCodeReg.FindAllSubmatch(serviceData, -1)
var methodCodePositions = methodCodeReg.FindAllIndex(serviceData, -1)
for methodMatchIndex, methodMatch := range methodCodeMatches {
var methodCode = string(methodMatch[1])
var methodData = methodCodeMap[methodCode]
var methodPieces = methodReg.FindSubmatch(methodData)
var methodCodePosition = methodCodePositions[methodMatchIndex]
var roles = methodRolesMap[strings.ToLower(string(methodPieces[1]))]
if roles == nil {
roles = []string{}
}
methods = append(methods, &MethodInfo{
Name: string(methodPieces[1]),
RequestMessageName: string(methodPieces[2]),
ResponseMessageName: string(methodPieces[3]),
IsDeprecated: strings.Contains(string(methodPieces[4]), "deprecated"),
Code: string(methodData),
Doc: readComments(serviceData[:methodCodePosition[0]]),
Roles: roles,
})
}
services = append(services, &ServiceInfo{
Name: serviceName,
Methods: methods,
Filename: filename,
Doc: comment,
})
}
// 消息列表
var topMessageCodeMap = map[string][]byte{} // code => message
var allMessageCodeMap = map[string][]byte{}
var messageCodeIndex = 0
var messagesReg = regexp.MustCompile(`(?sU)\n\s*message\s+(\w+)\s*\{([^{}]+)\n\s*}`)
var firstMessagesReg = regexp.MustCompile(`message\s+(\w+)`)
var messageCodeREG = regexp.MustCompile(`MESSAGE\d+`)
for {
var hasMessage = false
data = messagesReg.ReplaceAllFunc(data, func(messageData []byte) []byte {
messageCodeIndex++
hasMessage = true
// 是否包含子Message
var subMatches = messageCodeREG.FindAllSubmatch(messageData, -1)
for _, subMatch := range subMatches {
var subMatchCode = string(subMatch[0])
delete(topMessageCodeMap, subMatchCode)
}
var code = "MESSAGE" + types.String(messageCodeIndex)
topMessageCodeMap[code] = messageData
allMessageCodeMap[code] = messageData
return []byte("\n" + code)
})
if !hasMessage {
break
}
}
for messageCode, messageData := range topMessageCodeMap {
// 替换其中的子Message
for {
if messageCodeREG.Match(messageData) {
messageData = messageCodeREG.ReplaceAllFunc(messageData, func(messageCodeData []byte) []byte {
return allMessageCodeMap[string(messageCodeData)]
})
} else {
break
}
}
// 注释
var index = bytes.Index(data, []byte(messageCode))
var messageName = string(firstMessagesReg.FindSubmatch(messageData)[1])
messages = append(messages, &MessageInfo{
Name: messageName,
Code: string(bytes.TrimSpace(messageData)),
Doc: readComments(data[:index]),
})
}
}(path)
}
}(dir)
}
}
var countServices = len(services)
var countMethods = 0
var countMessages = len(messages)
for _, service := range services {
countMethods += len(service.Methods)
}
// 链接
var links = []*LinkInfo{}
// json links
{
var dirs = []string{Tea.Root + "/../pkg/rpc/jsons"}
for _, dir := range dirs {
func(dir string) {
dir = filepath.Clean(dir)
files, err := filepath.Glob(dir + "/*.md")
if err != nil {
fmt.Println("[ERROR]list .md files failed: " + err.Error())
return
}
// 排序以保持稳定性
sort.Strings(files)
for _, path := range files {
func(path string) {
var name = strings.TrimSuffix(filepath.Base(path), ".md")
data, err := os.ReadFile(path)
if err != nil {
fmt.Println("[ERROR]read '" + path + "' failed: " + err.Error())
return
}
links = append(links, &LinkInfo{
Name: "json:" + name,
Content: string(data),
})
}(path)
}
}(dir)
}
}
// 对消息进行排序,以保持稳定性
sort.Slice(messages, func(i, j int) bool {
return messages[i].Name < messages[j].Name
})
var rpcList = &RPCList{
Services: services,
Messages: messages,
Links: links,
}
jsonData, err := json.MarshalIndent(rpcList, "", " ")
if err != nil {
fmt.Println("[ERROR]marshal to json failed: " + err.Error())
return
}
var jsonFile = Tea.Root + "/rpc.json"
err = os.WriteFile(jsonFile, jsonData, 0666)
if err != nil {
fmt.Println("[ERROR]write json to file failed: " + err.Error())
return
}
if !quiet {
fmt.Println("services:", countServices, "methods:", countMethods, "messages:", countMessages)
fmt.Println("===")
fmt.Println("generated " + filepath.Base(jsonFile) + " successfully")
}
}

24
go.mod
View File

@@ -1,13 +1,21 @@
module github.com/TeaOSLab/EdgeCommon
go 1.15
go 1.18
require (
github.com/cespare/xxhash/v2 v2.1.1
github.com/go-yaml/yaml v2.1.0+incompatible
github.com/golang/protobuf v1.4.2
github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f
google.golang.org/grpc v1.32.0
google.golang.org/protobuf v1.25.0
gopkg.in/yaml.v2 v2.4.0 // indirect
github.com/golang/protobuf v1.5.2
github.com/iwind/TeaGo v0.0.0-20230623080147-cd1e53b4915f
github.com/tdewolff/minify/v2 v2.12.7
golang.org/x/net v0.8.0
google.golang.org/grpc v1.45.0
google.golang.org/protobuf v1.27.1
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/kr/text v0.2.0 // indirect
github.com/tdewolff/parse/v2 v2.6.6 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e // indirect
)

163
go.sum
View File

@@ -1,28 +1,29 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200624174652-8d2f3be8b2d9/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-redis/redis/v8 v8.0.0-beta.7/go.mod h1:FGJAWDWFht1sQ4qxyJHZZbVyvnVcKQN0E3u5/5lRz+g=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -34,108 +35,103 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f h1:r2O8PONj/KiuZjJHVHn7KlCePUIjNtgAmvLfgRafQ8o=
github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/iwind/TeaGo v0.0.0-20230623080147-cd1e53b4915f h1:xo6XmXLtveKcwcZAXV6VMxkWNzy/2dStfHEnyowsGAE=
github.com/iwind/TeaGo v0.0.0-20230623080147-cd1e53b4915f/go.mod h1:fi/Pq+/5m2HZoseM+39dMF57ANXRt6w4PkGu3NXPc5s=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
go.opentelemetry.io/otel v0.7.0/go.mod h1:aZMyHG5TqDOXEgH2tyLiXSUKly1jT3yqE9PmrzIeCdo=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tdewolff/minify/v2 v2.12.7 h1:pBzz2tAfz5VghOXiQIsSta6srhmTeinQPjRDHWoumCA=
github.com/tdewolff/minify/v2 v2.12.7/go.mod h1:ZRKTheiOGyLSK8hOZWWv+YoJAECzDivNgAlVYDHp/Ws=
github.com/tdewolff/parse/v2 v2.6.6 h1:Yld+0CrKUJaCV78DL1G2nk3C9lKrxyRTux5aaK/AkDo=
github.com/tdewolff/parse/v2 v2.6.6/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs=
github.com/tdewolff/test v1.0.7/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
github.com/tdewolff/test v1.0.9 h1:SswqJCmeN4B+9gEAi/5uqT0qpi1y2/2O47V/1hhGZT0=
github.com/tdewolff/test v1.0.9/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e h1:fNKDNuUyC4WH+inqDMpfXDdfvwfYILbsX+oskGZ8hxg=
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0=
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -144,20 +140,17 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -4,7 +4,7 @@ import (
"reflect"
)
// 拷贝同类型struct指针对象中的字段
// CopyStructObject 拷贝同类型struct指针对象中的字段
func CopyStructObject(destPtr, sourcePtr interface{}) {
value := reflect.ValueOf(destPtr)
value2 := reflect.ValueOf(sourcePtr)

View File

@@ -6,7 +6,7 @@ import (
"strings"
)
// 从一组规则中匹配域名
// MatchDomains 从一组规则中匹配域名
// 支持的格式example.com, www.example.com, .example.com, *.example.com, ~(\d+).example.com
// 更多参考http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name
func MatchDomains(patterns []string, domain string) (isMatched bool) {
@@ -14,19 +14,27 @@ func MatchDomains(patterns []string, domain string) (isMatched bool) {
return
}
for _, pattern := range patterns {
if matchDomain(pattern, domain) {
if MatchDomain(pattern, domain) {
return true
}
}
return
}
// 匹配单个域名规则
func matchDomain(pattern string, domain string) (isMatched bool) {
// MatchDomain 匹配单个域名规则
func MatchDomain(pattern string, domain string) (isMatched bool) {
if len(pattern) == 0 {
return
}
if pattern == domain {
return true
}
if pattern == "*" {
return true
}
// 正则表达式
if pattern[0] == '~' {
reg, err := stringutil.RegexpCompile(strings.TrimSpace(pattern[1:]))
@@ -42,8 +50,8 @@ func matchDomain(pattern string, domain string) (isMatched bool) {
}
// 其他匹配
patternPieces := strings.Split(pattern, ".")
domainPieces := strings.Split(domain, ".")
var patternPieces = strings.Split(pattern, ".")
var domainPieces = strings.Split(domain, ".")
if len(patternPieces) != len(domainPieces) {
return
}
@@ -52,8 +60,33 @@ func matchDomain(pattern string, domain string) (isMatched bool) {
if patternPiece == "" || patternPiece == "*" || patternPiece == domainPieces[index] {
continue
}
if strings.HasSuffix(patternPiece, ":*") {
var portIndex = strings.LastIndex(patternPiece, ":*")
if portIndex >= 0 {
var prefix = patternPiece[:portIndex]
if strings.HasPrefix(domainPieces[index], prefix+":") || domainPieces[index] == prefix {
continue
}
}
}
isMatched = false
break
}
return isMatched
}
// IsFuzzyDomain 判断是否为特殊域名
func IsFuzzyDomain(domain string) bool {
if len(domain) == 0 {
return true
}
if domain[0] == '.' || domain[0] == '~' {
return true
}
for _, c := range domain {
if c == '*' {
return true
}
}
return false
}

View File

@@ -6,74 +6,119 @@ import (
)
func TestMatchDomain(t *testing.T) {
a := assert.NewAssertion(t)
var a = assert.NewAssertion(t)
{
ok := MatchDomains([]string{}, "example.com")
var ok = MatchDomains([]string{}, "example.com")
a.IsFalse(ok)
}
{
ok := MatchDomains([]string{"example.com"}, "example.com")
var ok = MatchDomains([]string{"example.com"}, "example.com")
a.IsTrue(ok)
}
{
ok := MatchDomains([]string{"www.example.com"}, "example.com")
var ok = MatchDomains([]string{"www.example.com"}, "example.com")
a.IsFalse(ok)
}
{
ok := MatchDomains([]string{".example.com"}, "www.example.com")
var ok = MatchDomains([]string{".example.com"}, "www.example.com")
a.IsTrue(ok)
}
{
ok := MatchDomains([]string{".example.com"}, "a.www.example.com")
var ok = MatchDomains([]string{".example.com"}, "a.www.example.com")
a.IsTrue(ok)
}
{
ok := MatchDomains([]string{".example.com"}, "a.www.example123.com")
var ok = MatchDomains([]string{".example.com"}, "a.www.example123.com")
a.IsFalse(ok)
}
{
ok := MatchDomains([]string{"*.example.com"}, "www.example.com")
var ok = MatchDomains([]string{"*.example.com"}, "www.example.com")
a.IsTrue(ok)
}
{
ok := MatchDomains([]string{"*.*.com"}, "www.example.com")
var ok = MatchDomains([]string{"*.*.com"}, "www.example.com")
a.IsTrue(ok)
}
{
ok := MatchDomains([]string{"www.*.com"}, "www.example.com")
var ok = MatchDomains([]string{"www.*.com"}, "www.example.com")
a.IsTrue(ok)
}
{
ok := MatchDomains([]string{"gallery.*.com"}, "www.example.com")
var ok = MatchDomains([]string{"gallery.*.com"}, "www.example.com")
a.IsFalse(ok)
}
{
ok := MatchDomains([]string{"~\\w+.example.com"}, "www.example.com")
var ok = MatchDomains([]string{"~\\w+.example.com"}, "www.example.com")
a.IsTrue(ok)
}
{
ok := MatchDomains([]string{"~\\w+.example.com"}, "a.www.example.com")
var ok = MatchDomains([]string{"~\\w+.example.com"}, "a.www.example.com")
a.IsTrue(ok)
}
{
ok := MatchDomains([]string{"~^\\d+.example.com$"}, "www.example.com")
var ok = MatchDomains([]string{"~^\\d+.example.com$"}, "www.example.com")
a.IsFalse(ok)
}
{
ok := MatchDomains([]string{"~^\\d+.example.com$"}, "123.example.com")
var ok = MatchDomains([]string{"~^\\d+.example.com$"}, "123.example.com")
a.IsTrue(ok)
}
{
var ok = MatchDomains([]string{"*"}, "example.com")
a.IsTrue(ok)
}
// port
{
var ok = MatchDomains([]string{"example.com:8001"}, "example.com:8001")
a.IsTrue(ok)
}
{
var ok = MatchDomains([]string{"example.com:8002"}, "example.com:8001")
a.IsFalse(ok)
}
{
var ok = MatchDomains([]string{"*.example.com:8001"}, "a.example.com:8001")
a.IsTrue(ok)
}
{
var ok = MatchDomains([]string{"a.example.com:*"}, "a.example.com:8001")
a.IsTrue(ok)
}
{
var ok = MatchDomains([]string{"a.example.com:*"}, "a.example.com")
a.IsTrue(ok)
}
{
var ok = MatchDomains([]string{"*.example.com:*"}, "a.example.com:8001")
a.IsTrue(ok)
}
{
var ok = MatchDomains([]string{"*.example.com:8002"}, "a.example.com:8001")
a.IsFalse(ok)
}
}
func TestIsSpecialDomain(t *testing.T) {
var a = assert.NewAssertion(t)
a.IsTrue(IsFuzzyDomain(""))
a.IsTrue(IsFuzzyDomain(".hello.com"))
a.IsTrue(IsFuzzyDomain("*.hello.com"))
a.IsTrue(IsFuzzyDomain("hello.*.com"))
a.IsTrue(IsFuzzyDomain("~^hello\\.com"))
a.IsFalse(IsFuzzyDomain("hello.com"))
}

View File

@@ -2,25 +2,105 @@ package configutils
import (
"encoding/binary"
"github.com/cespare/xxhash/v2"
"math"
"math/big"
"net"
"strings"
)
// IP2Long 将IP转换为整型
// IPString2Long 将IP转换为整型
// 注意IPv6没有顺序
func IP2Long(ip string) uint64 {
func IPString2Long(ip string) uint64 {
if len(ip) == 0 {
return 0
}
s := net.ParseIP(ip)
if len(s) == 0 {
var netIP = net.ParseIP(ip)
if len(netIP) == 0 {
return 0
}
return IP2Long(netIP)
}
// IP2Long 将IP对象转换为整型
func IP2Long(netIP net.IP) uint64 {
if len(netIP) == 0 {
return 0
}
if strings.Contains(ip, ":") {
return math.MaxUint32 + xxhash.Sum64(s)
var b4 = netIP.To4()
if b4 != nil {
return uint64(binary.BigEndian.Uint32(b4.To4()))
}
return uint64(binary.BigEndian.Uint32(s.To4()))
var i = big.NewInt(0)
i.SetBytes(netIP.To16())
return i.Uint64()
}
// IsIPv4 检查是否为IPv4
func IsIPv4(netIP net.IP) bool {
if len(netIP) == 0 {
return false
}
return netIP.To4() != nil
}
// IsIPv6 检查是否为IPv6
func IsIPv6(netIP net.IP) bool {
if len(netIP) == 0 {
return false
}
return netIP.To4() == nil && netIP.To16() != nil
}
// IPVersion 获取IP版本号
func IPVersion(netIP net.IP) int {
if len(netIP) == 0 {
return 0
}
if netIP.To4() != nil {
return 4
}
if netIP.To16() != nil {
return 6
}
return 0
}
// ParseCIDR 计算CIDR最大值
func ParseCIDR(cidr string) (ipFrom string, ipTo string, err error) {
_, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
return "", "", err
}
ipFrom = ipNet.IP.String()
Loop:
for i := len(ipNet.Mask) - 1; i >= 0; i-- {
for j := 7; j >= 0; j-- {
var m = ipNet.Mask[i] >> (7 - j) & 1 // 读取某位bit
if m == 0 {
ipNet.IP[i] |= 1 << j // 将此位置为1
} else {
break Loop
}
}
}
ipTo = ipNet.IP.String()
return
}
// QuoteIP 为IPv6加上括号
func QuoteIP(ip string) string {
if len(ip) == 0 {
return ip
}
if !strings.Contains(ip, ":") {
return ip
}
if ip[0] != '[' {
return "[" + ip + "]"
}
return ip
}

View File

@@ -0,0 +1,46 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package configutils_test
import (
"fmt"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/iwind/TeaGo/assert"
"net"
"testing"
)
func TestParseCIDR(t *testing.T) {
t.Log(configutils.ParseCIDR("192.168.1.1/32"))
t.Log(configutils.ParseCIDR("192.168.1.1/24"))
t.Log(configutils.ParseCIDR("192.168.1.1/16"))
}
func TestIPString2Long(t *testing.T) {
for _, ip := range []string{"127.0.0.1", "192.168.1.100", "::1", "fd00:6868:6868:0:10ac:d056:3bf6:7452", "fd00:6868:6868:0:10ac:d056:3bf6:7453", "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "wrong ip"} {
t.Log(fmt.Sprintf("%42s", ip), "=>", configutils.IPString2Long(ip))
}
}
func TestIsIPv4(t *testing.T) {
t.Log(configutils.IsIPv4(net.ParseIP("192.168.1.100")))
t.Log(configutils.IsIPv4(net.ParseIP("::1")))
}
func TestIsIPv6(t *testing.T) {
t.Log(configutils.IsIPv6(net.ParseIP("192.168.1.100")))
t.Log(configutils.IsIPv6(net.ParseIP("::1")))
}
func TestIPVersion(t *testing.T) {
var a = assert.NewAssertion(t)
a.IsTrue(configutils.IPVersion(net.ParseIP("192.168.1.100")) == 4)
a.IsTrue(configutils.IPVersion(net.ParseIP("1.2.3")) == 0)
a.IsTrue(configutils.IPVersion(net.ParseIP("::1")) == 6)
a.IsTrue(configutils.IPVersion(net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334")) == 6)
}
func TestQuoteIP(t *testing.T) {
t.Log(configutils.QuoteIP(configutils.QuoteIP("2001:da8:22::10")))
}

View File

@@ -7,7 +7,7 @@ import (
var whitespaceReg = regexp.MustCompile(`\s+`)
// 关键词匹配
// MatchKeyword 关键词匹配
func MatchKeyword(source, keyword string) bool {
if len(keyword) == 0 {
return false
@@ -16,7 +16,7 @@ func MatchKeyword(source, keyword string) bool {
pieces := whitespaceReg.Split(keyword, -1)
source = strings.ToLower(source)
for _, piece := range pieces {
if strings.Index(source, strings.ToLower(piece)) > -1 {
if strings.Contains(source, strings.ToLower(piece)) {
return true
}
}

View File

@@ -6,30 +6,31 @@ import (
"sync"
)
// 变量信息存储类型
// VariableHolder 变量信息存储类型
type VariableHolder string
type VariableHolders = []interface{}
var variableMapping = map[string][]interface{}{}
var variableMapping = map[string][]interface{}{} // source => [holder1, ...]
var variableLocker = sync.RWMutex{}
var regexpNamedVariable = regexp.MustCompile("\\${[\\w.-]+}")
var regexpNamedVariable = regexp.MustCompile(`\${[@\w.-]+}`)
// 分析变量
var stringBuilderPool = sync.Pool{
New: func() interface{} {
return &strings.Builder{}
},
}
// ParseVariables 分析变量
func ParseVariables(source string, replacer func(varName string) (value string)) string {
if len(source) == 0 {
return ""
}
variableLocker.RLock()
holders, found := variableMapping[source]
variableLocker.RUnlock()
if !found {
indexes := regexpNamedVariable.FindAllStringIndex(source, -1)
before := 0
for _, loc := range indexes {
holders = append(holders, []byte(source[before:loc[0]]))
holder := source[loc[0]+2 : loc[1]-1]
holders = append(holders, VariableHolder(holder))
before = loc[1]
}
if before < len(source) {
holders = append(holders, []byte(source[before:]))
}
holders = ParseHolders(source)
variableLocker.Lock()
variableMapping[source] = holders
variableLocker.Unlock()
@@ -40,6 +41,50 @@ func ParseVariables(source string, replacer func(varName string) (value string))
return source
}
// 只有一个占位时,我们快速返回
if len(holders) == 1 {
var h = holders[0]
holder, ok := h.(VariableHolder)
if ok {
return replacer(string(holder))
}
return source
}
// 多个占位时使用Builder
var builder = stringBuilderPool.Get().(*strings.Builder)
builder.Reset()
defer stringBuilderPool.Put(builder)
for _, h := range holders {
holder, ok := h.(VariableHolder)
if ok {
builder.WriteString(replacer(string(holder)))
} else {
builder.Write(h.([]byte))
}
}
return builder.String()
}
func ParseVariablesError(source string, replacer func(varName string) (value string, err error)) (string, error) {
var resultErr error
var result = ParseVariables(source, func(varName string) (value string) {
replacedValue, err := replacer(varName)
if err != nil {
resultErr = err
}
return replacedValue
})
return result, resultErr
}
// ParseVariablesFromHolders 从占位中分析变量
func ParseVariablesFromHolders(holders VariableHolders, replacer func(varName string) (value string)) string {
// no variables
if len(holders) == 0 {
return ""
}
// replace
result := strings.Builder{}
for _, h := range holders {
@@ -53,7 +98,26 @@ func ParseVariables(source string, replacer func(varName string) (value string))
return result.String()
}
// 判断是否有变量
// ParseHolders 分析占位
func ParseHolders(source string) (holders VariableHolders) {
indexes := regexpNamedVariable.FindAllStringIndex(source, -1)
before := 0
for _, loc := range indexes {
holders = append(holders, []byte(source[before:loc[0]]))
holder := source[loc[0]+2 : loc[1]-1]
holders = append(holders, VariableHolder(holder))
before = loc[1]
}
if before < len(source) {
holders = append(holders, []byte(source[before:]))
}
return holders
}
// HasVariables 判断是否有变量
func HasVariables(source string) bool {
if len(source) == 0 {
return false
}
return regexpNamedVariable.MatchString(source)
}

View File

@@ -1,16 +1,31 @@
package configutils
import (
"fmt"
"github.com/iwind/TeaGo/types"
"runtime"
"strconv"
"testing"
)
func TestParseVariables(t *testing.T) {
v := ParseVariables("hello, ${name}, world", func(s string) string {
return "Lu"
})
t.Log(v)
{
v := ParseVariables("hello, ${name}, world", func(s string) string {
return "Lu"
})
t.Log(v)
}
{
v := ParseVariables("hello, world", func(s string) string {
return "Lu"
})
t.Log(v)
}
{
v := ParseVariables("${name}", func(s string) string {
return "Lu"
})
t.Log(v)
}
}
func TestParseNoVariables(t *testing.T) {
@@ -22,13 +37,38 @@ func TestParseNoVariables(t *testing.T) {
}
}
func TestParseHolders(t *testing.T) {
var holders = ParseHolders("hello, ${name}, world")
for _, h := range holders {
t.Log(types.String(h))
}
t.Log("parse result:", ParseVariablesFromHolders(holders, func(s string) string {
return "[" + s + "]"
}))
}
func BenchmarkParseVariables(b *testing.B) {
_ = ParseVariables("hello, ${name}, ${age}, ${gender}, ${home}, world", func(s string) string {
return "Lu"
})
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = ParseVariables("hello, ${name}, ${age}, ${gender}, ${home}, world", func(s string) string {
return "Lu"
})
}
})
}
func BenchmarkParseVariablesFromHolders(b *testing.B) {
var holders = ParseHolders("hello, ${name}, ${age}, ${gender}, ${home}, world")
for i := 0; i < b.N; i++ {
_ = ParseVariables("hello, ${name}, ${age}, ${gender}, ${home}, world", func(s string) string {
_ = ParseVariablesFromHolders(holders, func(s string) string {
return "Lu"
})
}
@@ -36,7 +76,17 @@ func BenchmarkParseVariables(b *testing.B) {
func BenchmarkParseVariablesUnique(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = ParseVariables("hello, ${name} "+strconv.Itoa(i%1000), func(s string) string {
_ = ParseVariables("hello, ${name} "+strconv.Itoa(i), func(s string) string {
return "Lu"
})
}
}
func BenchmarkParseVariablesUnique_Single(b *testing.B) {
runtime.GOMAXPROCS(1)
for i := 0; i < b.N; i++ {
_ = ParseVariables("${name}", func(s string) string {
return "Lu"
})
}
@@ -44,7 +94,15 @@ func BenchmarkParseVariablesUnique(b *testing.B) {
func BenchmarkParseNoVariables(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = ParseVariables("hello, world, "+fmt.Sprintf("%d", i%1000), func(s string) string {
_ = ParseVariables("hello, world", func(s string) string {
return "Lu"
})
}
}
func BenchmarkParseEmpty(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = ParseVariables("", func(s string) string {
return "Lu"
})
}

View File

@@ -1,12 +1,12 @@
package configutils
import (
"github.com/go-yaml/yaml"
"io/ioutil"
"gopkg.in/yaml.v3"
"os"
)
func UnmarshalYamlFile(file string, ptr interface{}) error {
data, err := ioutil.ReadFile(file)
data, err := os.ReadFile(file)
if err != nil {
return err
}

1
pkg/dnsconfigs/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
ns_*

View File

@@ -1,12 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package dnsconfigs
type AccessLogRef struct {
IsPrior bool `yaml:"isPrior" json:"isPrior"` // 是否覆盖
IsOn bool `yaml:"isOn" json:"isOn"` // 是否启用
}
func (this *AccessLogRef) Init() error {
return nil
}

View File

@@ -2,6 +2,18 @@ package dnsconfigs
// ClusterDNSConfig 集群的DNS设置
type ClusterDNSConfig struct {
CNAMERecords []string `yaml:"cnameRecords" json:"cnameRecords"` // 自动加入的CNAME
TTL int32 `yaml:"ttl" json:"ttl"` // 默认TTL各个DNS服务商对记录的TTL的限制各有不同
CNAMEAsDomain bool `yaml:"cnameAsDomain" json:"cnameAsDomain"` // 是否可以像域名一样直接访问CNAME
IncludingLnNodes bool `yaml:"includingLnNodes" json:"includingLnNodes"` // 是否包含Ln节点
NodesAutoSync bool `yaml:"nodesAutoSync" json:"nodesAutoSync"` // 是否自动同步节点状态
ServersAutoSync bool `yaml:"serversAutoSync" json:"serversAutoSync"` // 是否自动同步服务状态
}
func DefaultClusterDNSConfig() *ClusterDNSConfig {
return &ClusterDNSConfig{
CNAMEAsDomain: true,
IncludingLnNodes: true,
}
}

View File

@@ -0,0 +1,29 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package dnsconfigs
import (
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/iwind/TeaGo/types"
)
type DNSResolver struct {
Host string `json:"host"`
Port int `json:"port"`
Protocol string `json:"protocol"`
}
func (this *DNSResolver) Addr() string {
var port = this.Port
if port <= 0 {
// 暂时不支持DoH
// 实际应用中只支持udp
switch this.Protocol {
case "tls":
port = 853
default:
port = 53
}
}
return configutils.QuoteIP(this.Host) + ":" + types.String(port)
}

View File

@@ -1,31 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package dnsconfigs
import "fmt"
type NSNodeConfig struct {
Id int64 `json:"id"`
ClusterId int64 `json:"clusterId"`
AccessLogRef *AccessLogRef `json:"accessLogRef"`
paddedId string
}
func (this *NSNodeConfig) Init() error {
this.paddedId = fmt.Sprintf("%08d", this.Id)
// accessLog
if this.AccessLogRef != nil {
err := this.AccessLogRef.Init()
if err != nil {
return err
}
}
return nil
}
func (this *NSNodeConfig) PaddedId() string {
return this.paddedId
}

View File

@@ -1,33 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package dnsconfigs
type RecordTTL struct {
Name string `json:"name"`
Value int `json:"value"`
}
func FindAllRecordTTL() []*RecordTTL {
return []*RecordTTL{
{
Name: "10分钟",
Value: 10 * 60,
},
{
Name: "30分钟",
Value: 30 * 60,
},
{
Name: "1小时",
Value: 3600,
},
{
Name: "12小时",
Value: 12 * 3600,
},
{
Name: "1天",
Value: 86400,
},
}
}

View File

@@ -13,11 +13,13 @@ const (
RecordTypeSRV RecordType = "SRV"
RecordTypeTXT RecordType = "TXT"
RecordTypeCAA RecordType = "CAA"
RecordTypeSOA RecordType = "SOA"
)
type RecordTypeDefinition struct {
Type RecordType `json:"type"`
Description string `json:"description"`
CanDefine bool `json:"canDefine"` // 用户是否可以自定义
}
func FindAllRecordTypeDefinitions() []*RecordTypeDefinition {
@@ -25,34 +27,57 @@ func FindAllRecordTypeDefinitions() []*RecordTypeDefinition {
{
Type: RecordTypeA,
Description: "将域名指向一个IPV4地址",
CanDefine: true,
},
{
Type: RecordTypeCNAME,
Description: "将域名指向另外一个域名",
CanDefine: true,
},
{
Type: RecordTypeAAAA,
Description: "将域名指向一个IPV6地址",
CanDefine: true,
},
{
Type: RecordTypeNS,
Description: "将子域名指定其他DNS服务器解析",
CanDefine: false,
},
{
Type: RecordTypeSOA,
Description: "起始授权机构记录",
CanDefine: false,
},
{
Type: RecordTypeMX,
Description: "将域名指向邮件服务器地址",
CanDefine: true,
},
{
Type: RecordTypeSRV,
Description: "记录提供特定的服务的服务器",
CanDefine: true,
},
{
Type: RecordTypeTXT,
Description: "文本长度限制512通常做SPF记录或者校验域名所有者",
CanDefine: true,
},
{
Type: RecordTypeCAA,
Description: "CA证书颁发机构授权校验",
CanDefine: true,
},
}
}
func FindAllUserRecordTypeDefinitions() []*RecordTypeDefinition {
var result = []*RecordTypeDefinition{}
for _, r := range FindAllRecordTypeDefinitions() {
if r.CanDefine {
result = append(result, r)
}
}
return result
}

View File

@@ -1,40 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package dnsconfigs
import "github.com/TeaOSLab/EdgeCommon/pkg/configutils"
type RouteRangeType = string
const (
RouteRangeTypeIP RouteRangeType = "ipRange"
)
type RouteRangeInterface interface {
Init() error
Contains(ip uint64) bool
}
// RouteRangeIPRange IP范围配置
type RouteRangeIPRange struct {
IPFrom string `json:"ipFrom"`
IPTo string `json:"ipTo"`
ipFromLong uint64
ipToLong uint64
}
func (this *RouteRangeIPRange) Init() error {
this.ipFromLong = configutils.IP2Long(this.IPFrom)
this.ipToLong = configutils.IP2Long(this.IPTo)
if this.ipFromLong > this.ipToLong {
this.ipFromLong, this.ipToLong = this.ipToLong, this.ipFromLong
}
return nil
}
func (this *RouteRangeIPRange) Contains(ip uint64) bool {
return this.ipFromLong <= ip && this.ipToLong >= ip
}

View File

@@ -13,9 +13,9 @@ func (this *DetailedError) Code() string {
return this.code
}
func NewDetailedError(code string, error string) *DetailedError {
func NewDetailedError(code string, errString string) *DetailedError {
return &DetailedError{
msg: error,
msg: errString,
code: code,
}
}

View File

@@ -29,7 +29,7 @@ func (this *errorObj) Error() string {
return s
}
// 新错误
// New 新错误
func New(errText string) error {
ptr, file, line, ok := runtime.Caller(1)
funcName := ""
@@ -45,7 +45,7 @@ func New(errText string) error {
}
}
// 包装已有错误
// Wrap 包装已有错误
func Wrap(err error) error {
if err == nil {
return nil

1
pkg/iplibrary/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*-plus.db

View File

@@ -0,0 +1,136 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary
import (
"bytes"
"compress/gzip"
_ "embed"
"net"
"sync"
)
//go:embed internal-ip-library.db
var ipLibraryData []byte
var defaultLibrary = NewIPLibrary()
var commonLibrary *IPLibrary
var libraryLocker = &sync.Mutex{} // 为了保持加载顺序性
func DefaultIPLibraryData() []byte {
return ipLibraryData
}
// InitDefault 加载默认的IP库
func InitDefault() error {
libraryLocker.Lock()
defer libraryLocker.Unlock()
if commonLibrary != nil {
defaultLibrary = commonLibrary
return nil
}
var library = NewIPLibrary()
err := library.InitFromData(ipLibraryData, "")
if err != nil {
return err
}
commonLibrary = library
defaultLibrary = commonLibrary
return nil
}
// Lookup 查询IP信息
func Lookup(ip net.IP) *QueryResult {
return defaultLibrary.Lookup(ip)
}
// LookupIP 查询IP信息
func LookupIP(ip string) *QueryResult {
return defaultLibrary.LookupIP(ip)
}
// LookupIPSummaries 查询一组IP对应的区域描述
func LookupIPSummaries(ipList []string) map[string]string /** ip => summary **/ {
var result = map[string]string{}
for _, ip := range ipList {
var region = LookupIP(ip)
if region != nil && region.IsOk() {
result[ip] = region.Summary()
}
}
return result
}
type IPLibrary struct {
reader *Reader
}
func NewIPLibrary() *IPLibrary {
return &IPLibrary{}
}
func NewIPLibraryWithReader(reader *Reader) *IPLibrary {
return &IPLibrary{reader: reader}
}
func (this *IPLibrary) InitFromData(data []byte, password string) error {
if len(data) == 0 || this.reader != nil {
return nil
}
if len(password) > 0 {
srcData, err := NewEncrypt().Decode(data, password)
if err != nil {
return err
}
data = srcData
}
var reader = bytes.NewReader(data)
gzipReader, err := gzip.NewReader(reader)
if err != nil {
return err
}
defer func() {
_ = gzipReader.Close()
}()
libReader, err := NewReader(gzipReader)
if err != nil {
return err
}
this.reader = libReader
return nil
}
func (this *IPLibrary) Lookup(ip net.IP) *QueryResult {
if this.reader == nil {
return &QueryResult{}
}
var result = this.reader.Lookup(ip)
if result == nil {
result = &QueryResult{}
}
return result
}
func (this *IPLibrary) LookupIP(ip string) *QueryResult {
if this.reader == nil {
return &QueryResult{}
}
return this.Lookup(net.ParseIP(ip))
}
func (this *IPLibrary) Destroy() {
if this.reader != nil {
this.reader.Destroy()
this.reader = nil
}
}

View File

@@ -0,0 +1,136 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary_test
import (
"fmt"
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"net"
"runtime"
"runtime/debug"
"testing"
"time"
)
func TestIPLibrary_Init(t *testing.T) {
var lib = iplibrary.NewIPLibrary()
err := lib.InitFromData(iplibrary.DefaultIPLibraryData(), "")
if err != nil {
t.Fatal(err)
}
}
func TestIPLibrary_Load(t *testing.T) {
for i := 0; i < 10; i++ {
err := iplibrary.InitDefault()
if err != nil {
t.Fatal(err)
}
}
}
func TestIPLibrary_Lookup(t *testing.T) {
var stat1 = &runtime.MemStats{}
runtime.ReadMemStats(stat1)
var lib = iplibrary.NewIPLibrary()
var before = time.Now()
err := lib.InitFromData(iplibrary.DefaultIPLibraryData(), "")
if err != nil {
t.Fatal(err)
}
var costMs = time.Since(before).Seconds() * 1000
runtime.GC()
debug.FreeOSMemory()
var stat2 = &runtime.MemStats{}
runtime.ReadMemStats(stat2)
t.Log((stat2.Alloc-stat1.Alloc)/1024/1024, "M", fmt.Sprintf("%.2f", costMs), "ms")
for _, ip := range []string{
"127.0.0.1",
"8.8.8.8",
"4.4.4.4",
"202.96.0.20",
"111.197.165.199",
"66.249.66.69",
"2222", // wrong ip
"2406:8c00:0:3401:133:18:168:70", // ipv6
} {
var result = lib.Lookup(net.ParseIP(ip))
t.Log(ip, "=>", result.IsOk(), "[", result.CountryName(), result.CountryId(), "][", result.ProvinceName(), result.ProvinceId(), "][", result.TownName(), result.TownId(), "][", result.ProviderName(), result.ProviderId(), "]")
}
}
func TestIPLibrary_LookupIP(t *testing.T) {
var lib = iplibrary.NewIPLibrary()
err := lib.InitFromData(iplibrary.DefaultIPLibraryData(), "")
if err != nil {
t.Fatal(err)
}
for _, ip := range []string{
"66.249.66.69",
} {
var result = lib.LookupIP(ip)
if result.IsOk() {
t.Log(ip, "=>", result.IsOk(), "[", result.CountryName(), result.CountryId(), "][", result.ProvinceName(), result.ProvinceId(), "][", result.TownName(), result.TownId(), "][", result.ProviderName(), result.ProviderId(), "]")
} else {
t.Log(ip, "=>", result.IsOk())
}
}
}
func TestIPLibrary_LookupIP_Summary(t *testing.T) {
var lib = iplibrary.NewIPLibrary()
err := lib.InitFromData(iplibrary.DefaultIPLibraryData(), "")
if err != nil {
t.Fatal(err)
}
for _, ip := range []string{
"66.249.66.69",
"123456", // wrong ip
"", // empty
} {
var result = lib.LookupIP(ip)
if result.IsOk() {
t.Log(ip, "=>", "region summary:", result.RegionSummary(), "summary:", result.Summary())
} else {
t.Log(ip, "=>", "region summary:", result.RegionSummary(), "summary:", result.Summary())
}
}
}
func TestIPLibrary_LookupIPSummaries(t *testing.T) {
_ = iplibrary.InitDefault()
t.Logf("%+v", iplibrary.LookupIPSummaries([]string{
"127.0.0.1",
"8.8.8.8",
"4.4.4.4",
"202.96.0.20",
"111.197.165.199",
"66.249.66.69",
"2222", // wrong ip
"2406:8c00:0:3401:133:18:168:70", // ipv6
}))
}
func BenchmarkIPLibrary_Lookup(b *testing.B) {
var lib = iplibrary.NewIPLibrary()
err := lib.InitFromData(iplibrary.DefaultIPLibraryData(), "")
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = lib.LookupIP("66.249.66.69")
}
}

32
pkg/iplibrary/encrypt.go Normal file
View File

@@ -0,0 +1,32 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary
import "github.com/TeaOSLab/EdgeCommon/pkg/nodeutils"
type Encrypt struct {
}
func NewEncrypt() *Encrypt {
return &Encrypt{}
}
func (this *Encrypt) Encode(srcData []byte, password string) ([]byte, error) {
var method = nodeutils.AES256CFBMethod{}
err := method.Init([]byte(password), []byte(password))
if err != nil {
return nil, err
}
return method.Encrypt(srcData)
}
func (this *Encrypt) Decode(encodedData []byte, password string) ([]byte, error) {
var method = nodeutils.AES256CFBMethod{}
err := method.Init([]byte(password), []byte(password))
if err != nil {
return nil, err
}
return method.Decrypt(encodedData)
}

Binary file not shown.

47
pkg/iplibrary/ip_item.go Normal file
View File

@@ -0,0 +1,47 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary
import (
"github.com/iwind/TeaGo/types"
)
type ipv4Item struct {
IPFrom uint32
IPTo uint32
Region *ipRegion
}
type ipv6Item struct {
IPFrom uint64
IPTo uint64
Region *ipRegion
}
type ipRegion struct {
CountryId uint16
ProvinceId uint16
CityId uint32
TownId uint32
ProviderId uint16
}
func HashRegion(countryId uint16, provinceId uint16, cityId uint32, townId uint32, providerId uint16) string {
var providerHash = ""
if providerId > 0 {
providerHash = "_" + types.String(providerId)
}
if townId > 0 {
return "t" + types.String(townId) + providerHash
}
if cityId > 0 {
return "c" + types.String(cityId) + providerHash
}
if provinceId > 0 {
return "p" + types.String(provinceId) + providerHash
}
return "a" + types.String(countryId) + providerHash
}

95
pkg/iplibrary/meta.go Normal file
View File

@@ -0,0 +1,95 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary
type Country struct {
Id uint16 `json:"id"`
Name string `json:"name"`
Codes []string `json:"codes"`
}
type Province struct {
Id uint16 `json:"id"`
Name string `json:"name"`
Codes []string `json:"codes"`
}
type City struct {
Id uint32 `json:"id"`
Name string `json:"name"`
Codes []string `json:"codes"`
}
type Town struct {
Id uint32 `json:"id"`
Name string `json:"name"`
Codes []string `json:"codes"`
}
type Provider struct {
Id uint16 `json:"id"`
Name string `json:"name"`
Codes []string `json:"codes"`
}
type Meta struct {
Version int `json:"version"` // IP库版本
Code string `json:"code"` // 代号用来区分不同的IP库
Author string `json:"author"`
Countries []*Country `json:"countries"`
Provinces []*Province `json:"provinces"`
Cities []*City `json:"cities"`
Towns []*Town `json:"towns"`
Providers []*Provider `json:"providers"`
CreatedAt int64 `json:"createdAt"`
countryMap map[uint16]*Country // id => *Country
provinceMap map[uint16]*Province // id => *Province
cityMap map[uint32]*City // id => *City
townMap map[uint32]*Town // id => *Town
providerMap map[uint16]*Provider // id => *Provider
}
func (this *Meta) Init() {
this.countryMap = map[uint16]*Country{}
this.provinceMap = map[uint16]*Province{}
this.cityMap = map[uint32]*City{}
this.townMap = map[uint32]*Town{}
this.providerMap = map[uint16]*Provider{}
for _, country := range this.Countries {
this.countryMap[country.Id] = country
}
for _, province := range this.Provinces {
this.provinceMap[province.Id] = province
}
for _, city := range this.Cities {
this.cityMap[city.Id] = city
}
for _, town := range this.Towns {
this.townMap[town.Id] = town
}
for _, provider := range this.Providers {
this.providerMap[provider.Id] = provider
}
}
func (this *Meta) CountryWithId(countryId uint16) *Country {
return this.countryMap[countryId]
}
func (this *Meta) ProvinceWithId(provinceId uint16) *Province {
return this.provinceMap[provinceId]
}
func (this *Meta) CityWithId(cityId uint32) *City {
return this.cityMap[cityId]
}
func (this *Meta) TownWithId(townId uint32) *Town {
return this.townMap[townId]
}
func (this *Meta) ProviderWithId(providerId uint16) *Provider {
return this.providerMap[providerId]
}

View File

@@ -0,0 +1,3 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary_test

64
pkg/iplibrary/parser.go Normal file
View File

@@ -0,0 +1,64 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary
import (
"bytes"
"errors"
)
type Parser struct {
config *ParserConfig
data []byte
}
func NewParser(config *ParserConfig) (*Parser, error) {
if config == nil {
config = &ParserConfig{}
}
if config.Template == nil {
return nil, errors.New("template must not be nil")
}
return &Parser{
config: config,
}, nil
}
func (this *Parser) Write(data []byte) {
this.data = append(this.data, data...)
}
func (this *Parser) Parse() error {
if len(this.data) == 0 {
return nil
}
for {
var index = bytes.IndexByte(this.data, '\n')
if index >= 0 {
var line = this.data[:index+1]
values, found := this.config.Template.Extract(string(line), this.config.EmptyValues)
if found {
if this.config.Iterator != nil {
err := this.config.Iterator(values)
if err != nil {
return err
}
}
} else {
// 防止错误信息太长
if len(line) > 256 {
line = line[:256]
}
return errors.New("invalid line '" + string(line) + "'")
}
this.data = this.data[index+1:]
} else {
break
}
}
return nil
}

View File

@@ -0,0 +1,9 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary
type ParserConfig struct {
Template *Template
EmptyValues []string
Iterator func(values map[string]string) error
}

View File

@@ -0,0 +1,54 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary
import (
"errors"
"io"
)
type ReaderParser struct {
reader io.Reader
rawParser *Parser
}
func NewReaderParser(reader io.Reader, config *ParserConfig) (*ReaderParser, error) {
if config == nil {
config = &ParserConfig{}
}
if config.Template == nil {
return nil, errors.New("template must not be nil")
}
parser, err := NewParser(config)
if err != nil {
return nil, err
}
return &ReaderParser{
reader: reader,
rawParser: parser,
}, nil
}
func (this *ReaderParser) Parse() error {
var buf = make([]byte, 1024)
for {
n, err := this.reader.Read(buf)
if n > 0 {
this.rawParser.Write(buf[:n])
parseErr := this.rawParser.Parse()
if parseErr != nil {
return parseErr
}
}
if err != nil {
if err != io.EOF {
return err
}
break
}
}
return nil
}

View File

@@ -0,0 +1,62 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary_test
import (
"bytes"
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"testing"
)
func TestNewReaderParser(t *testing.T) {
template, err := iplibrary.NewTemplate("${ipFrom}|${ipTo}|${country}|${any}|${province}|${city}|${provider}")
if err != nil {
t.Fatal(err)
}
var buf = &bytes.Buffer{}
buf.WriteString(`8.45.160.0|8.45.161.255|美国|0|华盛顿|西雅图|Level3
8.45.162.0|8.45.162.255|美国|0|马萨诸塞|0|Level3
8.45.163.0|8.45.164.255|美国|0|俄勒冈|0|Level3
8.45.165.0|8.45.165.255|美国|0|华盛顿|0|Level3
8.45.166.0|8.45.167.255|美国|0|华盛顿|西雅图|Level3
8.45.168.0|8.127.255.255|美国|0|0|0|Level3
8.128.0.0|8.128.3.255|中国|0|上海|上海市|阿里巴巴
8.128.4.0|8.128.255.255|中国|0|0|0|阿里巴巴
8.129.0.0|8.129.255.255|中国|0|广东省|深圳市|阿里云
8.130.0.0|8.130.55.255|中国|0|北京|北京市|阿里云
8.130.56.0|8.131.255.255|中国|0|北京|北京市|阿里巴巴
8.132.0.0|8.133.255.255|中国|0|上海|上海市|阿里巴巴
8.134.0.0|8.134.20.63|中国|0|0|0|阿里云
8.134.20.64|8.134.79.255|中国|0|广东省|深圳市|阿里云
8.134.80.0|8.191.255.255|中国|0|0|0|阿里巴巴
8.192.0.0|8.192.0.255|美国|0|0|0|Level3
8.192.1.0|8.192.1.255|美国|0|马萨诸塞|波士顿|Level3
8.192.2.0|8.207.255.255|美国|0|0|0|Level3
8.208.0.0|8.208.127.255|英国|0|伦敦|伦敦|阿里云
8.208.128.0|8.208.255.255|英国|0|伦敦|伦敦|阿里巴巴
8.209.0.0|8.209.15.255|俄罗斯|0|莫斯科|莫斯科|阿里云
8.209.16.0|8.209.31.255|俄罗斯|0|莫斯科|莫斯科|阿里巴巴
8.209.32.0|8.209.32.15|中国|0|台湾省|台北|阿里云
8.209.32.16|8.209.32.255|中国|0|台湾省|台北|阿里巴巴
8.209.33.0|8.209.34.255|中国|0|台湾省|台北|阿里云
8.209.35.0|8.209.35.255|中国|0|台湾省|台北|阿里巴巴`)
var count int
parser, err := iplibrary.NewReaderParser(buf, &iplibrary.ParserConfig{
Template: template,
EmptyValues: []string{"0"},
Iterator: func(values map[string]string) error {
count++
t.Log(count, values)
return nil
},
})
if err != nil {
t.Fatal(err)
}
err = parser.Parse()
if err != nil {
t.Fatal(err)
}
}

View File

@@ -0,0 +1,44 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary_test
import (
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"testing"
)
func TestNewParser(t *testing.T) {
template, err := iplibrary.NewTemplate("${ipFrom}|${ipTo}|${country}|${any}|${province}|${city}|${provider}")
if err != nil {
t.Fatal(err)
}
parser, err := iplibrary.NewParser(&iplibrary.ParserConfig{
Template: template,
EmptyValues: []string{"0"},
Iterator: func(values map[string]string) error {
t.Log(values)
return nil
},
})
if err != nil {
t.Fatal(err)
}
parser.Write([]byte(`0.0.0.0|0.255.255.255|0|0|0|内网IP|内网IP
1.0.0.0|1.0.0.255|澳大利亚|0|0|0|0
1.0.1.0|1.0.3.255|中国|0|福建省|福州市|电信
1.0.4.0|1.0.7.255|澳大利亚|0|维多利亚|墨尔本|0
1.0.8.0|1.0.15.255|中国|0|广东省|广州市|电信
1.0.16.0|1.0.31.255|日本|0|0|0|0
1.0.32.0|1.0.63.255|中国|0|广东省|广州市|电信
1.0.64.0|1.0.79.255|日本|0|广岛县|0|0
1.0.80.0|1.0.127.255|日本|0|冈山县|0|0
1.0.128.0|1.0.128.255|泰国|0|清莱府|0|TOT
1.0.129.0|1.0.132.191|泰国|0|曼谷|曼谷|TOT`))
err = parser.Parse()
if err != nil {
t.Fatal(err)
}
}

322
pkg/iplibrary/reader.go Normal file
View File

@@ -0,0 +1,322 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary
import (
"bytes"
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"io"
"net"
"sort"
"strconv"
"strings"
)
// Reader IP库Reader
type Reader struct {
meta *Meta
regionMap map[string]*ipRegion // 缓存重复的区域用来节约内存
ipV4Items []*ipv4Item
ipV6Items []*ipv6Item
lastIPFrom uint64
lastCountryId uint16
lastProvinceId uint16
lastCityId uint32
lastTownId uint32
lastProviderId uint16
}
// NewReader 创建新Reader对象
func NewReader(reader io.Reader) (*Reader, error) {
var libReader = &Reader{
regionMap: map[string]*ipRegion{},
}
err := libReader.load(reader)
if err != nil {
return nil, err
}
return libReader, nil
}
// 从Reader中加载数据
func (this *Reader) load(reader io.Reader) error {
var buf = make([]byte, 1024)
var metaLine []byte
var metaLineFound = false
var dataBuf = []byte{}
for {
n, err := reader.Read(buf)
if n > 0 {
var data = buf[:n]
dataBuf = append(dataBuf, data...)
if metaLineFound {
left, err := this.parse(dataBuf)
if err != nil {
return err
}
dataBuf = left
} else {
var index = bytes.IndexByte(dataBuf, '\n')
if index > 0 {
metaLine = dataBuf[:index]
dataBuf = dataBuf[index+1:]
metaLineFound = true
var meta = &Meta{}
err = json.Unmarshal(metaLine, &meta)
if err != nil {
return err
}
meta.Init()
this.meta = meta
left, err := this.parse(dataBuf)
if err != nil {
return err
}
dataBuf = left
}
}
}
if err != nil {
if err != io.EOF {
return err
}
break
}
}
sort.Slice(this.ipV4Items, func(i, j int) bool {
var from0 = this.ipV4Items[i].IPFrom
var to0 = this.ipV4Items[i].IPTo
var from1 = this.ipV4Items[j].IPFrom
var to1 = this.ipV4Items[j].IPTo
if from0 == from1 {
return to0 < to1
}
return from0 < from1
})
sort.Slice(this.ipV6Items, func(i, j int) bool {
var from0 = this.ipV6Items[i].IPFrom
var to0 = this.ipV6Items[i].IPTo
var from1 = this.ipV6Items[j].IPFrom
var to1 = this.ipV6Items[j].IPTo
if from0 == from1 {
return to0 < to1
}
return from0 < from1
})
// 清理内存
this.regionMap = nil
return nil
}
func (this *Reader) Lookup(ip net.IP) *QueryResult {
if ip == nil {
return &QueryResult{}
}
var ipLong = configutils.IP2Long(ip)
var isV4 = configutils.IsIPv4(ip)
var resultItem any
if isV4 {
sort.Search(len(this.ipV4Items), func(i int) bool {
var item = this.ipV4Items[i]
if item.IPFrom <= uint32(ipLong) {
if item.IPTo >= uint32(ipLong) {
resultItem = item
return false
}
return false
}
return true
})
} else {
sort.Search(len(this.ipV6Items), func(i int) bool {
var item = this.ipV6Items[i]
if item.IPFrom <= ipLong {
if item.IPTo >= ipLong {
resultItem = item
return false
}
return false
}
return true
})
}
return &QueryResult{
item: resultItem,
meta: this.meta,
}
}
func (this *Reader) Meta() *Meta {
return this.meta
}
func (this *Reader) IPv4Items() []*ipv4Item {
return this.ipV4Items
}
func (this *Reader) IPv6Items() []*ipv6Item {
return this.ipV6Items
}
func (this *Reader) Destroy() {
this.meta = nil
this.regionMap = nil
this.ipV4Items = nil
this.ipV6Items = nil
}
// 分析数据
func (this *Reader) parse(data []byte) (left []byte, err error) {
if len(data) == 0 {
return
}
for {
var index = bytes.IndexByte(data, '\n')
if index >= 0 {
var line = data[:index]
err = this.parseLine(line)
if err != nil {
return nil, err
}
data = data[index+1:]
} else {
left = data
break
}
}
return
}
// 单行分析
func (this *Reader) parseLine(line []byte) error {
const maxPieces = 8
var pieces = strings.Split(string(line), "|")
var countPieces = len(pieces)
if countPieces < maxPieces { // 补足一行
for i := 0; i < maxPieces-countPieces; i++ {
pieces = append(pieces, "")
}
} else if countPieces > maxPieces {
return errors.New("invalid ip definition '" + string(line) + "'")
}
var version = pieces[0]
if len(version) == 0 {
version = "4"
}
if version != "4" && version != "6" {
return errors.New("invalid ip version '" + string(line) + "'")
}
// ip range
var ipFrom uint64
var ipTo uint64
if strings.HasPrefix(pieces[1], "+") {
ipFrom = this.lastIPFrom + this.decodeUint64(pieces[1][1:])
} else {
ipFrom = this.decodeUint64(pieces[1])
}
if len(pieces[2]) == 0 {
ipTo = ipFrom
} else {
ipTo = this.decodeUint64(pieces[2]) + ipFrom
}
this.lastIPFrom = ipFrom
// country
var countryId uint16
if pieces[3] == "+" {
countryId = this.lastCountryId
} else {
countryId = uint16(this.decodeUint64(pieces[3]))
}
this.lastCountryId = countryId
var provinceId uint16
if pieces[4] == "+" {
provinceId = this.lastProvinceId
} else {
provinceId = uint16(this.decodeUint64(pieces[4]))
}
this.lastProvinceId = provinceId
// city
var cityId uint32
if pieces[5] == "+" {
cityId = this.lastCityId
} else {
cityId = uint32(this.decodeUint64(pieces[5]))
}
this.lastCityId = cityId
// town
var townId uint32
if pieces[6] == "+" {
townId = this.lastTownId
} else {
townId = uint32(this.decodeUint64(pieces[6]))
}
this.lastTownId = townId
// provider
var providerId uint16
if pieces[7] == "+" {
providerId = this.lastProviderId
} else {
providerId = uint16(this.decodeUint64(pieces[7]))
}
this.lastProviderId = providerId
var hash = HashRegion(countryId, provinceId, cityId, townId, providerId)
region, ok := this.regionMap[hash]
if !ok {
region = &ipRegion{
CountryId: countryId,
ProvinceId: provinceId,
CityId: cityId,
TownId: townId,
ProviderId: providerId,
}
this.regionMap[hash] = region
}
if version == "4" {
this.ipV4Items = append(this.ipV4Items, &ipv4Item{
IPFrom: uint32(ipFrom),
IPTo: uint32(ipTo),
Region: region,
})
} else {
this.ipV6Items = append(this.ipV6Items, &ipv6Item{
IPFrom: ipFrom,
IPTo: ipTo,
Region: region,
})
}
return nil
}
func (this *Reader) decodeUint64(s string) uint64 {
if this.meta != nil && this.meta.Version == Version2 {
i, _ := strconv.ParseUint(s, 32, 64)
return i
}
i, _ := strconv.ParseUint(s, 10, 64)
return i
}

View File

@@ -0,0 +1,71 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary
import (
"bytes"
"compress/gzip"
"fmt"
"io"
"net"
"os"
)
type FileReader struct {
rawReader *Reader
//password string
}
func NewFileReader(path string, password string) (*FileReader, error) {
fp, err := os.Open(path)
if err != nil {
return nil, err
}
defer func() {
_ = fp.Close()
}()
return NewFileDataReader(fp, password)
}
func NewFileDataReader(dataReader io.Reader, password string) (*FileReader, error) {
if len(password) > 0 {
data, err := io.ReadAll(dataReader)
if err != nil {
return nil, err
}
sourceData, err := NewEncrypt().Decode(data, password)
if err != nil {
return nil, err
}
dataReader = bytes.NewReader(sourceData)
}
gzReader, err := gzip.NewReader(dataReader)
if err != nil {
return nil, fmt.Errorf("create gzip reader failed: %w", err)
}
reader, err := NewReader(gzReader)
if err != nil {
return nil, err
}
return &FileReader{
rawReader: reader,
}, nil
}
func (this *FileReader) Meta() *Meta {
return this.rawReader.meta
}
func (this *FileReader) Lookup(ip net.IP) *QueryResult {
return this.rawReader.Lookup(ip)
}
func (this *FileReader) RawReader() *Reader {
return this.rawReader
}

View File

@@ -0,0 +1,51 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary_test
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"github.com/iwind/TeaGo/maps"
"net"
"testing"
)
func TestNewFileReader(t *testing.T) {
reader, err := iplibrary.NewFileReader("./ip-20c1461c.db", "123456")
if err != nil {
t.Fatal(err)
}
for _, ip := range []string{
"127.0.0.1",
"192.168.0.1",
"192.168.0.150",
"8.8.8.8",
"111.197.165.199",
"175.178.206.125",
} {
var result = reader.Lookup(net.ParseIP(ip))
if result.IsOk() {
var data = maps.Map{
"countryId": result.CountryId(),
"countryName": result.CountryName(),
"provinceId": result.ProvinceId(),
"provinceName": result.ProvinceName(),
"cityId": result.CityId(),
"cityName": result.CityName(),
"townId": result.TownId(),
"townName": result.TownName(),
"providerId": result.ProviderId(),
"providerName": result.ProviderName(),
"summary": result.Summary(),
}
dataJSON, err := json.MarshalIndent(data, "", " ")
if err != nil {
t.Fatal(err)
}
t.Log(ip, "=>", string(dataJSON))
} else {
t.Log(ip+":", "not found")
}
}
}

View File

@@ -0,0 +1,276 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary
import (
"github.com/iwind/TeaGo/lists"
"strings"
)
type QueryResult struct {
item any
meta *Meta
}
func (this *QueryResult) IsOk() bool {
return this.item != nil
}
func (this *QueryResult) CountryId() int64 {
return int64(this.realCountryId())
}
func (this *QueryResult) CountryName() string {
if this.item == nil {
return ""
}
var countryId = this.realCountryId()
if countryId > 0 {
var country = this.meta.CountryWithId(countryId)
if country != nil {
return country.Name
}
}
return ""
}
func (this *QueryResult) CountryCodes() []string {
if this.item == nil {
return nil
}
var countryId = this.realCountryId()
if countryId > 0 {
var country = this.meta.CountryWithId(countryId)
if country != nil {
return country.Codes
}
}
return nil
}
func (this *QueryResult) ProvinceId() int64 {
return int64(this.realProvinceId())
}
func (this *QueryResult) ProvinceName() string {
if this.item == nil {
return ""
}
var provinceId = this.realProvinceId()
if provinceId > 0 {
var province = this.meta.ProvinceWithId(provinceId)
if province != nil {
return province.Name
}
}
return ""
}
func (this *QueryResult) ProvinceCodes() []string {
if this.item == nil {
return nil
}
var provinceId = this.realProvinceId()
if provinceId > 0 {
var province = this.meta.ProvinceWithId(provinceId)
if province != nil {
return province.Codes
}
}
return nil
}
func (this *QueryResult) CityId() int64 {
return int64(this.realCityId())
}
func (this *QueryResult) CityName() string {
if this.item == nil {
return ""
}
var cityId = this.realCityId()
if cityId > 0 {
var city = this.meta.CityWithId(cityId)
if city != nil {
return city.Name
}
}
return ""
}
func (this *QueryResult) TownId() int64 {
return int64(this.realTownId())
}
func (this *QueryResult) TownName() string {
if this.item == nil {
return ""
}
var townId = this.realTownId()
if townId > 0 {
var town = this.meta.TownWithId(townId)
if town != nil {
return town.Name
}
}
return ""
}
func (this *QueryResult) ProviderId() int64 {
return int64(this.realProviderId())
}
func (this *QueryResult) ProviderName() string {
if this.item == nil {
return ""
}
var providerId = this.realProviderId()
if providerId > 0 {
var provider = this.meta.ProviderWithId(providerId)
if provider != nil {
return provider.Name
}
}
return ""
}
func (this *QueryResult) ProviderCodes() []string {
if this.item == nil {
return nil
}
var providerId = this.realProviderId()
if providerId > 0 {
var provider = this.meta.ProviderWithId(providerId)
if provider != nil {
return provider.Codes
}
}
return nil
}
func (this *QueryResult) Summary() string {
if this.item == nil {
return ""
}
var pieces = []string{}
var countryName = this.CountryName()
var provinceName = this.ProvinceName()
var cityName = this.CityName()
var townName = this.TownName()
var providerName = this.ProviderName()
if len(countryName) > 0 {
pieces = append(pieces, countryName)
}
if len(provinceName) > 0 && !lists.ContainsString(pieces, provinceName) {
pieces = append(pieces, provinceName)
}
if len(cityName) > 0 && !lists.ContainsString(pieces, cityName) && !lists.ContainsString(pieces, strings.TrimSuffix(cityName, "市")) {
pieces = append(pieces, cityName)
}
if len(townName) > 0 && !lists.ContainsString(pieces, townName) && !lists.ContainsString(pieces, strings.TrimSuffix(townName, "县")) {
pieces = append(pieces, townName)
}
if len(providerName) > 0 && !lists.ContainsString(pieces, providerName) {
if len(pieces) > 0 {
pieces = append(pieces, "|")
}
pieces = append(pieces, providerName)
}
return strings.Join(pieces, " ")
}
func (this *QueryResult) RegionSummary() string {
if this.item == nil {
return ""
}
var pieces = []string{}
var countryName = this.CountryName()
var provinceName = this.ProvinceName()
var cityName = this.CityName()
var townName = this.TownName()
if len(countryName) > 0 {
pieces = append(pieces, countryName)
}
if len(provinceName) > 0 && !lists.ContainsString(pieces, provinceName) {
pieces = append(pieces, provinceName)
}
if len(cityName) > 0 && !lists.ContainsString(pieces, cityName) && !lists.ContainsString(pieces, strings.TrimSuffix(cityName, "市")) {
pieces = append(pieces, cityName)
}
if len(townName) > 0 && !lists.ContainsString(pieces, townName) && !lists.ContainsString(pieces, strings.TrimSuffix(townName, "县")) {
pieces = append(pieces, townName)
}
return strings.Join(pieces, " ")
}
func (this *QueryResult) realCountryId() uint16 {
if this.item != nil {
switch item := this.item.(type) {
case *ipv4Item:
return item.Region.CountryId
case *ipv6Item:
return item.Region.CountryId
}
}
return 0
}
func (this *QueryResult) realProvinceId() uint16 {
if this.item != nil {
switch item := this.item.(type) {
case *ipv4Item:
return item.Region.ProvinceId
case *ipv6Item:
return item.Region.ProvinceId
}
}
return 0
}
func (this *QueryResult) realCityId() uint32 {
if this.item != nil {
switch item := this.item.(type) {
case *ipv4Item:
return item.Region.CityId
case *ipv6Item:
return item.Region.CityId
}
}
return 0
}
func (this *QueryResult) realTownId() uint32 {
if this.item != nil {
switch item := this.item.(type) {
case *ipv4Item:
return item.Region.TownId
case *ipv6Item:
return item.Region.TownId
}
}
return 0
}
func (this *QueryResult) realProviderId() uint16 {
if this.item != nil {
switch item := this.item.(type) {
case *ipv4Item:
return item.Region.ProviderId
case *ipv6Item:
return item.Region.ProviderId
}
}
return 0
}

View File

@@ -0,0 +1,149 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary_test
import (
"bytes"
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"net"
"runtime"
"testing"
"time"
)
func TestNewReader(t *testing.T) {
var buf = &bytes.Buffer{}
var writer = iplibrary.NewWriter(buf, &iplibrary.Meta{
Author: "GoEdge <https://goedge.cn>",
})
err := writer.WriteMeta()
if err != nil {
t.Fatal(err)
}
err = writer.Write("192.168.1.100", "192.168.1.100", 1, 200, 300, 400, 500)
if err != nil {
t.Fatal(err)
}
err = writer.Write("192.168.2.100", "192.168.3.100", 2, 201, 301, 401, 501)
if err != nil {
t.Fatal(err)
}
err = writer.Write("192.168.3.101", "192.168.3.101", 3, 201, 301, 401, 501)
if err != nil {
t.Fatal(err)
}
err = writer.Write("192.168.0.101", "192.168.0.200", 4, 201, 301, 401, 501)
if err != nil {
t.Fatal(err)
}
err = writer.Write("::1", "::5", 5, 201, 301, 401, 501)
if err != nil {
t.Fatal(err)
}
/**var n = func() string {
return types.String(rands.Int(0, 255))
}
for i := 0; i < 1_000_000; i++ {
err = writer.Write(n()+"."+n()+"."+n()+"."+n(), n()+"."+n()+"."+n()+"."+n(), int64(i)+100, 201, 301, 401, 501)
if err != nil {
t.Fatal(err)
}
}**/
var stat = &runtime.MemStats{}
runtime.ReadMemStats(stat)
reader, err := iplibrary.NewReader(buf)
var stat2 = &runtime.MemStats{}
runtime.ReadMemStats(stat2)
t.Log((stat2.Alloc-stat.Alloc)/1024/1024, "M")
if err != nil {
t.Fatal(err)
}
t.Log("version:", reader.Meta().Version, "author:", reader.Meta().Author, "createdTime:", timeutil.FormatTime("Y-m-d H:i:s", reader.Meta().CreatedAt))
if len(reader.IPv4Items()) < 10 {
t.Log("===")
for _, item := range reader.IPv4Items() {
t.Logf("%+v", item)
}
t.Log("===")
}
if len(reader.IPv6Items()) < 10 {
t.Log("===")
for _, item := range reader.IPv6Items() {
t.Logf("%+v", item)
}
t.Log("===")
}
var before = time.Now()
for _, ip := range []string{
"192.168.0.1",
"192.168.0.150",
"192.168.1.100",
"192.168.2.100",
"192.168.3.50",
"192.168.0.150",
"192.168.4.80",
"::3",
"::8",
} {
var result = reader.Lookup(net.ParseIP(ip))
if result.IsOk() {
t.Log(ip+":", "countryId:", result.CountryId())
} else {
t.Log(ip+":", "not found")
}
}
t.Log(time.Since(before).Seconds()*1000, "ms")
}
func BenchmarkNewReader(b *testing.B) {
runtime.GOMAXPROCS(1)
var buf = &bytes.Buffer{}
var writer = iplibrary.NewWriter(buf, &iplibrary.Meta{
Author: "GoEdge <https://goedge.cn>",
})
err := writer.WriteMeta()
if err != nil {
b.Fatal(err)
}
var n = func() string {
return types.String(rands.Int(0, 255))
}
for i := 0; i < 1_000_000; i++ {
err = writer.Write(n()+"."+n()+"."+n()+"."+n(), n()+"."+n()+"."+n()+"."+n(), int64(i)+100, 201, 301, 401, 501)
if err != nil {
b.Fatal(err)
}
}
reader, err := iplibrary.NewReader(buf)
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
var ip = "192.168.1.100"
reader.Lookup(net.ParseIP(ip))
}
}

80
pkg/iplibrary/template.go Normal file
View File

@@ -0,0 +1,80 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary
import (
"github.com/iwind/TeaGo/lists"
"regexp"
)
type Template struct {
templateString string
reg *regexp.Regexp
}
func NewTemplate(templateString string) (*Template, error) {
var t = &Template{
templateString: templateString,
}
err := t.init()
if err != nil {
return nil, err
}
return t, nil
}
func (this *Template) init() error {
var template = regexp.QuoteMeta(this.templateString)
var keywordReg = regexp.MustCompile(`\\\$\\{(\w+)\\}`)
template = keywordReg.ReplaceAllStringFunc(template, func(keyword string) string {
var matches = keywordReg.FindStringSubmatch(keyword)
if len(matches) > 1 {
switch matches[1] {
case "ipFrom", "ipTo", "country", "province", "city", "town", "provider":
return "(?P<" + matches[1] + ">.*)"
}
return ".*"
}
return keyword
})
reg, err := regexp.Compile("^(?U)" + template + "\n?$")
if err != nil {
return err
}
this.reg = reg
return nil
}
func (this *Template) Extract(text string, emptyValues []string) (values map[string]string, ok bool) {
var matches = this.reg.FindStringSubmatch(text)
if len(matches) == 0 {
return
}
values = map[string]string{}
for index, name := range this.reg.SubexpNames() {
if len(name) == 0 {
continue
}
var v = matches[index]
if name != "ipFrom" && name != "ipTo" && (v == "0" || v == "无" || v == "空" || lists.ContainsString(emptyValues, v)) {
v = ""
}
values[name] = v
}
for _, keyword := range []string{"ipFrom", "ipTo", "country", "province", "city", "town", "provider"} {
_, hasKeyword := values[keyword]
if !hasKeyword {
values[keyword] = ""
}
}
// 自动修复省略的城市名
if len(values["city"]) == 0 && len(values["province"]) > 0 && len(values["town"]) > 0 {
values["city"] = values["province"]
}
ok = true
return
}

View File

@@ -0,0 +1,39 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary_test
import (
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"testing"
)
func TestNewTemplate(t *testing.T) {
template, err := iplibrary.NewTemplate("${ipFrom}|${ipTo}|${country}|${any}|${province}|${city}|${provider}")
if err != nil {
t.Fatal(err)
}
for _, s := range []string{
"0.0.0.0|0.255.255.255|0|0|0|内网IP|内网IP",
"42.0.32.0|42.0.63.255|中国|0|广东省|广州市|电信",
"42.0.32.0|42.0.63.255|中国|0|广东省|广州市|电信\n123",
"42.0.32.0|42.0.63.255|中国||广东省|广州市|电信",
"42.0.32.0|42.0.63.255|中国|0||广州市|电信",
"42.0.32.0|42.0.63.255|中国|0|广东省|广州市",
} {
values, ok := template.Extract(s, []string{})
t.Log(ok, s, "=>\n", values)
}
}
func TestNewTemplate2(t *testing.T) {
template, err := iplibrary.NewTemplate("${any},${any},${ipFrom},${ipTo},${country},${province},${city},${town},${provider},${any},${any}")
if err != nil {
t.Fatal(err)
}
for _, s := range []string{
"22723584,22723839,1.90.188.0,1.90.188.255,中国,北京,北京,房山,歌华有线,102400,010,城域网",
} {
values, _ := template.Extract(s, []string{})
t.Log(s, "=>\n", values)
}
}

269
pkg/iplibrary/updater.go Normal file
View File

@@ -0,0 +1,269 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary
import (
"errors"
"fmt"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"io"
"os"
"strings"
"time"
)
type UpdaterSource interface {
// DataDir 文件目录
DataDir() string
// FindLatestFile 检查最新的IP库文件
FindLatestFile() (code string, fileId int64, err error)
// DownloadFile 下载文件
DownloadFile(fileId int64, writer io.Writer) error
// LogInfo 普通日志
LogInfo(message string)
// LogError 错误日志
LogError(err error)
}
type Updater struct {
source UpdaterSource
currentCode string
ticker *time.Ticker
isUpdating bool
}
func NewUpdater(source UpdaterSource, interval time.Duration) *Updater {
return &Updater{
source: source,
ticker: time.NewTicker(interval),
}
}
func (this *Updater) Start() {
// 初始化
err := this.Init()
if err != nil {
this.source.LogError(err)
}
// 先运行一次
err = this.Loop()
if err != nil {
this.source.LogError(err)
}
// 开始定时运行
for range this.ticker.C {
err = this.Loop()
if err != nil {
this.source.LogError(err)
}
}
}
func (this *Updater) Init() error {
// 检查当前正在使用的IP库
var path = this.source.DataDir() + "/ip-library.db"
fp, err := os.Open(path)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return fmt.Errorf("read ip library file failed '%w'", err)
}
defer func() {
_ = fp.Close()
}()
return this.loadFile(fp)
}
func (this *Updater) Loop() error {
if this.isUpdating {
return nil
}
this.isUpdating = true
defer func() {
this.isUpdating = false
}()
code, fileId, err := this.source.FindLatestFile()
if err != nil {
// 不提示连接错误
if this.isConnError(err) {
return nil
}
return err
}
if len(code) == 0 || fileId <= 0 {
// 还原到内置IP库
if len(this.currentCode) > 0 {
this.currentCode = ""
this.source.LogInfo("resetting to default ip library ...")
var defaultPath = this.source.DataDir() + "/ip-library.db"
_, err = os.Stat(defaultPath)
if err == nil {
err = os.Remove(defaultPath)
if err != nil {
this.source.LogError(errors.New("can not remove default 'ip-library.db'"))
}
}
err = InitDefault()
if err != nil {
this.source.LogError(errors.New("initialize default ip library failed: " + err.Error()))
}
}
return nil
}
// 下载
if this.currentCode == code {
// 不再重复下载
return nil
}
// 检查是否存在
var dir = this.source.DataDir()
var path = dir + "/ip-" + code + ".db"
stat, err := os.Stat(path)
if err == nil && !stat.IsDir() && stat.Size() > 0 {
fp, err := os.Open(path)
if err != nil {
return err
}
defer func() {
_ = fp.Close()
}()
err = this.loadFile(fp)
if err != nil {
// 尝试删除
_ = os.Remove(path)
} else {
this.currentCode = code
// 拷贝到 ip-library.db
err = this.createDefaultFile(path, dir)
if err != nil {
this.source.LogError(err)
}
}
return err
}
// write to file
fp, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
if err != nil {
return fmt.Errorf("create ip library file failed: %w", err)
}
var isOk = false
defer func() {
if !isOk {
_ = os.Remove(path)
}
}()
err = this.source.DownloadFile(fileId, fp)
if err != nil {
_ = fp.Close()
return err
}
err = fp.Close()
if err != nil {
return nil
}
// load library from file
fp, err = os.Open(path)
if err != nil {
return nil
}
err = this.loadFile(fp)
_ = fp.Close()
if err != nil {
return fmt.Errorf("load file failed: %w", err)
}
isOk = true
this.currentCode = code
// 拷贝到 ip-library.db
err = this.createDefaultFile(path, dir)
if err != nil {
this.source.LogError(err)
}
return nil
}
func (this *Updater) loadFile(fp *os.File) error {
this.source.LogInfo("load ip library from '" + fp.Name() + "' ...")
fileReader, err := NewFileDataReader(fp, "")
if err != nil {
return fmt.Errorf("load ip library from reader failed: %w", err)
}
var reader = fileReader.RawReader()
defaultLibrary = NewIPLibraryWithReader(reader)
this.currentCode = reader.Meta().Code
return nil
}
func (this *Updater) createDefaultFile(sourcePath string, dir string) error {
sourceFp, err := os.Open(sourcePath)
if err != nil {
return fmt.Errorf("prepare to copy file to 'ip-library.db' failed: %w", err)
}
defer func() {
_ = sourceFp.Close()
}()
dstFp, err := os.Create(dir + "/ip-library.db")
if err != nil {
return fmt.Errorf("prepare to copy file to 'ip-library.db' failed: %w", err)
}
defer func() {
_ = dstFp.Close()
}()
_, err = io.Copy(dstFp, sourceFp)
if err != nil {
return fmt.Errorf("copy file to 'ip-library.db' failed: %w", err)
}
return nil
}
// isConnError 是否为连接错误
func (this *Updater) isConnError(err error) bool {
if err == nil {
return false
}
// 检查是否为连接错误
statusErr, ok := status.FromError(err)
if ok {
var errorCode = statusErr.Code()
return errorCode == codes.Unavailable || errorCode == codes.Canceled
}
if strings.Contains(err.Error(), "code = Canceled") {
return true
}
return false
}

View File

@@ -0,0 +1,53 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary_test
import (
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"github.com/iwind/TeaGo/Tea"
_ "github.com/iwind/TeaGo/bootstrap"
"io"
"testing"
"time"
)
type updaterSource struct {
t *testing.T
}
func (this *updaterSource) DataDir() string {
return Tea.Root + "/data"
}
func (this *updaterSource) FindLatestFile() (code string, fileId int64, err error) {
return "CODE", 1, nil
}
func (this *updaterSource) DownloadFile(fileId int64, writer io.Writer) error {
this.t.Log("downloading file:", fileId, "writer:", writer)
_, err := writer.Write(iplibrary.DefaultIPLibraryData())
return err
}
func (this *updaterSource) LogInfo(message string) {
this.t.Log(message)
}
func (this *updaterSource) LogError(err error) {
this.t.Fatal(err)
}
func TestNewUpdater(t *testing.T) {
var updater = iplibrary.NewUpdater(&updaterSource{
t: t,
}, 1*time.Minute)
err := updater.Init()
if err != nil {
t.Fatal(err)
}
err = updater.Loop()
if err != nil {
t.Fatal(err)
}
}

10
pkg/iplibrary/version.go Normal file
View File

@@ -0,0 +1,10 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary
type Version = int
const (
Version1 Version = 1
Version2 Version = 2 // 主要变更为数字使用32进制
)

202
pkg/iplibrary/writer.go Normal file
View File

@@ -0,0 +1,202 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary
import (
"crypto/md5"
"encoding/json"
"errors"
"fmt"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"hash"
"io"
"net"
"strconv"
"strings"
"time"
)
type hashWriter struct {
rawWriter io.Writer
hash hash.Hash
}
func newHashWriter(writer io.Writer) *hashWriter {
return &hashWriter{
rawWriter: writer,
hash: md5.New(),
}
}
func (this *hashWriter) Write(p []byte) (n int, err error) {
n, err = this.rawWriter.Write(p)
this.hash.Write(p)
return
}
func (this *hashWriter) Sum() string {
return fmt.Sprintf("%x", this.hash.Sum(nil))
}
type Writer struct {
writer *hashWriter
meta *Meta
lastIPFrom uint64 // 上一次的IP
lastCountryId int64
lastProvinceId int64
lastCityId int64
lastTownId int64
lastProviderId int64
}
func NewWriter(writer io.Writer, meta *Meta) *Writer {
if meta == nil {
meta = &Meta{}
}
meta.Version = Version2
meta.CreatedAt = time.Now().Unix()
var libWriter = &Writer{
writer: newHashWriter(writer),
meta: meta,
}
return libWriter
}
func (this *Writer) WriteMeta() error {
metaJSON, err := json.Marshal(this.meta)
if err != nil {
return err
}
_, err = this.writer.Write(metaJSON)
if err != nil {
return err
}
_, err = this.writer.Write([]byte("\n"))
return err
}
func (this *Writer) Write(ipFrom string, ipTo string, countryId int64, provinceId int64, cityId int64, townId int64, providerId int64) error {
// validate IP
var fromIP = net.ParseIP(ipFrom)
if fromIP == nil {
return errors.New("invalid 'ipFrom': '" + ipFrom + "'")
}
var fromIsIPv4 = configutils.IsIPv4(fromIP)
var toIP = net.ParseIP(ipTo)
if toIP == nil {
return errors.New("invalid 'ipTo': " + ipTo)
}
var toIsIPv4 = configutils.IsIPv4(toIP)
if fromIsIPv4 != toIsIPv4 {
return errors.New("'ipFrom(" + ipFrom + ")' and 'ipTo(" + ipTo + ")' should have the same IP version")
}
var pieces = []string{}
// 0
if fromIsIPv4 {
pieces = append(pieces, "")
} else {
pieces = append(pieces, "6")
}
// 1
var fromIPLong = configutils.IP2Long(fromIP)
var toIPLong = configutils.IP2Long(toIP)
if toIPLong < fromIPLong {
fromIPLong, toIPLong = toIPLong, fromIPLong
}
if this.lastIPFrom > 0 && fromIPLong > this.lastIPFrom {
pieces = append(pieces, "+"+this.formatUint64(fromIPLong-this.lastIPFrom))
} else {
pieces = append(pieces, this.formatUint64(fromIPLong))
}
this.lastIPFrom = fromIPLong
if ipFrom == ipTo {
// 2
pieces = append(pieces, "")
} else {
// 2
pieces = append(pieces, this.formatUint64(toIPLong-fromIPLong))
}
// 3
if countryId > 0 {
if countryId == this.lastCountryId {
pieces = append(pieces, "+")
} else {
pieces = append(pieces, this.formatUint64(uint64(countryId)))
}
} else {
pieces = append(pieces, "")
}
this.lastCountryId = countryId
// 4
if provinceId > 0 {
if provinceId == this.lastProvinceId {
pieces = append(pieces, "+")
} else {
pieces = append(pieces, this.formatUint64(uint64(provinceId)))
}
} else {
pieces = append(pieces, "")
}
this.lastProvinceId = provinceId
// 5
if cityId > 0 {
if cityId == this.lastCityId {
pieces = append(pieces, "+")
} else {
pieces = append(pieces, this.formatUint64(uint64(cityId)))
}
} else {
pieces = append(pieces, "")
}
this.lastCityId = cityId
// 6
if townId > 0 {
if townId == this.lastTownId {
pieces = append(pieces, "+")
} else {
pieces = append(pieces, this.formatUint64(uint64(townId)))
}
} else {
pieces = append(pieces, "")
}
this.lastTownId = townId
// 7
if providerId > 0 {
if providerId == this.lastProviderId {
pieces = append(pieces, "+")
} else {
pieces = append(pieces, this.formatUint64(uint64(providerId)))
}
} else {
pieces = append(pieces, "")
}
this.lastProviderId = providerId
_, err := this.writer.Write([]byte(strings.TrimRight(strings.Join(pieces, "|"), "|")))
if err != nil {
return err
}
_, err = this.writer.Write([]byte("\n"))
return err
}
func (this *Writer) Sum() string {
return this.writer.Sum()
}
func (this *Writer) formatUint64(i uint64) string {
return strconv.FormatUint(i, 32)
}

View File

@@ -0,0 +1,80 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary
import (
"compress/gzip"
"os"
)
type FileWriter struct {
fp *os.File
gzWriter *gzip.Writer
password string
rawWriter *Writer
}
func NewFileWriter(path string, meta *Meta, password string) (*FileWriter, error) {
fp, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
if err != nil {
return nil, err
}
gzWriter, err := gzip.NewWriterLevel(fp, gzip.BestCompression)
if err != nil {
return nil, err
}
var writer = &FileWriter{
fp: fp,
gzWriter: gzWriter,
rawWriter: NewWriter(gzWriter, meta),
password: password,
}
return writer, nil
}
func (this *FileWriter) WriteMeta() error {
return this.rawWriter.WriteMeta()
}
func (this *FileWriter) Write(ipFrom string, ipTo string, countryId int64, provinceId int64, cityId int64, townId int64, providerId int64) error {
return this.rawWriter.Write(ipFrom, ipTo, countryId, provinceId, cityId, townId, providerId)
}
func (this *FileWriter) Sum() string {
return this.rawWriter.Sum()
}
func (this *FileWriter) Close() error {
err1 := this.gzWriter.Close()
err2 := this.fp.Close()
if err1 != nil {
return err1
}
if err2 != nil {
return err2
}
// 加密内容
if len(this.password) > 0 {
var filePath = this.fp.Name()
data, err := os.ReadFile(filePath)
if err != nil {
return err
}
if len(data) > 0 {
encodedData, err := NewEncrypt().Encode(data, this.password)
if err != nil {
return err
}
err = os.WriteFile(filePath, encodedData, 0666)
if err != nil {
return err
}
}
}
return nil
}

View File

@@ -0,0 +1,56 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary_test
import (
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
"testing"
)
func TestNewFileWriter(t *testing.T) {
writer, err := iplibrary.NewFileWriter("./internal-ip-library-test.db", &iplibrary.Meta{
Author: "GoEdge",
}, "")
if err != nil {
t.Fatal(err)
}
err = writer.WriteMeta()
if err != nil {
t.Fatal(err)
}
err = writer.Write("192.168.1.100", "192.168.1.100", 100, 200, 300, 400, 500)
if err != nil {
t.Fatal(err)
}
err = writer.Write("192.168.2.100", "192.168.3.100", 101, 201, 301, 401, 501)
if err != nil {
t.Fatal(err)
}
err = writer.Write("192.168.3.101", "192.168.3.101", 101, 201, 301, 401, 501)
if err != nil {
t.Fatal(err)
}
var n = func() string {
return types.String(rands.Int(0, 255))
}
for i := 0; i < 1; i++ {
err = writer.Write(n()+"."+n()+"."+n()+"."+n(), n()+"."+n()+"."+n()+"."+n(), int64(i)+100, 201, 301, 401, 501)
if err != nil {
t.Fatal(err)
}
}
err = writer.Close()
if err != nil {
t.Fatal(err)
}
t.Log("ok", writer.Sum())
}

View File

@@ -0,0 +1,44 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package iplibrary_test
import (
"bytes"
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"testing"
)
func TestNewWriter(t *testing.T) {
var buf = &bytes.Buffer{}
var writer = iplibrary.NewWriter(buf, &iplibrary.Meta{
Author: "GoEdge <https://goedge.cn>",
})
err := writer.WriteMeta()
if err != nil {
t.Fatal(err)
}
err = writer.Write("192.168.1.100", "192.168.1.100", 100, 200, 300, 400, 500)
if err != nil {
t.Fatal(err)
}
err = writer.Write("192.168.2.100", "192.168.3.100", 101, 201, 301, 401, 501)
if err != nil {
t.Fatal(err)
}
err = writer.Write("192.168.3.101", "192.168.3.101", 101, 201, 301, 401, 501)
if err != nil {
t.Fatal(err)
}
err = writer.Write("::1", "::2", 101, 201, 301, 401, 501)
if err != nil {
t.Fatal(err)
}
t.Log(buf.String())
t.Log("sum:", writer.Sum())
}

716
pkg/langs/codes/codes.go Normal file
View File

@@ -0,0 +1,716 @@
// generated by run 'langs generate'
package codes
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
)
const (
ACMEProviderAccount_LogCreateACMEProviderAccount langs.MessageCode = "acme_provider_account@log_create_acme_provider_account" // 创建ACME服务商账号 %d
ACMEProviderAccount_LogDeleteACMEProviderAccount langs.MessageCode = "acme_provider_account@log_delete_acme_provider_account" // 删除ACME服务商账号 %d
ACMEProviderAccount_LogUpdateACMEProviderAccount langs.MessageCode = "acme_provider_account@log_update_acme_provider_account" // 修改ACME服务商账号 %d
ACMETask_LogCreateACMETask langs.MessageCode = "acme_task@log_create_acme_task" // 创建证书申请任务 %d
ACMETask_LogDeleteACMETask langs.MessageCode = "acme_task@log_delete_acme_task" // 删除证书申请任务 %d
ACMETask_LogRunACMETask langs.MessageCode = "acme_task@log_run_acme_task" // 执行ACME任务 %d
ACMETask_LogUpdateACMETask langs.MessageCode = "acme_task@log_update_acme_task" // 修改证书申请任务 %d
ACMEUser_LogCreateACMEUser langs.MessageCode = "acme_user@log_create_acme_user" // 创建ACME用户 %d
ACMEUser_LogDeleteACMEUser langs.MessageCode = "acme_user@log_delete_acme_user" // 删除ACME用户 %d
ACMEUser_LogUpdateACMEUser langs.MessageCode = "acme_user@log_update_acme_user" // 修改ACME用户 %d
ADNetwork_LogCreateADNetwork langs.MessageCode = "ad_network@log_create_ad_network" // 创建高防IP线路 %d
ADNetwork_LogDeleteADNetwork langs.MessageCode = "ad_network@log_delete_ad_network" // 删除高防IP线路
ADNetwork_LogUpdateADNetwork langs.MessageCode = "ad_network@log_update_ad_network" // 修改高防IP线路 %d
ADPackage_LogCreateADPackage langs.MessageCode = "ad_package@log_create_ad_package" // 创建高防产品 %d
ADPackage_LogDeleteADPackage langs.MessageCode = "ad_package@log_delete_ad_package" // 删除高防产品 %d
ADPackage_LogUpdateADPackage langs.MessageCode = "ad_package@log_update_ad_package" // 修改高防产品 %d
ADPackageInstance_LogCreateADPackageInstance langs.MessageCode = "ad_package_instance@log_create_ad_package_instance" // 创建高防实例 %d
ADPackageInstance_LogDeleteADPackageInstance langs.MessageCode = "ad_package_instance@log_delete_ad_package_instance" // 删除高防实例 %d
ADPackageInstance_LogUpdateADPackageInstance langs.MessageCode = "ad_package_instance@log_update_ad_package_instance" // 修改高防实例 %d
ADPackagePeriod_LogCreateADPackagePeriod langs.MessageCode = "ad_package_period@log_create_ad_package_period" // 创建高防IP实例有效期 %d
ADPackagePeriod_LogDeleteADPackagePeriod langs.MessageCode = "ad_package_period@log_delete_ad_package_period" // 删除高防IP实例有效期选项 %d
ADPackagePeriod_LogUpdateADPackagePeriod langs.MessageCode = "ad_package_period@log_update_ad_package_period" // 修改高防IP实例有效期选项 %d
ADPackagePrice_LogCreateADPackagePrice langs.MessageCode = "ad_package_price@log_create_ad_package_price" // 为用户 %d 创建高防实例:%d有效期%d数量%d
ADPackagePrice_LogUpdateADPackagePrice langs.MessageCode = "ad_package_price@log_update_ad_package_price" // 修改高防产品 %d 有效期 %d 的价格
Admin_LogCreateAdmin langs.MessageCode = "admin@log_create_admin" // 创建系统用户 %d
Admin_LogDeleteAdmin langs.MessageCode = "admin@log_delete_admin" // 删除系统用户 %d
Admin_LogUpdateAdmin langs.MessageCode = "admin@log_update_admin" // 修改系统用户 %d
AdminCommon_Canceled langs.MessageCode = "admin_common@canceled" // 已取消
AdminCommon_LogSystemError langs.MessageCode = "admin_common@log_system_error" // 系统发生错误:%s
AdminCommon_MenuSettingBasic langs.MessageCode = "admin_common@menu_setting_basic" // 基础设置
AdminCommon_MenuSettingCache langs.MessageCode = "admin_common@menu_setting_cache" // 缓存设置
AdminCommon_MenuSettingCachePolicy langs.MessageCode = "admin_common@menu_setting_cache_policy" // 缓存策略
AdminCommon_MenuSettingDDoSProtection langs.MessageCode = "admin_common@menu_setting_ddos_protection" // DDoS防护
AdminCommon_MenuSettingDNS langs.MessageCode = "admin_common@menu_setting_dns" // DNS设置
AdminCommon_MenuSettingHealthCheck langs.MessageCode = "admin_common@menu_setting_health_check" // 健康检查
AdminCommon_MenuSettingMetrics langs.MessageCode = "admin_common@menu_setting_metrics" // 统计指标
AdminCommon_MenuSettingSecurityPolicy langs.MessageCode = "admin_common@menu_setting_security_policy" // 网络安全
AdminCommon_MenuSettingWAFPolicy langs.MessageCode = "admin_common@menu_setting_waf_policy" // WAF策略
AdminCommon_MenuSettingWebP langs.MessageCode = "admin_common@menu_setting_webp" // WebP
AdminCommon_ServerError langs.MessageCode = "admin_common@server_error" // 服务器出了点小问题,请联系技术人员处理。
AdminCommon_System langs.MessageCode = "admin_common@system" // 系统
AdminDashboard_DiskUsageWarning langs.MessageCode = "admin_dashboard@disk_usage_warning" // 当前服务器磁盘空间不足,请立即扩充容量,文件路径:%s已使用%dG已使用比例%.2f%%,仅剩余空间:%.2f%%。<br/>如果是因为本机数据库数据过多,你可以:<a href="/settings/database/clean">[清理访问日志]</a> &nbsp; &nbsp; <a href="https://goedge.cn/docs/APINode/QA.md" target="_blank">[调整数据库binlog设置]</a>
AdminDashboard_UIDNS langs.MessageCode = "admin_dashboard@ui_dns" // DNS
AdminDashboard_UIEvents langs.MessageCode = "admin_dashboard@ui_events" // 事件
AdminDashboard_UIOverview langs.MessageCode = "admin_dashboard@ui_overview" // 概况
AdminDashboard_UIUser langs.MessageCode = "admin_dashboard@ui_user" // 用户
AdminDashboard_UIWAF langs.MessageCode = "admin_dashboard@ui_waf" // WAF
AdminLogin_LogFailed langs.MessageCode = "admin_login@log_failed" // 登录失败,用户名:%s
AdminLogin_LogOtpVerifiedSuccess langs.MessageCode = "admin_login@log_otp_verified_success" // 成功通过OTP验证登录系统
AdminLogin_LogSuccess langs.MessageCode = "admin_login@log_success" // 成功登录系统,用户名:%s
AdminLogin_LogSystemError langs.MessageCode = "admin_login@log_system_error" // 登录时发生系统错误:%s
AdminLogin_LogUpdateLogin langs.MessageCode = "admin_login@log_update_login" // 修改登录设置
AdminMenu_AdminRecipients langs.MessageCode = "admin_menu@admin_recipients" // 通知媒介
AdminMenu_Admins langs.MessageCode = "admin_menu@admins" // 系统用户
AdminMenu_Dashboard langs.MessageCode = "admin_menu@dashboard" // 数据看板
AdminMenu_DNS langs.MessageCode = "admin_menu@dns" // 域名解析
AdminMenu_DNSClusters langs.MessageCode = "admin_menu@dns_clusters" // 集群列表
AdminMenu_DNSIssues langs.MessageCode = "admin_menu@dns_issues" // 问题修复
AdminMenu_DNSProviders langs.MessageCode = "admin_menu@dns_providers" // DNS服务商
AdminMenu_Finance langs.MessageCode = "admin_menu@finance" // 财务管理
AdminMenu_FinanceAccounts langs.MessageCode = "admin_menu@finance_accounts" // 用户账户
AdminMenu_FinanceBills langs.MessageCode = "admin_menu@finance_bills" // 费用账单
AdminMenu_FinanceFee langs.MessageCode = "admin_menu@finance_fee" // 计费设置
AdminMenu_FinanceIncome langs.MessageCode = "admin_menu@finance_income" // 统计报表
AdminMenu_FinanceLogs langs.MessageCode = "admin_menu@finance_logs" // 收支明细
AdminMenu_FinanceOrders langs.MessageCode = "admin_menu@finance_orders" // 订单管理
AdminMenu_FinancePackages langs.MessageCode = "admin_menu@finance_packages" // 流量包
AdminMenu_Logs langs.MessageCode = "admin_menu@logs" // 日志审计
AdminMenu_NodeAntiDDoSProducts langs.MessageCode = "admin_menu@node_anti_ddos_products" // 高防IP
AdminMenu_NodeClusters langs.MessageCode = "admin_menu@node_clusters" // 集群列表
AdminMenu_NodeDistributedMonitors langs.MessageCode = "admin_menu@node_distributed_monitors" // 区域监控
AdminMenu_NodeIPList langs.MessageCode = "admin_menu@node_ip_list" // 节点IP
AdminMenu_NodeLogs langs.MessageCode = "admin_menu@node_logs" // 节点日志
AdminMenu_NodeRegions langs.MessageCode = "admin_menu@node_regions" // 区域设置
AdminMenu_NodeSSHGrants langs.MessageCode = "admin_menu@node_ssh_grants" // 节点SSH
AdminMenu_Nodes langs.MessageCode = "admin_menu@nodes" // 边缘节点
AdminMenu_NS langs.MessageCode = "admin_menu@ns" // 智能DNS
AdminMenu_NSAccessLogs langs.MessageCode = "admin_menu@ns_access_logs" // 访问日志
AdminMenu_NSClusters langs.MessageCode = "admin_menu@ns_clusters" // 集群管理
AdminMenu_NSDomainBatchOperations langs.MessageCode = "admin_menu@ns_domain_batch_operations" // 批量操作
AdminMenu_NSDomainGroups langs.MessageCode = "admin_menu@ns_domain_groups" // 域名分组
AdminMenu_NSDomains langs.MessageCode = "admin_menu@ns_domains" // 域名管理
AdminMenu_NSNodeLogs langs.MessageCode = "admin_menu@ns_node_logs" // 运行日志
AdminMenu_NSPlans langs.MessageCode = "admin_menu@ns_plans" // 套餐设置
AdminMenu_NSResolveTest langs.MessageCode = "admin_menu@ns_resolve_test" // 解析测试
AdminMenu_NSRoutes langs.MessageCode = "admin_menu@ns_routes" // 线路管理
AdminMenu_NSSettings langs.MessageCode = "admin_menu@ns_settings" // 全局配置
AdminMenu_NSUserPlans langs.MessageCode = "admin_menu@ns_user_plans" // 用户套餐
AdminMenu_PlanUserPlans langs.MessageCode = "admin_menu@plan_user_plans" // 已购套餐
AdminMenu_Plans langs.MessageCode = "admin_menu@plans" // 套餐管理
AdminMenu_ServerAccessLogPolicies langs.MessageCode = "admin_menu@server_access_log_policies" // 日志策略
AdminMenu_ServerAccessLogs langs.MessageCode = "admin_menu@server_access_logs" // 访问日志
AdminMenu_ServerCachePolicies langs.MessageCode = "admin_menu@server_cache_policies" // 缓存策略
AdminMenu_ServerCerts langs.MessageCode = "admin_menu@server_certs" // 证书管理
AdminMenu_ServerGlobalSettings langs.MessageCode = "admin_menu@server_global_settings" // 通用设置
AdminMenu_ServerGroups langs.MessageCode = "admin_menu@server_groups" // 网站分组
AdminMenu_ServerIPLists langs.MessageCode = "admin_menu@server_ip_lists" // IP名单
AdminMenu_ServerMetrics langs.MessageCode = "admin_menu@server_metrics" // 统计指标
AdminMenu_ServerPurgeFetchCaches langs.MessageCode = "admin_menu@server_purge_fetch_caches" // 刷新预热
AdminMenu_ServerScripts langs.MessageCode = "admin_menu@server_scripts" // 脚本库
AdminMenu_ServerTrafficStats langs.MessageCode = "admin_menu@server_traffic_stats" // 用量统计
AdminMenu_ServerWAFPolicies langs.MessageCode = "admin_menu@server_waf_policies" // WAF策略
AdminMenu_Servers langs.MessageCode = "admin_menu@servers" // 网站列表
AdminMenu_SettingAdvancedSettings langs.MessageCode = "admin_menu@setting_advanced_settings" // 高级设置
AdminMenu_SettingAuthority langs.MessageCode = "admin_menu@setting_authority" // 商业版本
AdminMenu_SettingBasicSettings langs.MessageCode = "admin_menu@setting_basic_settings" // 基础设置
AdminMenu_Settings langs.MessageCode = "admin_menu@settings" // 系统设置
AdminMenu_TicketCategory langs.MessageCode = "admin_menu@ticket_category" // 分类
AdminMenu_Tickets langs.MessageCode = "admin_menu@tickets" // 工单系统
AdminMenu_UserList langs.MessageCode = "admin_menu@user_list" // 用户列表
AdminMenu_UserSettings langs.MessageCode = "admin_menu@user_settings" // 用户设置
AdminMenu_Users langs.MessageCode = "admin_menu@users" // 平台用户
AdminProfile_LogUpdateProfile langs.MessageCode = "admin_profile@log_update_profile" // 修改个人资料
AdminSecurity_LogUpdateSecuritySettings langs.MessageCode = "admin_security@log_update_security_settings" // 修改管理界面安全设置
AdminServer_LogUpdateServerHTTPSettings langs.MessageCode = "admin_server@log_update_server_http_settings" // 修改管理界面的HTTP设置
AdminServer_LogUpdateServerHTTPSSettings langs.MessageCode = "admin_server@log_update_server_https_settings" // 修改管理界面的HTTPS设置
AdminSetting_TabAccessLogDatabases langs.MessageCode = "admin_setting@tab_access_log_databases" // 日志数据库
AdminSetting_TabAdminSecuritySettings langs.MessageCode = "admin_setting@tab_admin_security_settings" // 安全设置
AdminSetting_TabAdminServer langs.MessageCode = "admin_setting@tab_admin_server" // Web服务
AdminSetting_TabAdminUI langs.MessageCode = "admin_setting@tab_admin_ui" // 管理界面设置
AdminSetting_TabAPINodes langs.MessageCode = "admin_setting@tab_api_nodes" // API节点
AdminSetting_TabAuthority langs.MessageCode = "admin_setting@tab_authority" // 商业版认证
AdminSetting_TabBackup langs.MessageCode = "admin_setting@tab_backup" // 备份
AdminSetting_TabClientBrowsers langs.MessageCode = "admin_setting@tab_client_browsers" // 浏览器库
AdminSetting_TabClientOperationSystems langs.MessageCode = "admin_setting@tab_client_operation_systems" // 操作系统库
AdminSetting_TabDatabase langs.MessageCode = "admin_setting@tab_database" // 数据库
AdminSetting_TabIPLibrary langs.MessageCode = "admin_setting@tab_ip_library" // IP库
AdminSetting_TabLogin langs.MessageCode = "admin_setting@tab_login" // 登录设置
AdminSetting_TabMonitorNodes langs.MessageCode = "admin_setting@tab_monitor_nodes" // 监控节点
AdminSetting_TabProfile langs.MessageCode = "admin_setting@tab_profile" // 个人资料
AdminSetting_TabTransfer langs.MessageCode = "admin_setting@tab_transfer" // 迁移
AdminSetting_TabUpdates langs.MessageCode = "admin_setting@tab_updates" // 检查更新
AdminSetting_TabUserNodes langs.MessageCode = "admin_setting@tab_user_nodes" // 用户节点
AdminSetting_TabUserUI langs.MessageCode = "admin_setting@tab_user_ui" // 用户界面设置
AdminUI_DefaultProductName langs.MessageCode = "admin_ui@default_product_name" // GoEdge
AdminUI_DefaultSystemName langs.MessageCode = "admin_ui@default_system_name" // GoEdge管理员系统
AdminUI_LogUpdateUISettings langs.MessageCode = "admin_ui@log_update_ui_settings" // 修改管理界面设置
AdminUpdate_LogIgnoreVersion langs.MessageCode = "admin_update@log_ignore_version" // 忽略升级版本 %s
AdminUpdate_LogResetIgnoreVersion langs.MessageCode = "admin_update@log_reset_ignore_version" // 重置忽略升级版本
AdminUpdate_LogUpdateCheckSettings langs.MessageCode = "admin_update@log_update_check_settings" // 修改检查更新设置
APINode_LogCreateAPINode langs.MessageCode = "api_node@log_create_api_node" // 创建API节点 %d
APINode_LogDeleteAPINode langs.MessageCode = "api_node@log_delete_api_node" // 删除API节点 %d
APINode_LogUpdateAPINode langs.MessageCode = "api_node@log_update_api_node" // 修改API节点 %d
ClientBrowser_LogCreateBrowser langs.MessageCode = "client_browser@log_create_browser" // 创建浏览器信息 %s
ClientBrowser_LogUpdateClientBrowser langs.MessageCode = "client_browser@log_update_client_browser" // 修改浏览器信息 %d
ClientSystem_LogCreateSystem langs.MessageCode = "client_system@log_create_system" // 创建操作系统信息 %s
ClientSystem_LogUpdateClientSystem langs.MessageCode = "client_system@log_update_client_system" // 修改操作系统信息 %d
Database_LogDeleteTable langs.MessageCode = "database@log_delete_table" // 删除数据表 %s
Database_LogTruncateTable langs.MessageCode = "database@log_truncate_table" // 清空数据表 %s 数据
Database_LogUpdateAPINodeDatabaseConfig langs.MessageCode = "database@log_update_api_node_database_config" // 修改API节点数据库设置
Database_LogUpdateCleanDays langs.MessageCode = "database@log_update_clean_days" // 修改数据库自动清理设置
DBNode_LogCreateDBNode langs.MessageCode = "db_node@log_create_db_node" // 创建数据库节点 %d
DBNode_LogDeleteDBNode langs.MessageCode = "db_node@log_delete_db_node" // 删除数据库节点 %d
DBNode_LogDeleteTable langs.MessageCode = "db_node@log_delete_table" // 删除数据库节点 %d 数据表 %s
DBNode_LogTruncateTable langs.MessageCode = "db_node@log_truncate_table" // 清空数据库节点 %d 数据表 %s 数据
DBNode_LogUpdateDBNode langs.MessageCode = "db_node@log_update_db_node" // 修改数据库节点 %d
DBNode_TabNodes langs.MessageCode = "db_node@tab_nodes" // 数据库节点
DDoSProtection_LogUpdateClusterDDoSProtection langs.MessageCode = "ddos_protection@log_update_cluster_ddos_protection" // 修改集群 %d 的DDOS防护设置
DDoSProtection_LogUpdateNodeDDoSProtection langs.MessageCode = "ddos_protection@log_update_node_ddos_protection" // 修改节点 %d 的DDOS防护设置
DNS_LogCreateDomain langs.MessageCode = "dns@log_create_domain" // 添加管理域名到DNS服务商 %d
DNS_LogDeleteDomain langs.MessageCode = "dns@log_delete_domain" // 从DNS服务商中删除域名 %d
DNS_LogRecoverDomain langs.MessageCode = "dns@log_recover_domain" // 从DNS服务商中恢复域名 %d
DNS_LogSyncCluster langs.MessageCode = "dns@log_sync_cluster" // 同步集群 %d 的DNS设置
DNS_LogSyncDomain langs.MessageCode = "dns@log_sync_domain" // 同步DNS域名数据 %d
DNS_LogUpdateClusterDNS langs.MessageCode = "dns@log_update_cluster_dns" // 修改集群 %d DNS设置
DNS_LogUpdateDomain langs.MessageCode = "dns@log_update_domain" // 修改DNS服务商域名 %d
DNS_LogUpdateNodeDNS langs.MessageCode = "dns@log_update_node_dns" // 修改节点 %d 的DNS设置
DNSProvider_LogCreateDNSProvider langs.MessageCode = "dns_provider@log_create_dns_provider" // 创建DNS服务商 %d
DNSProvider_LogDeleteDNSProvider langs.MessageCode = "dns_provider@log_delete_dns_provider" // 删除DNS服务商 %d
DNSProvider_LogUpdateDNSProvider langs.MessageCode = "dns_provider@log_update_dns_provider" // 修改DNS服务商 %d
DNSTask_LogDeleteAllDNSTasks langs.MessageCode = "dns_task@log_delete_all_dns_tasks" // 删除所有DNS同步任务
DNSTask_LogDeleteDNSTask langs.MessageCode = "dns_task@log_delete_dns_task" // 删除DNS同步任务 %d
Finance_LogBillGenerateManually langs.MessageCode = "finance@log_bill_generate_manually" // 手动生成上个月 %s 账单
Finance_LogUpdateUserOrderConfig langs.MessageCode = "finance@log_update_user_order_config" // 修改订单设置
FinanceFee_LogUpdateFeeSetting langs.MessageCode = "finance_fee@log_update_fee_setting" // 修改默认计费方式
HTTPAccessLogPolicy_LogCreateHTTPAccessLogPolicy langs.MessageCode = "http_access_log_policy@log_create_http_access_log_policy" // 创建访问日志策略 %d
HTTPAccessLogPolicy_LogDeleteHTTPAccessLogPolicy langs.MessageCode = "http_access_log_policy@log_delete_http_access_log_policy" // 删除访问日志策略 %d
HTTPAccessLogPolicy_LogTestHTTPAccessLogPolicy langs.MessageCode = "http_access_log_policy@log_test_http_access_log_policy" // 测试向访问日志策略 %d 写入数据
HTTPAccessLogPolicy_LogUpdateHTTPAccessLogPolicy langs.MessageCode = "http_access_log_policy@log_update_http_access_log_policy" // 修改访问日志策略 %d
HTTPAuthPolicy_LogCreateHTTPAuthPolicy langs.MessageCode = "http_auth_policy@log_create_http_auth_policy" // 创建HTTP鉴权 %d
HTTPAuthPolicy_LogUpdateHTTPAuthPolicy langs.MessageCode = "http_auth_policy@log_update_http_auth_policy" // 修改HTTP鉴权 %d
HTTPCacheTask_LogCreateHTTPCacheTaskFetch langs.MessageCode = "http_cache_task@log_create_http_cache_task_fetch" // 批量预热缓存Key
HTTPCacheTask_LogCreateHTTPCacheTaskPurge langs.MessageCode = "http_cache_task@log_create_http_cache_task_purge" // 批量刷新缓存Key
HTTPCacheTask_LogDeleteHTTPCacheTask langs.MessageCode = "http_cache_task@log_delete_http_cache_task" // 删除缓存任务 %d
HTTPCacheTask_LogResetHTTPCacheTask langs.MessageCode = "http_cache_task@log_reset_http_cache_task" // 重置缓存任务 %d 状态
HTTPFastcgi_LogCreateHTTPFastcgi langs.MessageCode = "http_fastcgi@log_create_http_fastcgi" // 创建Fastcgi %d
HTTPFastcgi_LogUpdateHTTPFastcgi langs.MessageCode = "http_fastcgi@log_update_http_fastcgi" // 修改Fastcgi %d
HTTPLocation_LogCreateHTTPLocation langs.MessageCode = "http_location@log_create_http_location" // 创建路由规则:%s
HTTPLocation_LogUpdateHTTPLocation langs.MessageCode = "http_location@log_update_http_location" // 修改路由规则 %d 设置
HTTPRewriteRule_LogCreateRewriteRule langs.MessageCode = "http_rewrite_rule@log_create_rewrite_rule" // 在Web %d 中创建重写规则 %d
HTTPRewriteRule_LogDeleteRewriteRule langs.MessageCode = "http_rewrite_rule@log_delete_rewrite_rule" // 从Web %d 中删除重写规则 %d
HTTPRewriteRule_LogSortRewriteRules langs.MessageCode = "http_rewrite_rule@log_sort_rewrite_rules" // 对Web %d 中的重写规则进行排序
HTTPRewriteRule_LogUpdateRewriteRule langs.MessageCode = "http_rewrite_rule@log_update_rewrite_rule" // 修改Web %d 中的重写规则 %d
IPItem_LogCreateIPItem langs.MessageCode = "ip_item@log_create_ip_item" // 在名单 %d 中创建IP %d
IPItem_LogDeleteIPItem langs.MessageCode = "ip_item@log_delete_ip_item" // 从IP名单 %d 中删除IP %d
IPItem_LogReadAllIPItems langs.MessageCode = "ip_item@log_read_all_ip_items" // 将所有IP名单置为已读
IPItem_LogUpdateIPItem langs.MessageCode = "ip_item@log_update_ip_item" // 修改IP名单中的IP %d
IPLibrary_LogFinishIPLibrary langs.MessageCode = "ip_library@log_finish_ip_library" // 完成IP库%d 制作
IPLibraryArtifact_LogCancelIPLibraryArtifact langs.MessageCode = "ip_library_artifact@log_cancel_ip_library_artifact" // 取消使用IP库 %d
IPLibraryArtifact_LogDeleteIPLibraryArtifact langs.MessageCode = "ip_library_artifact@log_delete_ip_library_artifact" // 删除IP库 %d
IPLibraryArtifact_LogUseIPLibraryArtifact langs.MessageCode = "ip_library_artifact@log_use_ip_library_artifact" // 使用IP库 %d
IPLibraryFile_LogDeleteIPLibraryFile langs.MessageCode = "ip_library_file@log_delete_ip_library_file" // 删除IP库文件 %d
IPLibraryFile_LogGenerateIPLibraryFile langs.MessageCode = "ip_library_file@log_generate_ip_library_file" // 重新生成IP库 %d 文件
IPLibraryFile_LogUploadIPLibraryFile langs.MessageCode = "ip_library_file@log_upload_ip_library_file" // 上传IP库 %d
IPList_LogBindIPListWAFPolicy langs.MessageCode = "ip_list@log_bind_ip_list_waf_policy" // 绑定IP名单 %d 到WAF策略 %d
IPList_LogCreateIPItemsBatch langs.MessageCode = "ip_list@log_create_ip_items_batch" // 在IP名单 %d 中批量添加IP
IPList_LogCreateIPList langs.MessageCode = "ip_list@log_create_ip_list" // 创建IP名单 %d
IPList_LogDeleteIPBatch langs.MessageCode = "ip_list@log_delete_ip_batch" // 批量删除IP名单中的IP%s
IPList_LogDeleteIPList langs.MessageCode = "ip_list@log_delete_ip_list" // 删除IP名单 %d
IPList_LogExportIPList langs.MessageCode = "ip_list@log_export_ip_list" // 导出IP名单 %d
IPList_LogImportIPList langs.MessageCode = "ip_list@log_import_ip_list" // 导入IP名单 %d
IPList_LogUnbindIPListWAFPolicy langs.MessageCode = "ip_list@log_unbind_ip_list_waf_policy" // 解除绑定IP名单 %d WAF策略 %d
IPList_LogUpdateIPList langs.MessageCode = "ip_list@log_update_ip_list" // 修改IP名单 %d
Level_Error langs.MessageCode = "level@error" // 错误
Level_Info langs.MessageCode = "level@info" // 信息
Level_Warn langs.MessageCode = "level@warn" // 警告
Log_LogCleanAllLogs langs.MessageCode = "log@log_clean_all_logs" // 清除全部日志
Log_LogCleanLogsDaysBefore langs.MessageCode = "log@log_clean_logs_days_before" // 清除 %d 以前的日志
Log_LogDeleteLog langs.MessageCode = "log@log_delete_log" // 删除单个操作日志 %d
Log_LogUpdateSettings langs.MessageCode = "log@log_update_settings" // 修改日志相关配置
Log_TagAccessLog langs.MessageCode = "log@tag_access_log" // 访问日志
Log_TagListener langs.MessageCode = "log@tag_listener" // 端口监听
Log_TagScript langs.MessageCode = "log@tag_script" // 脚本
Log_TagWAF langs.MessageCode = "log@tag_waf" // WAF
Message_LogReadAll langs.MessageCode = "message@log_read_all" // 将所有消息置为已读
Message_LogReadMessages langs.MessageCode = "message@log_read_messages" // 将一组消息置为已读
MessageMediaInstance_LogCreateMessageMediaInstance langs.MessageCode = "message_media_instance@log_create_message_media_instance" // 创建消息媒介 %d
MessageMediaInstance_LogDeleteMessageMediaInstance langs.MessageCode = "message_media_instance@log_delete_message_media_instance" // 删除消息媒介 %d
MessageMediaInstance_LogUpdateMessageMediaInstance langs.MessageCode = "message_media_instance@log_update_message_media_instance" // 修改消息媒介 %d
MessageReceiver_LogDeleteReceiver langs.MessageCode = "message_receiver@log_delete_receiver" // 删除接收人关联关系 %d
MessageReceiver_LogUpdateClusterMessageReceivers langs.MessageCode = "message_receiver@log_update_cluster_message_receivers" // 修改集群 %d 消息接收人
MessageRecipient_LogCreateMessageRecipient langs.MessageCode = "message_recipient@log_create_message_recipient" // 创建媒介接收人 %d
MessageRecipient_LogDeleteMessageRecipient langs.MessageCode = "message_recipient@log_delete_message_recipient" // 删除媒介接收人 %d
MessageRecipient_LogUpdateMessageRecipient langs.MessageCode = "message_recipient@log_update_message_recipient" // 修改媒介接收人 %d
MessageTask_LogCreateTestingMessageTask langs.MessageCode = "message_task@log_create_testing_message_task" // 创建媒介测试任务 %d
MessageTask_LogDeleteMessageTask langs.MessageCode = "message_task@log_delete_message_task" // 删除消息发送任务 %d
MessageTask_LogUpdateMessageTaskStatus langs.MessageCode = "message_task@log_update_message_task_status" // 修改消息任务 %d 状态为 %d
MetricChart_LogCreateMetricChart langs.MessageCode = "metric_chart@log_create_metric_chart" // 创建指标图表 %d
MetricChart_LogDeleteMetricChart langs.MessageCode = "metric_chart@log_delete_metric_chart" // 删除指标图表 %d
MetricChart_LogUpdateMetricChart langs.MessageCode = "metric_chart@log_update_metric_chart" // 修改指标图表 %d
MetricItem_LogAddMetricItemToCluster langs.MessageCode = "metric_item@log_add_metric_item_to_cluster" // 添加指标 %d 到集群 %d
MetricItem_LogCreateMetricItem langs.MessageCode = "metric_item@log_create_metric_item" // 创建统计指标 %d
MetricItem_LogDeleteMetricItem langs.MessageCode = "metric_item@log_delete_metric_item" // 删除统计指标
MetricItem_LogDeleteMetricItemFromCluster langs.MessageCode = "metric_item@log_delete_metric_item_from_cluster" // 从集群 %d 中移除指标 %d
MetricItem_LogUpdateMetricItem langs.MessageCode = "metric_item@log_update_metric_item" // 修改统计指标 %d
MonitorNode_LogCreateMonitorNode langs.MessageCode = "monitor_node@log_create_monitor_node" // 创建监控节点 %d
MonitorNode_LogDeleteMonitorNode langs.MessageCode = "monitor_node@log_delete_monitor_node" // 删除监控节点 %d
MonitorNode_LogUpdateMonitorNode langs.MessageCode = "monitor_node@log_update_monitor_node" // 修改监控节点 %d
Node_LogCreateNode langs.MessageCode = "node@log_create_node" // 创建节点 %d
Node_LogCreateNodeBatch langs.MessageCode = "node@log_create_node_batch" // 批量创建节点
Node_LogDeleteNodeFromCluster langs.MessageCode = "node@log_delete_node_from_cluster" // 从集群 %d 中删除节点 %d
Node_LogInstallNode langs.MessageCode = "node@log_install_node" // 安装节点 %d
Node_LogInstallNodeRemotely langs.MessageCode = "node@log_install_node_remotely" // 远程安装节点 %d
Node_LogStartNodeRemotely langs.MessageCode = "node@log_start_node_remotely" // 远程启动节点 %d
Node_LogStopNodeRemotely langs.MessageCode = "node@log_stop_node_remotely" // 远程停止节点 %d
Node_LogUpNode langs.MessageCode = "node@log_up_node" // 手动上线节点 %d
Node_LogUpdateNode langs.MessageCode = "node@log_update_node" // 修改节点 %d 基本信息
Node_LogUpdateNodeInstallationStatus langs.MessageCode = "node@log_update_node_installation_status" // 修改节点安装状态 %d
Node_LogUpgradeNodeRemotely langs.MessageCode = "node@log_upgrade_node_remotely" // 远程升级节点 %d
Node_UngroupedLabel langs.MessageCode = "node@ungrouped_label" // 未分组
NodeAction_LogCopyNodeActionsToCluster langs.MessageCode = "node_action@log_copy_node_actions_to_cluster" // 复制节点 %d 调度动作到集群
NodeAction_LogCopyNodeActionsToGroup langs.MessageCode = "node_action@log_copy_node_actions_to_group" // 复制节点 %d 调度动作到分组
NodeAction_LogCreateNodeAction langs.MessageCode = "node_action@log_create_node_action" // 创建动作 %d
NodeAction_LogDeleteNodeAction langs.MessageCode = "node_action@log_delete_node_action" // 删除节点动作 %d
NodeAction_LogSortNodeActions langs.MessageCode = "node_action@log_sort_node_actions" // 修改节点 %d 动作排序
NodeAction_LogUpdateNodeAction langs.MessageCode = "node_action@log_update_node_action" // 修改节点动作 %d
NodeCache_LogUpdateNodeCacheSettings langs.MessageCode = "node_cache@log_update_node_cache_settings" // 修改节点 %d 缓存设置
NodeCluster_LogCreateCluster langs.MessageCode = "node_cluster@log_create_cluster" // 创建节点集群:%d
NodeCluster_LogDeleteCluster langs.MessageCode = "node_cluster@log_delete_cluster" // 删除集群 %d
NodeCluster_LogPinCluster langs.MessageCode = "node_cluster@log_pin_cluster" // 置顶集群 %d
NodeCluster_LogRunClusterHealthCheck langs.MessageCode = "node_cluster@log_run_cluster_health_check" // 执行集群健康检查设置 %d
NodeCluster_LogUnpinCluster langs.MessageCode = "node_cluster@log_unpin_cluster" // 取消置顶集群 %d
NodeCluster_LogUpdateClusterBasicSettings langs.MessageCode = "node_cluster@log_update_cluster_basic_settings" // 修改集群基础设置 %d
NodeCluster_LogUpdateClusterHealthCheck langs.MessageCode = "node_cluster@log_update_cluster_health_check" // 修改集群健康检查设置 %d
NodeClusterMenu_SettingBasic langs.MessageCode = "node_cluster_menu@setting_basic" // 基础设置
NodeClusterMenu_SettingCachePolicy langs.MessageCode = "node_cluster_menu@setting_cache_policy" // 缓存策略
NodeClusterMenu_SettingCC langs.MessageCode = "node_cluster_menu@setting_cc" // CC防护
NodeClusterMenu_SettingDDoSProtection langs.MessageCode = "node_cluster_menu@setting_ddos_protection" // DDoS防护
NodeClusterMenu_SettingDNS langs.MessageCode = "node_cluster_menu@setting_dns" // DNS设置
NodeClusterMenu_SettingHealthCheck langs.MessageCode = "node_cluster_menu@setting_health_check" // 健康检查
NodeClusterMenu_SettingHTTP3 langs.MessageCode = "node_cluster_menu@setting_http3" // HTTP/3
NodeClusterMenu_SettingMetrics langs.MessageCode = "node_cluster_menu@setting_metrics" // 统计指标
NodeClusterMenu_SettingNotification langs.MessageCode = "node_cluster_menu@setting_notification" // 消息通知
NodeClusterMenu_SettingPages langs.MessageCode = "node_cluster_menu@setting_pages" // 自定义页面
NodeClusterMenu_SettingSchedule langs.MessageCode = "node_cluster_menu@setting_schedule" // 智能调度
NodeClusterMenu_SettingSecurityPolicy langs.MessageCode = "node_cluster_menu@setting_security_policy" // 网络安全
NodeClusterMenu_SettingServiceGlobal langs.MessageCode = "node_cluster_menu@setting_service_global" // 网站设置
NodeClusterMenu_SettingSystemService langs.MessageCode = "node_cluster_menu@setting_system_service" // 系统服务
NodeClusterMenu_SettingThresholds langs.MessageCode = "node_cluster_menu@setting_thresholds" // 阈值设置
NodeClusterMenu_SettingTOA langs.MessageCode = "node_cluster_menu@setting_toa" // TOA设置
NodeClusterMenu_SettingUAM langs.MessageCode = "node_cluster_menu@setting_uam" // 5秒盾
NodeClusterMenu_SettingWAFActions langs.MessageCode = "node_cluster_menu@setting_waf_actions" // WAF动作
NodeClusterMenu_SettingWAFPolicy langs.MessageCode = "node_cluster_menu@setting_waf_policy" // WAF策略
NodeClusterMenu_SettingWebP langs.MessageCode = "node_cluster_menu@setting_webp" // WebP
NodeClusterMenu_TabClusterDashboard langs.MessageCode = "node_cluster_menu@tab_cluster_dashboard" // 集群看板
NodeClusterMenu_TabClusterDelete langs.MessageCode = "node_cluster_menu@tab_cluster_delete" // 删除集群
NodeClusterMenu_TabClusterNodes langs.MessageCode = "node_cluster_menu@tab_cluster_nodes" // 节点列表
NodeClusterMenu_TabClusterSettings langs.MessageCode = "node_cluster_menu@tab_cluster_settings" // 集群设置
NodeDNS_LogUpdateNodeDNS langs.MessageCode = "node_dns@log_update_node_dns" // 修改节点 %d DNS设置
NodeGrant_LogCreateSSHGrant langs.MessageCode = "node_grant@log_create_ssh_grant" // 创建SSH认证 %d
NodeGrant_LogDeleteSSHGrant langs.MessageCode = "node_grant@log_delete_ssh_grant" // 删除SSH认证 %d
NodeGrant_LogUpdateSSHGrant langs.MessageCode = "node_grant@log_update_ssh_grant" // 修改SSH认证 %d
NodeGrant_MethodPrivateKey langs.MessageCode = "node_grant@method_private_key" // 私钥
NodeGrant_MethodUserPassword langs.MessageCode = "node_grant@method_user_password" // 用户名+密码
NodeGroup_LogCreateNodeGroup langs.MessageCode = "node_group@log_create_node_group" // 创建节点分组 %d
NodeGroup_LogDeleteNodeGroup langs.MessageCode = "node_group@log_delete_node_group" // 删除节点分组 %d
NodeGroup_LogSortNodeGroups langs.MessageCode = "node_group@log_sort_node_groups" // 修改节点分组排序
NodeGroup_LogUpdateNodeGroup langs.MessageCode = "node_group@log_update_node_group" // 修改节点分组 %d
NodeIPAddress_LogDeleteNodeIPAddress langs.MessageCode = "node_ip_address@log_delete_node_ip_address" // 删除IP地址 %d
NodeIPAddress_LogDownNodeIPAddress langs.MessageCode = "node_ip_address@log_down_node_ip_address" // 手动设置IP地址 %d 下线
NodeIPAddress_LogRestoreNodeIPAddress langs.MessageCode = "node_ip_address@log_restore_node_ip_address" // 取消IP地址 %d 的备用IP
NodeIPAddress_LogUpNodeIPAddress langs.MessageCode = "node_ip_address@log_up_node_ip_address" // 手动设置IP地址 %d 上线
NodeLog_LogDeleteNodeLogsBatch langs.MessageCode = "node_log@log_delete_node_logs_batch" // 批量删除节点运行日志
NodeLog_LogFixAllLogs langs.MessageCode = "node_log@log_fix_all_logs" // 设置所有日志为已修复
NodeLog_LogFixNodeLogs langs.MessageCode = "node_log@log_fix_node_logs" // 设置日志 %s 为已修复
NodeMenu_CreateMultipleNodes langs.MessageCode = "node_menu@create_multiple_nodes" // 批量创建
NodeMenu_CreateSingleNode langs.MessageCode = "node_menu@create_single_node" // 单个创建
NodeMenu_InstallAutoRegister langs.MessageCode = "node_menu@install_auto_register" // 自动注册
NodeMenu_InstallManually langs.MessageCode = "node_menu@install_manually" // 手动安装
NodeMenu_InstallRemote langs.MessageCode = "node_menu@install_remote" // 远程安装(%d)
NodeMenu_InstallRemoteUpgrade langs.MessageCode = "node_menu@install_remote_upgrade" // 远程升级(%d)
NodeMenu_SettingBasic langs.MessageCode = "node_menu@setting_basic" // 基础设置
NodeMenu_SettingCache langs.MessageCode = "node_menu@setting_cache" // 缓存设置
NodeMenu_SettingDDoSProtection langs.MessageCode = "node_menu@setting_ddos_protection" // DDoS防护
NodeMenu_SettingDNS langs.MessageCode = "node_menu@setting_dns" // DNS设置
NodeMenu_SettingSchedule langs.MessageCode = "node_menu@setting_schedule" // 智能调度
NodeMenu_SettingSSH langs.MessageCode = "node_menu@setting_ssh" // SSH设置
NodeMenu_SettingSystem langs.MessageCode = "node_menu@setting_system" // 系统设置
NodeMenu_SettingThresholds langs.MessageCode = "node_menu@setting_thresholds" // 阈值设置
NodePriceItem_LogCreateNodePriceItemBandwidth langs.MessageCode = "node_price_item@log_create_node_price_item_bandwidth" // 创建带宽价格项目 %d
NodePriceItem_LogCreateNodePriceItemTraffic langs.MessageCode = "node_price_item@log_create_node_price_item_traffic" // 创建流量价格项目 %d
NodePriceItem_LogDeleteNodePriceItem langs.MessageCode = "node_price_item@log_delete_node_price_item" // 删除流量价格项目 %d
NodePriceItem_LogUpdateNodePriceItemBandwidth langs.MessageCode = "node_price_item@log_update_node_price_item_bandwidth" // 修改带宽价格项目 %d
NodePriceItem_LogUpdateNodePriceItemTraffic langs.MessageCode = "node_price_item@log_update_node_price_item_traffic" // 修改流量价格项目 %d
NodeRegion_LogCreateNodeRegion langs.MessageCode = "node_region@log_create_node_region" // 创建节点区域 %d
NodeRegion_LogDeleteNodeRegion langs.MessageCode = "node_region@log_delete_node_region" // 删除节点区域 %d
NodeRegion_LogMoveNodeBetweenRegions langs.MessageCode = "node_region@log_move_node_between_regions" // 修改节点 %d 区域到 %d
NodeRegion_LogSortNodeRegions langs.MessageCode = "node_region@log_sort_node_regions" // 修改节点区域排序
NodeRegion_LogUpdateNodeRegion langs.MessageCode = "node_region@log_update_node_region" // 修改节点区域 %d
NodeRegionPrice_LogUpdateNodeRegionPrice langs.MessageCode = "node_region_price@log_update_node_region_price" // 修改区域 %d - 价格项 %d 的价格
NodeSchedule_LogResetNodeActionStatus langs.MessageCode = "node_schedule@log_reset_node_action_status" // 重置节点 %d 动作状态
NodeSchedule_LogUpdateNodeScheduleBasic langs.MessageCode = "node_schedule@log_update_node_schedule_basic" // 修改节点调度基本信息
NodeSSH_LogUpdateNodeSSH langs.MessageCode = "node_ssh@log_update_node_ssh" // 修改节点 %d SSH配置
NodeSystem_LogUpdateNodeSystemSettings langs.MessageCode = "node_system@log_update_node_system_settings" // 修改节点 %d 系统信息
NodeSystemd_LogUpdateClusterSystemdSettings langs.MessageCode = "node_systemd@log_update_cluster_systemd_settings" // 修改集群 %d 的系统服务设置
NodeTask_LogDeleteAllNodeTasks langs.MessageCode = "node_task@log_delete_all_node_tasks" // 删除所有节点同步任务
NodeTask_LogDeleteNodeTask langs.MessageCode = "node_task@log_delete_node_task" // 删除同步任务 %d
NodeTask_LogDeleteNodeTasksBatch langs.MessageCode = "node_task@log_delete_node_tasks_batch" // 批量删除节点同步任务
NodeThreshold_LogCreateNodeThreshold langs.MessageCode = "node_threshold@log_create_node_threshold" // 创建节点阈值 %d
NodeThreshold_LogDeleteNodeThreshold langs.MessageCode = "node_threshold@log_delete_node_threshold" // 删除阈值 %d
NodeThreshold_LogUpdateNodeThreshold langs.MessageCode = "node_threshold@log_update_node_threshold" // 修改节点阈值 %d
NodeTOA_LogUpdateClusterTOA langs.MessageCode = "node_toa@log_update_cluster_toa" // 修改集群 %d 的TOA设置
NS_LogCreateNSDomainsBatch langs.MessageCode = "ns@log_create_ns_domains_batch" // 批量添加域名
NS_LogCreateNSRecordsBatch langs.MessageCode = "ns@log_create_ns_records_batch" // 批量添加解析
NS_LogDeleteNSDomainsBatch langs.MessageCode = "ns@log_delete_ns_domains_batch" // 批量删除域名,用户 %d
NS_LogDeleteNSRecordsBatch langs.MessageCode = "ns@log_delete_ns_records_batch" // 批量删除域名记录
NS_LogDisableNSRecordsBatch langs.MessageCode = "ns@log_disable_ns_records_batch" // 批量停用域名记录
NS_LogEnableNSRecordsBatch langs.MessageCode = "ns@log_enable_ns_records_batch" // 批量启用域名记录
NS_LogImportRecordsBatch langs.MessageCode = "ns@log_import_records_batch" // 批量导入记录
NS_LogUpdateNSRecordsBatch langs.MessageCode = "ns@log_update_ns_records_batch" // 批量修改域名记录
NS_LogUpdateNSUserConfig langs.MessageCode = "ns@log_update_ns_user_config" // 修改NS全局设置--用户相关设置
NS_SettingAccessLogs langs.MessageCode = "ns@setting_access_logs" // 访问日志设置
NS_SettingUser langs.MessageCode = "ns@setting_user" // 用户设置
NSCluster_LogCreateNSCluster langs.MessageCode = "ns_cluster@log_create_ns_cluster" // 创建域名服务集群 %d
NSCluster_LogDeleteNSCluster langs.MessageCode = "ns_cluster@log_delete_ns_cluster" // 删除域名服务集群 %d
NSCluster_LogUpdateNSClusterSettingsAccessLog langs.MessageCode = "ns_cluster@log_update_ns_cluster_settings_access_log" // 修改域名服务集群 %d 访问日志配置
NSCluster_LogUpdateNSClusterSettingsAnswer langs.MessageCode = "ns_cluster@log_update_ns_cluster_settings_answer" // 修改NS集群 %d 应答模式设置
NSCluster_LogUpdateNSClusterSettingsBasic langs.MessageCode = "ns_cluster@log_update_ns_cluster_settings_basic" // 修改域名服务集群基本信息 %d
NSCluster_LogUpdateNSClusterSettingsDDoSProtection langs.MessageCode = "ns_cluster@log_update_ns_cluster_settings_ddos_protection" // 修改NS集群 %d 的DDOS防护设置
NSCluster_LogUpdateNSClusterSettingsDoH langs.MessageCode = "ns_cluster@log_update_ns_cluster_settings_doh" // 修改NS集群 %d DoH设置
NSCluster_LogUpdateNSClusterSettingsRecursion langs.MessageCode = "ns_cluster@log_update_ns_cluster_settings_recursion" // 修改DNS集群 %d 的递归DNS设置
NSCluster_LogUpdateNSClusterSettingsSOA langs.MessageCode = "ns_cluster@log_update_ns_cluster_settings_soa" // 修改NS集群 %d SOA配置
NSCluster_LogUpdateNSClusterSettingsTCP langs.MessageCode = "ns_cluster@log_update_ns_cluster_settings_tcp" // 修理NS集群 %d TCP设置
NSCluster_LogUpdateNSClusterSettingsTLS langs.MessageCode = "ns_cluster@log_update_ns_cluster_settings_tls" // 修改NS集群 %d TLS设置
NSCluster_LogUpdateNSClusterSettingsUDP langs.MessageCode = "ns_cluster@log_update_ns_cluster_settings_udp" // 修改NS集群 %d UDP设置
NSCluster_MenuAccessLogs langs.MessageCode = "ns_cluster@menu_access_logs" // 访问日志
NSCluster_MenuAnswerSetting langs.MessageCode = "ns_cluster@menu_answer_setting" // 应答模式
NSCluster_MenuBasic langs.MessageCode = "ns_cluster@menu_basic" // 基础设置
NSCluster_MenuDDoSProtection langs.MessageCode = "ns_cluster@menu_ddos_protection" // DDoS防护
NSCluster_MenuDNSRecursion langs.MessageCode = "ns_cluster@menu_dns_recursion" // 递归DNS
NSCluster_MenuDoH langs.MessageCode = "ns_cluster@menu_doh" // DoH
NSCluster_MenuSOA langs.MessageCode = "ns_cluster@menu_soa" // SOA
NSCluster_MenuTCP langs.MessageCode = "ns_cluster@menu_tcp" // TCP
NSCluster_MenuTLS langs.MessageCode = "ns_cluster@menu_tls" // TLS
NSCluster_MenuUDP langs.MessageCode = "ns_cluster@menu_udp" // UDP
NSCluster_TabDelete langs.MessageCode = "ns_cluster@tab_delete" // 删除集群
NSCluster_TabNodes langs.MessageCode = "ns_cluster@tab_nodes" // 节点列表
NSCluster_TabSetting langs.MessageCode = "ns_cluster@tab_setting" // 集群设置
NSDomain_LogCreateNSDomain langs.MessageCode = "ns_domain@log_create_ns_domain" // 创建域名 %d
NSDomain_LogCreateNSDomainsBatch langs.MessageCode = "ns_domain@log_create_ns_domains_batch" // 批量添加域名
NSDomain_LogDeleteNSDomain langs.MessageCode = "ns_domain@log_delete_ns_domain" // 删除域名 %d
NSDomain_LogUpdateNSDomain langs.MessageCode = "ns_domain@log_update_ns_domain" // 修改域名 %d
NSDomain_LogUpdateNSDomainHealthCheck langs.MessageCode = "ns_domain@log_update_ns_domain_health_check" // 修改域名 %d 健康检查设置
NSDomain_LogUpdateNSDomainStatus langs.MessageCode = "ns_domain@log_update_ns_domain_status" // 修改域名 %d 状态为 %s
NSDomain_LogUpdateNSDomainTSIG langs.MessageCode = "ns_domain@log_update_ns_domain_tsig" // 修改域名 %d 的TSIG配置
NSDomain_LogValidateNSDomains langs.MessageCode = "ns_domain@log_validate_ns_domains" // 批量验证域名
NSDomainGroup_LogCreateNSDomainGroup langs.MessageCode = "ns_domain_group@log_create_ns_domain_group" // 创建域名分组 %d
NSDomainGroup_LogDeleteNSDomainGroup langs.MessageCode = "ns_domain_group@log_delete_ns_domain_group" // 删除域名分组 %d
NSDomainGroup_LogUpdateNSDomainGroup langs.MessageCode = "ns_domain_group@log_update_ns_domain_group" // 修改域名分组 %d
NSKey_LogCreateNSKey langs.MessageCode = "ns_key@log_create_ns_key" // 创建DNS密钥 %d
NSKey_LogDeleteNSKey langs.MessageCode = "ns_key@log_delete_ns_key" // 删除DNS密钥 %d
NSKey_LogUpdateNSKey langs.MessageCode = "ns_key@log_update_ns_key" // 修改DNS密钥 %d
NSNode_LogCreateNSNode langs.MessageCode = "ns_node@log_create_ns_node" // 创建域名服务节点 %d
NSNode_LogDeleteNSNode langs.MessageCode = "ns_node@log_delete_ns_node" // 删除域名服务节点 %d
NSNode_LogInstallNSNodeRemotely langs.MessageCode = "ns_node@log_install_ns_node_remotely" // 安装节点 %d
NSNode_LogStartNSNodeRemotely langs.MessageCode = "ns_node@log_start_ns_node_remotely" // 远程启动节点 %d
NSNode_LogStopNSNodeRemotely langs.MessageCode = "ns_node@log_stop_ns_node_remotely" // 远程停止节点 %d
NSNode_LogUpdateNSNode langs.MessageCode = "ns_node@log_update_ns_node" // 修改节点 %d
NSNode_LogUpdateNSNodeInstallationStatus langs.MessageCode = "ns_node@log_update_ns_node_installation_status" // 修改节点安装状态 %d
NSNodeSSH_LogUpdateNSNodeSSH langs.MessageCode = "ns_node_ssh@log_update_ns_node_ssh" // 修改节点 %d SSH配置
NSPlan_LogCreateNSPlan langs.MessageCode = "ns_plan@log_create_ns_plan" // 创建套餐 %d
NSPlan_LogDeleteNSPlan langs.MessageCode = "ns_plan@log_delete_ns_plan" // 删除套餐 %d
NSPlan_LogSortNSPlans langs.MessageCode = "ns_plan@log_sort_ns_plans" // 套餐排序
NSPlan_LogUpdateNSPlan langs.MessageCode = "ns_plan@log_update_ns_plan" // 修改套餐 %d
NSRecord_LogCreateNSRecord langs.MessageCode = "ns_record@log_create_ns_record" // 创建域名记录 %d
NSRecord_LogCreateNSRecordsBatch langs.MessageCode = "ns_record@log_create_ns_records_batch" // 批量创建域名记录
NSRecord_LogDeleteNSRecord langs.MessageCode = "ns_record@log_delete_ns_record" // 删除域名记录 %d
NSRecord_LogUpNSRecord langs.MessageCode = "ns_record@log_up_ns_record" // 手动设置DNS记录 %d 为上线状态
NSRecord_LogUpdateNSRecord langs.MessageCode = "ns_record@log_update_ns_record" // 修改域名记录 %d
NSRecord_LogUpdateNSRecordHealthCheck langs.MessageCode = "ns_record@log_update_ns_record_health_check" // 修改记录 %d 的健康检查
NSRoute_LogCreateNSRoute langs.MessageCode = "ns_route@log_create_ns_route" // 创建域名服务线路 %d
NSRoute_LogDeleteNSRoute langs.MessageCode = "ns_route@log_delete_ns_route" // 删除域名服务线路 %d
NSRoute_LogSortNSRoutes langs.MessageCode = "ns_route@log_sort_ns_routes" // 对线路进行排序
NSRoute_LogUpdateNSRoute langs.MessageCode = "ns_route@log_update_ns_route" // 修改域名线路 %d
NSRouteCategory_LogCreateNSRouteCategory langs.MessageCode = "ns_route_category@log_create_ns_route_category" // 创建NS线路分类 %d
NSRouteCategory_LogDeleteNSRouteCategory langs.MessageCode = "ns_route_category@log_delete_ns_route_category" // 删除NS线路分类 %d
NSRouteCategory_LogSortNSRouteCategories langs.MessageCode = "ns_route_category@log_sort_ns_route_categories" // 对NS线路分类进行排序
NSRouteCategory_LogUpdateNSRouteCategory langs.MessageCode = "ns_route_category@log_update_ns_route_category" // 修改NS线路分类 %d
NSUserPlan_LogCreateNSUserPlan langs.MessageCode = "ns_user_plan@log_create_ns_user_plan" // 为用户 %d 创建DNS套餐 %d
NSUserPlan_LogDeleteNSUserPlan langs.MessageCode = "ns_user_plan@log_delete_ns_user_plan" // 删除用户套餐 %d
NSUserPlan_LogUpdateNSUserPlan langs.MessageCode = "ns_user_plan@log_update_ns_user_plan" // 修改用户DNS套餐 %d
OrderMethod_LogCreateOrderMethod langs.MessageCode = "order_method@log_create_order_method" // 创建支付方式 %d
OrderMethod_LogDeleteOrderMethod langs.MessageCode = "order_method@log_delete_order_method" // 删除支付方式 %d
OrderMethod_LogUpdateOrderMethod langs.MessageCode = "order_method@log_update_order_method" // 修改支付方式 %d
Plan_LogCreatePlan langs.MessageCode = "plan@log_create_plan" // 创建套餐 %d
Plan_LogDeletePlan langs.MessageCode = "plan@log_delete_plan" // 删除套餐 %d
Plan_LogSortPlans langs.MessageCode = "plan@log_sort_plans" // 对套餐进行排序
Plan_LogUpdatePlan langs.MessageCode = "plan@log_update_plan" // 修改套餐 %d
RegionCity_LogAddRegionCityCode langs.MessageCode = "region_city@log_add_region_city_code" // 添加城市/市 %d 别名 %s
RegionCity_LogUpdateRegionCityCustom langs.MessageCode = "region_city@log_update_region_city_custom" // 定制城市 %d 信息
RegionCountry_LogAddRegionCountryCode langs.MessageCode = "region_country@log_add_region_country_code" // 添加国家/地区 %d 别名 %s
RegionCountry_LogUpdateRegionCountryCustom langs.MessageCode = "region_country@log_update_region_country_custom" // 定制国家/地区 %d 信息
RegionCountry_RegionChina langs.MessageCode = "region_country@region_china" // 中国
RegionCountry_RegionChinaHk langs.MessageCode = "region_country@region_china_hk" // 中国香港
RegionCountry_RegionChinaMainland langs.MessageCode = "region_country@region_china_mainland" // 中国内地
RegionCountry_RegionChinaMo langs.MessageCode = "region_country@region_china_mo" // 中国澳门
RegionCountry_RegionChinaTw langs.MessageCode = "region_country@region_china_tw" // 中国台湾
RegionCountry_RegionGreaterChina langs.MessageCode = "region_country@region_greater_china" // 中国全境
RegionProvider_LogAddRegionProviderCode langs.MessageCode = "region_provider@log_add_region_provider_code" // 添加ISP服务商 %d 别名 %s
RegionProvider_LogUpdateRegionProviderCustom langs.MessageCode = "region_provider@log_update_region_provider_custom" // 定制ISP %d 信息
RegionProvince_LogAddRegionProvinceCode langs.MessageCode = "region_province@log_add_region_province_code" // 添加省份/州 %d 别名 %s
RegionProvince_LogUpdateRegionProvinceCustom langs.MessageCode = "region_province@log_update_region_province_custom" // 定制省份 %d 信息
RegionTown_LogAddRegionTownCode langs.MessageCode = "region_town@log_add_region_town_code" // 添加区/县 %d 别名 %s
RegionTown_LogUpdateRegionTownCustom langs.MessageCode = "region_town@log_update_region_town_custom" // 定制县级 %d 信息
ReportNode_LogCreateReportNode langs.MessageCode = "report_node@log_create_report_node" // 创建监控终端 %d
ReportNode_LogDeleteReportNode langs.MessageCode = "report_node@log_delete_report_node" // 删除监控终端 %d
ReportNode_LogUpdateReportNode langs.MessageCode = "report_node@log_update_report_node" // 修改监控终端 %d
ReportNodeGroup_LogCreateReportNodeGroup langs.MessageCode = "report_node_group@log_create_report_node_group" // 创建监控节点分组 %d
ReportNodeGroup_LogDeleteReportNodeGroup langs.MessageCode = "report_node_group@log_delete_report_node_group" // 删除监控节点分组 %d
ReportNodeGroup_LogUpdateReportNodeGroup langs.MessageCode = "report_node_group@log_update_report_node_group" // 修改监控节点分组 %d
ReverseProxy_LogUpdateReverseProxyScheduling langs.MessageCode = "reverse_proxy@log_update_reverse_proxy_scheduling" // 修改反向代理 %d 负载均衡算法
Script_LogCreateScript langs.MessageCode = "script@log_create_script" // 创建脚本 %d
Script_LogDeleteScript langs.MessageCode = "script@log_delete_script" // 删除脚本 %d
Script_LogPublishScripts langs.MessageCode = "script@log_publish_scripts" // 发布脚本库到边缘节点
Script_LogUpdateScript langs.MessageCode = "script@log_update_script" // 修改脚本 %d
Server_CopySettingCurrentCluster langs.MessageCode = "server@copy_setting_current_cluster" // 当前集群:%s
Server_CopySettingCurrentGroup langs.MessageCode = "server@copy_setting_current_group" // 当前分组:%s
Server_CopySettingCurrentUser langs.MessageCode = "server@copy_setting_current_user" // 当前用户:%s
Server_CopySettingSelectCluster langs.MessageCode = "server@copy_setting_select_cluster" // 选择集群
Server_CopySettingSelectGroup langs.MessageCode = "server@copy_setting_select_group" // 选择分组
Server_CopySettingSelectServer langs.MessageCode = "server@copy_setting_select_server" // 选择网站
Server_CopySettingSelectUser langs.MessageCode = "server@copy_setting_select_user" // 选择用户
Server_LogCopyServerConfigs langs.MessageCode = "server@log_copy_server_configs" // 从网站 %d 中同步配置 %s
Server_LogCreateServer langs.MessageCode = "server@log_create_server" // 创建网站 %d
Server_LogDeleteServer langs.MessageCode = "server@log_delete_server" // 删除网站 %d
Server_LogDisableServer langs.MessageCode = "server@log_disable_server" // 停用网站 %d
Server_LogEnableServer langs.MessageCode = "server@log_enable_server" // 启用网站 %d
Server_LogSubmitAuditingServer langs.MessageCode = "server@log_submit_auditing_server" // 提交网站 %d 域名审核
Server_LogUpdateGlobalSettings langs.MessageCode = "server@log_update_global_settings" // 保存网站全局配置
Server_LogUpdateServerBasic langs.MessageCode = "server@log_update_server_basic" // 修改网站 %d 基本信息
Server_LogUpdateServerGroups langs.MessageCode = "server@log_update_server_groups" // 修改网站 %d 所属分组
Server_LogUpdateServerIsOn langs.MessageCode = "server@log_update_server_is_on" // 修改网站 %d 启用状态
Server_LogUpdateServerName langs.MessageCode = "server@log_update_server_name" // 修改网站名称
Server_MenuAccesslogHistory langs.MessageCode = "server@menu_accesslog_history" // 历史
Server_MenuAccesslogRealtime langs.MessageCode = "server@menu_accesslog_realtime" // 实时
Server_MenuAccesslogToday langs.MessageCode = "server@menu_accesslog_today" // 今天
Server_MenuDashboard langs.MessageCode = "server@menu_dashboard" // 看板
Server_MenuSettingAccessLog langs.MessageCode = "server@menu_setting_access_log" // 访问日志
Server_MenuSettingAuth langs.MessageCode = "server@menu_setting_auth" // 访问鉴权
Server_MenuSettingBasic langs.MessageCode = "server@menu_setting_basic" // 基本信息
Server_MenuSettingCache langs.MessageCode = "server@menu_setting_cache" // 缓存
Server_MenuSettingCC langs.MessageCode = "server@menu_setting_cc" // CC防护
Server_MenuSettingCharset langs.MessageCode = "server@menu_setting_charset" // 字符编码
Server_MenuSettingClientIP langs.MessageCode = "server@menu_setting_client_ip" // 访客IP地址
Server_MenuSettingCompression langs.MessageCode = "server@menu_setting_compression" // 内容压缩
Server_MenuSettingDelete langs.MessageCode = "server@menu_setting_delete" // 删除
Server_MenuSettingDNS langs.MessageCode = "server@menu_setting_dns" // DNS
Server_MenuSettingDomains langs.MessageCode = "server@menu_setting_domains" // 域名
Server_MenuSettingFastcgi langs.MessageCode = "server@menu_setting_fastcgi" // Fastcgi
Server_MenuSettingGroup langs.MessageCode = "server@menu_setting_group" // 分组
Server_MenuSettingHTTP langs.MessageCode = "server@menu_setting_http" // HTTP
Server_MenuSettingHTTPHeaders langs.MessageCode = "server@menu_setting_http_headers" // HTTP报头
Server_MenuSettingHTTPProxy langs.MessageCode = "server@menu_setting_http_proxy" // HTTP代理
Server_MenuSettingHTTPS langs.MessageCode = "server@menu_setting_https" // HTTPS
Server_MenuSettingLocations langs.MessageCode = "server@menu_setting_locations" // 路由规则
Server_MenuSettingOptimization langs.MessageCode = "server@menu_setting_optimization" // 页面优化
Server_MenuSettingOrigins langs.MessageCode = "server@menu_setting_origins" // 源站
Server_MenuSettingOthers langs.MessageCode = "server@menu_setting_others" // 其他设置
Server_MenuSettingPages langs.MessageCode = "server@menu_setting_pages" // 自定义页面
Server_MenuSettingPlan langs.MessageCode = "server@menu_setting_plan" // 套餐
Server_MenuSettingRedirects langs.MessageCode = "server@menu_setting_redirects" // URL跳转
Server_MenuSettingReferers langs.MessageCode = "server@menu_setting_referers" // 防盗链
Server_MenuSettingRequestLimit langs.MessageCode = "server@menu_setting_request_limit" // 请求限制
Server_MenuSettingRewriteRules langs.MessageCode = "server@menu_setting_rewrite_rules" // 重写规则
Server_MenuSettingRoot langs.MessageCode = "server@menu_setting_root" // 静态分发
Server_MenuSettingScripts langs.MessageCode = "server@menu_setting_scripts" // 边缘脚本
Server_MenuSettingStat langs.MessageCode = "server@menu_setting_stat" // 统计
Server_MenuSettingTCP langs.MessageCode = "server@menu_setting_tcp" // TCP
Server_MenuSettingTCPProxy langs.MessageCode = "server@menu_setting_tcp_proxy" // TCP代理
Server_MenuSettingTLS langs.MessageCode = "server@menu_setting_tls" // TLS
Server_MenuSettingTrafficLimit langs.MessageCode = "server@menu_setting_traffic_limit" // 流量限制
Server_MenuSettingUAM langs.MessageCode = "server@menu_setting_uam" // 5秒盾
Server_MenuSettingUDP langs.MessageCode = "server@menu_setting_udp" // UDP
Server_MenuSettingUDPProxy langs.MessageCode = "server@menu_setting_udp_proxy" // UDP代理
Server_MenuSettingUnix langs.MessageCode = "server@menu_setting_unix" // Unix
Server_MenuSettingUserAgents langs.MessageCode = "server@menu_setting_user_agents" // UA名单
Server_MenuSettingWAF langs.MessageCode = "server@menu_setting_waf" // WAF
Server_MenuSettingWebP langs.MessageCode = "server@menu_setting_webp" // WebP
Server_MenuSettingWebsocket langs.MessageCode = "server@menu_setting_websocket" // Websocket
Server_MenuStatClients langs.MessageCode = "server@menu_stat_clients" // 终端
Server_MenuStatProviders langs.MessageCode = "server@menu_stat_providers" // 运营商
Server_MenuStatRegions langs.MessageCode = "server@menu_stat_regions" // 地域分布
Server_MenuStatTraffic langs.MessageCode = "server@menu_stat_traffic" // 流量统计
Server_MenuStatWAF langs.MessageCode = "server@menu_stat_waf" // WAF
Server_ServerNamesLogUpdateServerNames langs.MessageCode = "server@server_names_log_update_server_names" // 修改网站 %d 域名
Server_TabAccessLogs langs.MessageCode = "server@tab_access_logs" // 日志
Server_TabDashboard langs.MessageCode = "server@tab_dashboard" // 看板
Server_TabDelete langs.MessageCode = "server@tab_delete" // 删除
Server_TabServerList langs.MessageCode = "server@tab_server_list" // 网站列表
Server_TabSettings langs.MessageCode = "server@tab_settings" // 设置
Server_TabStat langs.MessageCode = "server@tab_stat" // 统计
ServerAccessLog_LogUpdateAccessLogSetting langs.MessageCode = "server_access_log@log_update_access_log_setting" // 修改Web %d 的访问日志设置
ServerAuth_LogUpdateHTTPAuthSettings langs.MessageCode = "server_auth@log_update_http_auth_settings" // 修改Web %d 的鉴权设置
ServerCache_LogFetchCaches langs.MessageCode = "server_cache@log_fetch_caches" // 预热网站 %d 缓存
ServerCache_LogPurgeCaches langs.MessageCode = "server_cache@log_purge_caches" // 删除网站 %d 缓存
ServerCache_LogUpdateCacheSettings langs.MessageCode = "server_cache@log_update_cache_settings" // 修改Web %d 的缓存设置
ServerCache_LogUpdateClusterCachePolicy langs.MessageCode = "server_cache@log_update_cluster_cache_policy" // 设置集群 %d 的缓存策略为 %d
ServerCachePolicy_LogCleanAll langs.MessageCode = "server_cache_policy@log_clean_all" // 清除缓存,缓存策略:%d
ServerCachePolicy_LogCreateCachePolicy langs.MessageCode = "server_cache_policy@log_create_cache_policy" // 创建缓存策略:%d
ServerCachePolicy_LogDeleteCachePolicy langs.MessageCode = "server_cache_policy@log_delete_cache_policy" // 删除缓存策略:%d
ServerCachePolicy_LogFetchCaches langs.MessageCode = "server_cache_policy@log_fetch_caches" // 预热缓存,缓存策略:%d
ServerCachePolicy_LogPurgeCaches langs.MessageCode = "server_cache_policy@log_purge_caches" // 删除缓存,缓存策略:%d
ServerCachePolicy_LogStatCaches langs.MessageCode = "server_cache_policy@log_stat_caches" // 统计缓存,缓存策略:%d
ServerCachePolicy_LogTestReading langs.MessageCode = "server_cache_policy@log_test_reading" // 测试读取,缓存策略:%d
ServerCachePolicy_LogTestWriting langs.MessageCode = "server_cache_policy@log_test_writing" // 测试写入,缓存策略:%d
ServerCachePolicy_LogUpdateCachePolicy langs.MessageCode = "server_cache_policy@log_update_cache_policy" // 修改缓存策略:%d
ServerCC_LogUpdateCCSettings langs.MessageCode = "server_cc@log_update_cc_settings" // 修改Web %d CC防护配置
ServerCC_LogUpdateClusterHTTPCCPolicy langs.MessageCode = "server_cc@log_update_cluster_http_cc_policy" // 修改集群 %d 的HTTP CC策略设置
ServerCharset_LogUpdateCharsetSetting langs.MessageCode = "server_charset@log_update_charset_setting" // 修改Web %d 的字符集设置
ServerCommon_LogUpdateCommonSettings langs.MessageCode = "server_common@log_update_common_settings" // 修改网站Web %d 设置的其他设置
ServerCompression_LogUpdateCompressionSettings langs.MessageCode = "server_compression@log_update_compression_settings" // 修改Web %d 的压缩设置
ServerDNS_LogRegenerateDNSName langs.MessageCode = "server_dns@log_regenerate_dns_name" // 重新生成网站 %d 的CNAME
ServerDNS_LogUpdateDNSName langs.MessageCode = "server_dns@log_update_dns_name" // 修改网站 %d CNAME为 %s
ServerDNS_LogUpdateDNSSettings langs.MessageCode = "server_dns@log_update_dns_settings" // 修改网站 %d 的DNS设置
ServerFastcgi_LogUpdateHTTPFastcgi langs.MessageCode = "server_fastcgi@log_update_http_fastcgi" // 修改Web %d 的Fastcgi设置
ServerGlobalSetting_LogUpdateClusterGlobalServerConfig langs.MessageCode = "server_global_setting@log_update_cluster_global_server_config" // 修改集群 %d 全局配置
ServerGroup_LogCreateServerGroup langs.MessageCode = "server_group@log_create_server_group" // 创建网站分组 %d
ServerGroup_LogDeleteServerGroup langs.MessageCode = "server_group@log_delete_server_group" // 删除网站分组 %d
ServerGroup_LogSortServerGroups langs.MessageCode = "server_group@log_sort_server_groups" // 修改网站分组排序
ServerGroup_LogUpdateServerGroup langs.MessageCode = "server_group@log_update_server_group" // 修改网站分组 %d
ServerHTTP3_LogUpdateClusterHTTP3Policy langs.MessageCode = "server_http3@log_update_cluster_http3_policy" // 修改集群 %d 的HTTP3策略设置
ServerHTTP_LogUpdateHTTPSettings langs.MessageCode = "server_http@log_update_http_settings" // 修改网站 %d 的HTTP设置
ServerHTTPHeader_LogCreateDeletingHeader langs.MessageCode = "server_http_header@log_create_deleting_header" // 添加删除的报头 HeaderPolicyId: %d, Name: %s
ServerHTTPHeader_LogCreateNonStandardHeader langs.MessageCode = "server_http_header@log_create_non_standard_header" // 添加非标的报头 HeaderPolicyId: %d, Name: %s
ServerHTTPHeader_LogCreateSettingRequestHeader langs.MessageCode = "server_http_header@log_create_setting_request_header" // 添加自定义请求报头HeaderPolicyId:%d, Name:%s, Value:%s
ServerHTTPHeader_LogCreateSettingResponseHeader langs.MessageCode = "server_http_header@log_create_setting_response_header" // 添加自定义响应报头HeaderPolicyId:%d, Name:%s, Value:%s
ServerHTTPHeader_LogDeleteDeletingHeader langs.MessageCode = "server_http_header@log_delete_deleting_header" // 删除需要删除的报头HeaderPolicyId:%d, HeaderName:%s
ServerHTTPHeader_LogDeleteHeader langs.MessageCode = "server_http_header@log_delete_header" // 删除报头HeaderPolicyId:%d, HeaderId:%d
ServerHTTPHeader_LogDeleteNonStandardHeader langs.MessageCode = "server_http_header@log_delete_non_standard_header" // 删除需要非标的报头HeaderPolicyId:%d, HeaderName:%s
ServerHTTPHeader_LogUpdateHTTPHeaders langs.MessageCode = "server_http_header@log_update_http_headers" // 修改Web %d 的报头设置
ServerHTTPHeader_LogUpdateSettingHeader langs.MessageCode = "server_http_header@log_update_setting_header" // 修改设置报头HeaderId:%d, Name:%s, Value:%s
ServerHTTPS_LogUpdateHTTPSSettings langs.MessageCode = "server_https@log_update_https_settings" // 修改网站 %d 的HTTPS设置
ServerOptimization_LogUpdateOptimizationSettings langs.MessageCode = "server_optimization@log_update_optimization_settings" // 修改Web %d 的页面优化设置
ServerOrigin_LogCreateOrigin langs.MessageCode = "server_origin@log_create_origin" // 创建源站 %d
ServerOrigin_LogDeleteOrigin langs.MessageCode = "server_origin@log_delete_origin" // 删除源站 %d
ServerOrigin_LogUpdateOrigin langs.MessageCode = "server_origin@log_update_origin" // 修改源站 %d
ServerPage_LogCreatePage langs.MessageCode = "server_page@log_create_page" // 创建自定义页面 %d
ServerPage_LogUpdateClusterPages langs.MessageCode = "server_page@log_update_cluster_pages" // 修改集群 %d 自定义页面策略
ServerPage_LogUpdatePage langs.MessageCode = "server_page@log_update_page" // 修改自定义页面 %d
ServerPage_LogUpdatePages langs.MessageCode = "server_page@log_update_pages" // 修改Web %d 的自定义页面设置
ServerRedirect_LogUpdateRedirects langs.MessageCode = "server_redirect@log_update_redirects" // 修改Web %d 的跳转设置
ServerReferer_LogUpdateReferers langs.MessageCode = "server_referer@log_update_referers" // 修改Web %d 防盗链设置
ServerRequestLimit_LogUpdateRequestLimitSettings langs.MessageCode = "server_request_limit@log_update_request_limit_settings" // 修改Web %d 请求限制
ServerReverseProxy_LogUpdateLocationReverseProxySettings langs.MessageCode = "server_reverse_proxy@log_update_location_reverse_proxy_settings" // 修改路由规则 %d 的反向代理设置
ServerReverseProxy_LogUpdateServerGroupReverseProxySettings langs.MessageCode = "server_reverse_proxy@log_update_server_group_reverse_proxy_settings" // 修改分组 %d 的反向代理设置
ServerReverseProxy_LogUpdateServerReverseProxySettings langs.MessageCode = "server_reverse_proxy@log_update_server_reverse_proxy_settings" // 修改网站 %d 的反向代理设置
ServerRoot_LogUpdateRoot langs.MessageCode = "server_root@log_update_root" // 修改Web %d 静态分发设置
ServerScript_LogUpdateScripts langs.MessageCode = "server_script@log_update_scripts" // 修改Web %d 边缘脚本
ServerStat_LogUpdateStatSettings langs.MessageCode = "server_stat@log_update_stat_settings" // 修改Web %d 的统计设置
ServerTCP_LogUpdateTCPSettings langs.MessageCode = "server_tcp@log_update_tcp_settings" // 修改网站 %d TCP设置
ServerTLS_LogUpdateTLSSettings langs.MessageCode = "server_tls@log_update_tls_settings" // 修改网站 %d TLS设置
ServerTrafficLimit_LogUpdateTrafficLimitSettings langs.MessageCode = "server_traffic_limit@log_update_traffic_limit_settings" // 修改网站 %d 流量限制
ServerTrafficStat_AllServers langs.MessageCode = "server_traffic_stat@all_servers" // 全部网站(%d
ServerUAM_LogUpdateClusterUAMPolicy langs.MessageCode = "server_uam@log_update_cluster_uam_policy" // 修改集群 %d 的UAM设置
ServerUAM_LogUpdateServerUAMSettings langs.MessageCode = "server_uam@log_update_server_uam_settings" // 修改网站 %d 全站防护模式
ServerUAM_LogUpdateUAMSettings langs.MessageCode = "server_uam@log_update_uam_settings" // 修改Web %d 全站防护模式
ServerUDP_LogUpdateUDPSettings langs.MessageCode = "server_udp@log_update_udp_settings" // 修改网站 %d UDP设置
ServerUserAgent_LogUpdateUserAgents langs.MessageCode = "server_user_agent@log_update_user_agents" // 修改Web %d User-Agent设置
ServerWAF_LogUpdateWAFSettings langs.MessageCode = "server_waf@log_update_waf_settings" // 修改Web %d 的WAF设置
ServerWebP_LogUpdateClusterWebPPolicy langs.MessageCode = "server_webp@log_update_cluster_webp_policy" // 修改集群 %d 的WebP设置
ServerWebsocket_LogUpdateWebsocketSettings langs.MessageCode = "server_websocket@log_update_websocket_settings" // 修改Web %d 的Websocket设置
SSLCert_LogDeleteSSLCert langs.MessageCode = "ssl_cert@log_delete_ssl_cert" // 删除SSL证书 %d
SSLCert_LogDownloadSSLCert langs.MessageCode = "ssl_cert@log_download_ssl_cert" // 下载SSL证书 %d
SSLCert_LogDownloadSSLCertKey langs.MessageCode = "ssl_cert@log_download_ssl_cert_key" // 下载SSL密钥 %d
SSLCert_LogDownloadSSLCertZip langs.MessageCode = "ssl_cert@log_download_ssl_cert_zip" // 下载SSL证书压缩包 %d
SSLCert_LogOCSPIgnoreOCSPStatus langs.MessageCode = "ssl_cert@log_ocsp_ignore_ocsp_status" // 忽略一组证书的OCSP状态
SSLCert_LogOCSPResetAllOCSPStatus langs.MessageCode = "ssl_cert@log_ocsp_reset_all_ocsp_status" // 忽略所有证书的OCSP状态
SSLCert_LogOCSPResetOCSPStatus langs.MessageCode = "ssl_cert@log_ocsp_reset_ocsp_status" // 重置一组证书的OCSP状态
SSLCert_LogUpdateSSLCert langs.MessageCode = "ssl_cert@log_update_ssl_cert" // 修改SSL证书 %d
SSLCert_LogUploadSSLCert langs.MessageCode = "ssl_cert@log_upload_ssl_cert" // 上传SSL证书 %d
SSLCert_LogUploadSSLCertBatch langs.MessageCode = "ssl_cert@log_upload_ssl_cert_batch" // 批量上传证书
SSLCert_MenuApply langs.MessageCode = "ssl_cert@menu_apply" // 申请证书
SSLCert_MenuCerts langs.MessageCode = "ssl_cert@menu_certs" // 证书
SSLCert_MenuOCSP langs.MessageCode = "ssl_cert@menu_ocsp" // OCSP日志
System_HomePage langs.MessageCode = "system@home_page" // https://goedge.cn
TicketCategory_LogCreateTicketCategory langs.MessageCode = "ticket_category@log_create_ticket_category" // 添加工单分类 %d
TicketCategory_LogDeleteTicketCategory langs.MessageCode = "ticket_category@log_delete_ticket_category" // 删除工单分类 %d
TicketCategory_LogUpdateTicketCategory langs.MessageCode = "ticket_category@log_update_ticket_category" // 修改分类 %d
TrafficPackage_LogCreateTrafficPackage langs.MessageCode = "traffic_package@log_create_traffic_package" // 创建流量包 %d
TrafficPackage_LogDeleteTrafficPackage langs.MessageCode = "traffic_package@log_delete_traffic_package" // 删除流量包 %d
TrafficPackage_LogUpdateTrafficPackage langs.MessageCode = "traffic_package@log_update_traffic_package" // 修改流量包 %d
TrafficPackagePeriod_LogCreateTrafficPackagePeriod langs.MessageCode = "traffic_package_period@log_create_traffic_package_period" // 创建流量包有效期 %d
TrafficPackagePeriod_LogDeleteTrafficPackagePeriod langs.MessageCode = "traffic_package_period@log_delete_traffic_package_period" // 删除流量包有效期选项 %d
TrafficPackagePeriod_LogUpdateTrafficPackagePeriod langs.MessageCode = "traffic_package_period@log_update_traffic_package_period" // 修改流量包有效期选项 %d
TrafficPackagePrice_LogUpdateTrafficPackagePrice langs.MessageCode = "traffic_package_price@log_update_traffic_package_price" // 修改流量包 %d 区域 %d x 有效期 %d 的价格
User_LogCreateUser langs.MessageCode = "user@log_create_user" // 创建用户 %d
User_LogDeleteUser langs.MessageCode = "user@log_delete_user" // 删除用户 %d
User_LogUpdateUser langs.MessageCode = "user@log_update_user" // 修改用户 %d
User_LogUpdateUserEmailSettings langs.MessageCode = "user@log_update_user_email_settings" // 修改用户邮件设置
User_LogUpdateUserFeatures langs.MessageCode = "user@log_update_user_features" // 设置用户 %d 的功能列表
User_LogUpdateUserGlobalSettings langs.MessageCode = "user@log_update_user_global_settings" // 修改用户设置
User_LogUpdateUserPricePeriod langs.MessageCode = "user@log_update_user_price_period" // 修改计费周期为 %s
User_LogUpdateUserPriceType langs.MessageCode = "user@log_update_user_price_type" // 修改计费类型为 %s
User_LogUpdateUserProfile langs.MessageCode = "user@log_update_user_profile" // 修改个人资料
User_LogUpdateUserSmsSettings langs.MessageCode = "user@log_update_user_sms_settings" // 修改用户短信设置
User_LogVerifyUser langs.MessageCode = "user@log_verify_user" // 审核用户:%d 结果:%s
UserAccessKey_LogCreateUserAccessKey langs.MessageCode = "user_access_key@log_create_user_access_key" // 创建AccessKey %d
UserAccessKey_LogDeleteUserAccessKey langs.MessageCode = "user_access_key@log_delete_user_access_key" // 删除AccessKey %d
UserAccessKey_LogUpdateUserAccessKeyIsOn langs.MessageCode = "user_access_key@log_update_user_access_key_is_on" // 设置AccessKey %d 启用状态
UserAccount_LogUpdateUserAccount langs.MessageCode = "user_account@log_update_user_account" // 操作用户账户 %d: %s
UserADInstance_LogDeleteUserADInstance langs.MessageCode = "user_ad_instance@log_delete_user_ad_instance" // 删除用户高防实例 %d
UserADInstance_LogRenewUserADInstance langs.MessageCode = "user_ad_instance@log_renew_user_ad_instance" // 为用户高防实例 %d 续期
UserADInstance_LogUpdateUserADInstanceObjects langs.MessageCode = "user_ad_instance@log_update_user_ad_instance_objects" // 修改用户高防实例 %d 防护对象
UserBill_LogPayUserBill langs.MessageCode = "user_bill@log_pay_user_bill" // 支付账单 %d
UserCommon_Canceled langs.MessageCode = "user_common@canceled" // 已取消
UserCommon_LogSystemError langs.MessageCode = "user_common@log_system_error" // 系统发生错误:%s
UserCommon_ServerError langs.MessageCode = "user_common@server_error" // 服务器出了点小问题,请联系技术人员处理。
UserCommon_System langs.MessageCode = "user_common@system" // 系统
UserIdentity_LogCancelUserIdentity langs.MessageCode = "user_identity@log_cancel_user_identity" // 取消身份认证审核
UserIdentity_LogRejectUserIdentity langs.MessageCode = "user_identity@log_reject_user_identity" // 驳回用户 %d 的实名认证
UserIdentity_LogResetUserIdentity langs.MessageCode = "user_identity@log_reset_user_identity" // 重置用户 %d 的实名认证
UserIdentity_LogSubmitUserIdentity langs.MessageCode = "user_identity@log_submit_user_identity" // 提交身份认证审核
UserIdentity_LogUpdateUserIdentityEnterprise langs.MessageCode = "user_identity@log_update_user_identity_enterprise" // 修改/上传企业实名认证信息
UserIdentity_LogUpdateUserIdentityIndividual langs.MessageCode = "user_identity@log_update_user_identity_individual" // 修改/上传个人实名认证信息
UserIdentity_LogVerifyUserIdentity langs.MessageCode = "user_identity@log_verify_user_identity" // 通过用户 %d 的实名认证
UserLogin_LogUpdateLogin langs.MessageCode = "user_login@log_update_login" // 修改登录设置
UserNode_LogCreateUserNode langs.MessageCode = "user_node@log_create_user_node" // 创建用户节点 %d
UserNode_LogDeleteUserNode langs.MessageCode = "user_node@log_delete_user_node" // 删除用户节点 %d
UserNode_LogUpdateUserNode langs.MessageCode = "user_node@log_update_user_node" // 修改用户节点 %d
UserOrder_LogFinishUserOrder langs.MessageCode = "user_order@log_finish_user_order" // 设置订单 %s 为完成支付
UserPlan_LogBindUserPlanToServer langs.MessageCode = "user_plan@log_bind_user_plan_to_server" // 修改网站 %d 绑定的套餐为 %d
UserPlan_LogBuyUserPlan langs.MessageCode = "user_plan@log_buy_user_plan" // 为用户 %d 购买套餐 %d
UserPlan_LogCancelUserPlanFromServer langs.MessageCode = "user_plan@log_cancel_user_plan_from_server" // 取消网站 %d 绑定的套餐
UserPlan_LogDeleteUserPlan langs.MessageCode = "user_plan@log_delete_user_plan" // 删除用户已购套餐 %d
UserPlan_LogRenewUserPlan langs.MessageCode = "user_plan@log_renew_user_plan" // 续费已购套餐 %d
UserTicket_LogCreateUserTicket langs.MessageCode = "user_ticket@log_create_user_ticket" // 创建工单 %d
UserTicketLog_LogReplyTicket langs.MessageCode = "user_ticket_log@log_reply_ticket" // 回复工单 %d
UserTrafficPackage_LogCreateUserTrafficPackage langs.MessageCode = "user_traffic_package@log_create_user_traffic_package" // 为用户 %d 创建流量包:%d区域%d有效期%d数量%d
UserTrafficPackage_LogDeleteUserTrafficPackage langs.MessageCode = "user_traffic_package@log_delete_user_traffic_package" // 删除用户流量包 %d
WAF_ConnectorAnd langs.MessageCode = "waf@connector_and" // 和(AND)
WAF_ConnectorAndDescription langs.MessageCode = "waf@connector_and_description" // 所有规则都满足才视为匹配
WAF_ConnectorOr langs.MessageCode = "waf@connector_or" // 或(OR)
WAF_ConnectorOrDescription langs.MessageCode = "waf@connector_or_description" // 任一规则满足了就视为匹配
WAF_LogDeleteIPFromWAFPolicy langs.MessageCode = "waf@log_delete_ip_from_waf_policy" // 从WAF策略 %d 名单中删除IP %d
WAF_LogUpdateForbiddenCountries langs.MessageCode = "waf@log_update_forbidden_countries" // WAF策略 %d 设置禁止访问的国家和地区
WAF_LogUpdateForbiddenProvinces langs.MessageCode = "waf@log_update_forbidden_provinces" // WAF策略 %d 设置禁止访问的省份
WAF_LogUpdateIPFromWAFPolicy langs.MessageCode = "waf@log_update_ip_from_waf_policy" // 修改WAF策略 %d 名单中的IP %d
WAFAction_LogCreateWAFAction langs.MessageCode = "waf_action@log_create_waf_action" // 创建集群 %d 的WAF动作
WAFAction_LogDeleteWAFAction langs.MessageCode = "waf_action@log_delete_waf_action" // 删除WAF动作 %d
WAFAction_LogUpdateWAFAction langs.MessageCode = "waf_action@log_update_waf_action" // 修改WAF动作 %d
WAFPolicy_LogCreateWAFPolicy langs.MessageCode = "waf_policy@log_create_waf_policy" // 创建WAF策略 %d
WAFPolicy_LogDeleteWAFPolicy langs.MessageCode = "waf_policy@log_delete_waf_policy" // 删除WAF策略 %d
WAFPolicy_LogExportWAFPolicy langs.MessageCode = "waf_policy@log_export_waf_policy" // 导出WAF策略 %d
WAFPolicy_LogImportWAFPolicy langs.MessageCode = "waf_policy@log_import_waf_policy" // 从文件中导入规则到WAF策略 %d
WAFPolicy_LogUpdateClusterWAFPolicy langs.MessageCode = "waf_policy@log_update_cluster_waf_policy" // 设置集群 %d 的WAF策略为 %d
WAFPolicy_LogUpdateWAFPolicy langs.MessageCode = "waf_policy@log_update_waf_policy" // 修改WAF策略 %d 基本信息
WAFPolicy_LogUpgradeWAFPolicy langs.MessageCode = "waf_policy@log_upgrade_waf_policy" // 升级WAF %d 内置规则
WAFRuleGroup_LogCreateRuleGroup langs.MessageCode = "waf_rule_group@log_create_rule_group" // 创建规则分组 %d名称%s
WAFRuleGroup_LogDeleteRuleGroup langs.MessageCode = "waf_rule_group@log_delete_rule_group" // 删除WAF策略 %d 的规则分组 %d
WAFRuleGroup_LogSortRuleGroups langs.MessageCode = "waf_rule_group@log_sort_rule_groups" // 修改WAF策略 %d 中的规则分组中的排序
WAFRuleGroup_LogUpdateRuleGroup langs.MessageCode = "waf_rule_group@log_update_rule_group" // 修改WAF规则分组 %d 基本信息
WAFRuleGroup_LogUpdateRuleGroupIsOn langs.MessageCode = "waf_rule_group@log_update_rule_group_is_on" // 设置WAF规则分组 %d 开启状态
WAFRuleSet_LogDeleteRuleSet langs.MessageCode = "waf_rule_set@log_delete_rule_set" // 删除WAF规则分组 %d 中的规则集 %d
WAFRuleSet_LogSortRuleSets langs.MessageCode = "waf_rule_set@log_sort_rule_sets" // 修改WAF规则分组 %d 中的规则集排序
WAFRuleSet_LogUpdateRuleSet langs.MessageCode = "waf_rule_set@log_update_rule_set" // 修改WAF规则集 %d 基本信息
WAFRuleSet_LogUpdateRuleSetIsOn langs.MessageCode = "waf_rule_set@log_update_rule_set_is_on" // 修改WAF规则集 %d 开启状态
)

84
pkg/langs/lang.go Normal file
View File

@@ -0,0 +1,84 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package langs
import (
"errors"
"fmt"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"strings"
)
const varPrefix = "lang."
type LangCode = string
type Lang struct {
code string
messageMap map[MessageCode]string // message code => message text
}
func NewLang(code string) *Lang {
return &Lang{
code: code,
messageMap: map[MessageCode]string{},
}
}
func (this *Lang) Set(messageCode MessageCode, messageText string) {
this.messageMap[messageCode] = messageText
}
func (this *Lang) Has(messageCode MessageCode) bool {
_, ok := this.messageMap[messageCode]
return ok
}
// Get 读取单条消息
// get single message with message code
func (this *Lang) Get(messageCode MessageCode) string {
return this.messageMap[messageCode]
}
// GetAll 读取所有消息
// get all messages
func (this *Lang) GetAll() map[MessageCode]string {
return this.messageMap
}
// Compile variable to literal strings
func (this *Lang) Compile() error {
for code, oldMessage := range this.messageMap {
message, err := this.get(code, 0)
if err != nil {
return fmt.Errorf("compile '%s': '%s' failed: %w", string(code), oldMessage, err)
}
this.messageMap[code] = message
}
return nil
}
func (this *Lang) get(messageCode MessageCode, loopIndex int) (string, error) {
if loopIndex >= 8 /** max recurse **/ {
return "", errors.New("too many recurse")
}
loopIndex++
message, ok := this.messageMap[messageCode]
if len(message) == 0 {
if !ok && loopIndex > 1 {
// recover as variable
return "${" + varPrefix + string(messageCode) + "}", errors.New("can not find message for code '" + string(messageCode) + "'")
}
return "", nil
}
return configutils.ParseVariablesError(message, func(varName string) (value string, err error) {
if !strings.HasPrefix(varName, varPrefix) {
return "${" + varName + "}", nil
}
return this.get(MessageCode(varName[len(varPrefix):]), loopIndex)
})
}

105
pkg/langs/manager.go Normal file
View File

@@ -0,0 +1,105 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package langs
import (
"fmt"
"strings"
)
var defaultManager = NewManager()
type Manager struct {
langMap map[string]*Lang // lang code => *Lang, lang code must be in lowercase
defaultLangCode string
}
func NewManager() *Manager {
return &Manager{
langMap: map[string]*Lang{},
defaultLangCode: "zh-cn",
}
}
func DefaultManager() *Manager {
return defaultManager
}
func (this *Manager) AddLang(code string) *Lang {
var lang = NewLang(code)
this.langMap[code] = lang
return lang
}
func (this *Manager) HasLang(code string) bool {
_, ok := this.langMap[code]
return ok
}
func (this *Manager) GetLang(code string) (lang *Lang, ok bool) {
lang, ok = this.langMap[code]
return
}
func (this *Manager) MatchLang(code string) (matchedCode string) {
// lookup exact match
code = strings.ToLower(code)
_, ok := this.langMap[code]
if ok {
return code
}
// lookup language family, such as en-us, en
if strings.Contains(code, "-") {
code, _, _ = strings.Cut(code, "-")
}
for rawCode := range this.langMap {
if strings.HasPrefix(rawCode, code+"-") { // en-us vs en
return rawCode
}
}
return this.DefaultLang()
}
func (this *Manager) SetDefaultLang(code string) {
this.defaultLangCode = code
}
func (this *Manager) DefaultLang() string {
if len(this.defaultLangCode) > 0 {
return this.defaultLangCode
}
return "zh-cn"
}
// GetMessage
// message: name: %s, age: %d, salary: %.2f
func (this *Manager) GetMessage(langCode string, messageCode MessageCode, args ...any) string {
var lang = this.langMap[langCode]
if lang == nil && len(this.defaultLangCode) > 0 {
lang = this.langMap[this.defaultLangCode]
}
if lang == nil {
return ""
}
var message = lang.Get(messageCode)
if len(message) == 0 {
// try to get message from default lang
if lang.code != this.defaultLangCode {
var defaultLang = this.langMap[this.defaultLangCode]
if defaultLang != nil {
return defaultLang.Get(messageCode)
}
}
return ""
}
if len(args) == 0 {
return message
}
return fmt.Sprintf(message, args...)
}

43
pkg/langs/manager_test.go Normal file
View File

@@ -0,0 +1,43 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package langs_test
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
"testing"
)
func TestManager_GetMessage(t *testing.T) {
var manager = langs.NewManager()
var lang = manager.AddLang("en-US")
lang.Set("user_description", "user: %s, age: %d")
t.Log(manager.GetMessage("en-US", "user_description", "Lily", 23))
t.Log(manager.GetMessage("zh-CN", "user_description", "Lucy", 23)) // use 'en-US' as fallback language
}
func TestManager_GetMessage2(t *testing.T) {
var manager = langs.NewManager()
manager.SetDefaultLang("zh-CN")
var lang = manager.AddLang("en-US")
lang.Set("user_description", "user: %s, age: %d")
t.Log(manager.GetMessage("en-US", "user_description", "Lily", 23))
t.Log(manager.GetMessage("zh-CN", "user_description", "Lucy", 23)) // should be empty
}
func TestManager_MatchLang(t *testing.T) {
var manager = langs.NewManager()
manager.AddLang("en-us")
manager.AddLang("en")
manager.AddLang("zh-cn")
manager.AddLang("zh-hk")
//manager.AddLang("zh-tw")
for _, code := range []string{
"en",
"en-us",
"zh-cn",
"zh-tw",
} {
t.Log(code, "=>", manager.MatchLang(code))
}
}

13
pkg/langs/message.go Normal file
View File

@@ -0,0 +1,13 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package langs
type MessageCode string
func (this MessageCode) For(langCode LangCode, args ...any) string {
return Message(langCode, this, args...)
}
func (this MessageCode) String() string {
return string(this)
}

14
pkg/langs/message_test.go Normal file
View File

@@ -0,0 +1,14 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package langs
import "testing"
func TestMessageCode_For(t *testing.T) {
defaultManager.AddLang("en-us").
Set("name", "Lily")
var messageCode MessageCode = "name"
t.Log(messageCode.String(), string(messageCode))
t.Log(messageCode.For("en-us"))
}

View File

@@ -0,0 +1,718 @@
// generated by run 'langs generate'
package messages
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
)
func init() {
langs.Load("zh-cn", map[langs.MessageCode]string{
"acme_provider_account@log_create_acme_provider_account": "创建ACME服务商账号 %d",
"acme_provider_account@log_delete_acme_provider_account": "删除ACME服务商账号 %d",
"acme_provider_account@log_update_acme_provider_account": "修改ACME服务商账号 %d",
"acme_task@log_create_acme_task": "创建证书申请任务 %d",
"acme_task@log_delete_acme_task": "删除证书申请任务 %d",
"acme_task@log_run_acme_task": "执行ACME任务 %d",
"acme_task@log_update_acme_task": "修改证书申请任务 %d",
"acme_user@log_create_acme_user": "创建ACME用户 %d",
"acme_user@log_delete_acme_user": "删除ACME用户 %d",
"acme_user@log_update_acme_user": "修改ACME用户 %d",
"ad_network@log_create_ad_network": "创建高防IP线路 %d",
"ad_network@log_delete_ad_network": "删除高防IP线路",
"ad_network@log_update_ad_network": "修改高防IP线路 %d",
"ad_package@log_create_ad_package": "创建高防产品 %d",
"ad_package@log_delete_ad_package": "删除高防产品 %d",
"ad_package@log_update_ad_package": "修改高防产品 %d",
"ad_package_instance@log_create_ad_package_instance": "创建高防实例 %d",
"ad_package_instance@log_delete_ad_package_instance": "删除高防实例 %d",
"ad_package_instance@log_update_ad_package_instance": "修改高防实例 %d",
"ad_package_period@log_create_ad_package_period": "创建高防IP实例有效期 %d",
"ad_package_period@log_delete_ad_package_period": "删除高防IP实例有效期选项 %d",
"ad_package_period@log_update_ad_package_period": "修改高防IP实例有效期选项 %d",
"ad_package_price@log_create_ad_package_price": "为用户 %d 创建高防实例:%d有效期%d数量%d",
"ad_package_price@log_update_ad_package_price": "修改高防产品 %d 有效期 %d 的价格",
"admin@log_create_admin": "创建系统用户 %d",
"admin@log_delete_admin": "删除系统用户 %d",
"admin@log_update_admin": "修改系统用户 %d",
"admin_common@canceled": "已取消",
"admin_common@log_system_error": "系统发生错误:%s",
"admin_common@menu_setting_basic": "基础设置",
"admin_common@menu_setting_cache": "缓存设置",
"admin_common@menu_setting_cache_policy": "缓存策略",
"admin_common@menu_setting_ddos_protection": "DDoS防护",
"admin_common@menu_setting_dns": "DNS设置",
"admin_common@menu_setting_health_check": "健康检查",
"admin_common@menu_setting_metrics": "统计指标",
"admin_common@menu_setting_security_policy": "网络安全",
"admin_common@menu_setting_waf_policy": "WAF策略",
"admin_common@menu_setting_webp": "WebP",
"admin_common@server_error": "服务器出了点小问题,请联系技术人员处理。",
"admin_common@system": "系统",
"admin_dashboard@disk_usage_warning": "当前服务器磁盘空间不足,请立即扩充容量,文件路径:%s已使用%dG已使用比例%.2f%%,仅剩余空间:%.2f%%。<br/>如果是因为本机数据库数据过多,你可以:<a href=\"/settings/database/clean\">[清理访问日志]</a> &nbsp; &nbsp; <a href=\"https://goedge.cn/docs/APINode/QA.md\" target=\"_blank\">[调整数据库binlog设置]</a>",
"admin_dashboard@ui_dns": "DNS",
"admin_dashboard@ui_events": "事件",
"admin_dashboard@ui_overview": "概况",
"admin_dashboard@ui_user": "用户",
"admin_dashboard@ui_waf": "WAF",
"admin_login@log_failed": "登录失败,用户名:%s",
"admin_login@log_otp_verified_success": "成功通过OTP验证登录系统",
"admin_login@log_success": "成功登录系统,用户名:%s",
"admin_login@log_system_error": "登录时发生系统错误:%s",
"admin_login@log_update_login": "修改登录设置",
"admin_menu@admin_recipients": "通知媒介",
"admin_menu@admins": "系统用户",
"admin_menu@dashboard": "数据看板",
"admin_menu@dns": "域名解析",
"admin_menu@dns_clusters": "集群列表",
"admin_menu@dns_issues": "问题修复",
"admin_menu@dns_providers": "DNS服务商",
"admin_menu@finance": "财务管理",
"admin_menu@finance_accounts": "用户账户",
"admin_menu@finance_bills": "费用账单",
"admin_menu@finance_fee": "计费设置",
"admin_menu@finance_income": "统计报表",
"admin_menu@finance_logs": "收支明细",
"admin_menu@finance_orders": "订单管理",
"admin_menu@finance_packages": "流量包",
"admin_menu@logs": "日志审计",
"admin_menu@node_anti_ddos_products": "高防IP",
"admin_menu@node_clusters": "集群列表",
"admin_menu@node_distributed_monitors": "区域监控",
"admin_menu@node_ip_list": "节点IP",
"admin_menu@node_logs": "节点日志",
"admin_menu@node_regions": "区域设置",
"admin_menu@node_ssh_grants": "节点SSH",
"admin_menu@nodes": "边缘节点",
"admin_menu@ns": "智能DNS",
"admin_menu@ns_access_logs": "访问日志",
"admin_menu@ns_clusters": "集群管理",
"admin_menu@ns_domain_batch_operations": "批量操作",
"admin_menu@ns_domain_groups": "域名分组",
"admin_menu@ns_domains": "域名管理",
"admin_menu@ns_node_logs": "运行日志",
"admin_menu@ns_plans": "套餐设置",
"admin_menu@ns_resolve_test": "解析测试",
"admin_menu@ns_routes": "线路管理",
"admin_menu@ns_settings": "全局配置",
"admin_menu@ns_user_plans": "用户套餐",
"admin_menu@plan_user_plans": "已购套餐",
"admin_menu@plans": "套餐管理",
"admin_menu@server_access_log_policies": "日志策略",
"admin_menu@server_access_logs": "访问日志",
"admin_menu@server_cache_policies": "缓存策略",
"admin_menu@server_certs": "证书管理",
"admin_menu@server_global_settings": "通用设置",
"admin_menu@server_groups": "网站分组",
"admin_menu@server_ip_lists": "IP名单",
"admin_menu@server_metrics": "统计指标",
"admin_menu@server_purge_fetch_caches": "刷新预热",
"admin_menu@server_scripts": "脚本库",
"admin_menu@server_traffic_stats": "用量统计",
"admin_menu@server_waf_policies": "WAF策略",
"admin_menu@servers": "网站列表",
"admin_menu@setting_advanced_settings": "高级设置",
"admin_menu@setting_authority": "商业版本",
"admin_menu@setting_basic_settings": "基础设置",
"admin_menu@settings": "系统设置",
"admin_menu@ticket_category": "分类",
"admin_menu@tickets": "工单系统",
"admin_menu@user_list": "用户列表",
"admin_menu@user_settings": "用户设置",
"admin_menu@users": "平台用户",
"admin_profile@log_update_profile": "修改个人资料",
"admin_security@log_update_security_settings": "修改管理界面安全设置",
"admin_server@log_update_server_http_settings": "修改管理界面的HTTP设置",
"admin_server@log_update_server_https_settings": "修改管理界面的HTTPS设置",
"admin_setting@tab_access_log_databases": "日志数据库",
"admin_setting@tab_admin_security_settings": "安全设置",
"admin_setting@tab_admin_server": "Web服务",
"admin_setting@tab_admin_ui": "管理界面设置",
"admin_setting@tab_api_nodes": "API节点",
"admin_setting@tab_authority": "商业版认证",
"admin_setting@tab_backup": "备份",
"admin_setting@tab_client_browsers": "浏览器库",
"admin_setting@tab_client_operation_systems": "操作系统库",
"admin_setting@tab_database": "数据库",
"admin_setting@tab_ip_library": "IP库",
"admin_setting@tab_login": "登录设置",
"admin_setting@tab_monitor_nodes": "监控节点",
"admin_setting@tab_profile": "个人资料",
"admin_setting@tab_transfer": "迁移",
"admin_setting@tab_updates": "检查更新",
"admin_setting@tab_user_nodes": "用户节点",
"admin_setting@tab_user_ui": "用户界面设置",
"admin_ui@default_product_name": "GoEdge",
"admin_ui@default_system_name": "GoEdge管理员系统",
"admin_ui@log_update_ui_settings": "修改管理界面设置",
"admin_update@log_ignore_version": "忽略升级版本 %s",
"admin_update@log_reset_ignore_version": "重置忽略升级版本",
"admin_update@log_update_check_settings": "修改检查更新设置",
"api_node@log_create_api_node": "创建API节点 %d",
"api_node@log_delete_api_node": "删除API节点 %d",
"api_node@log_update_api_node": "修改API节点 %d",
"client_browser@log_create_browser": "创建浏览器信息 %s",
"client_browser@log_update_client_browser": "修改浏览器信息 %d",
"client_system@log_create_system": "创建操作系统信息 %s",
"client_system@log_update_client_system": "修改操作系统信息 %d",
"database@log_delete_table": "删除数据表 %s",
"database@log_truncate_table": "清空数据表 %s 数据",
"database@log_update_api_node_database_config": "修改API节点数据库设置",
"database@log_update_clean_days": "修改数据库自动清理设置",
"db_node@log_create_db_node": "创建数据库节点 %d",
"db_node@log_delete_db_node": "删除数据库节点 %d",
"db_node@log_delete_table": "删除数据库节点 %d 数据表 %s",
"db_node@log_truncate_table": "清空数据库节点 %d 数据表 %s 数据",
"db_node@log_update_db_node": "修改数据库节点 %d",
"db_node@tab_nodes": "数据库节点",
"ddos_protection@log_update_cluster_ddos_protection": "修改集群 %d 的DDOS防护设置",
"ddos_protection@log_update_node_ddos_protection": "修改节点 %d 的DDOS防护设置",
"dns@log_create_domain": "添加管理域名到DNS服务商 %d",
"dns@log_delete_domain": "从DNS服务商中删除域名 %d",
"dns@log_recover_domain": "从DNS服务商中恢复域名 %d",
"dns@log_sync_cluster": "同步集群 %d 的DNS设置",
"dns@log_sync_domain": "同步DNS域名数据 %d",
"dns@log_update_cluster_dns": "修改集群 %d DNS设置",
"dns@log_update_domain": "修改DNS服务商域名 %d",
"dns@log_update_node_dns": "修改节点 %d 的DNS设置",
"dns_provider@log_create_dns_provider": "创建DNS服务商 %d",
"dns_provider@log_delete_dns_provider": "删除DNS服务商 %d",
"dns_provider@log_update_dns_provider": "修改DNS服务商 %d",
"dns_task@log_delete_all_dns_tasks": "删除所有DNS同步任务",
"dns_task@log_delete_dns_task": "删除DNS同步任务 %d",
"finance@log_bill_generate_manually": "手动生成上个月 %s 账单",
"finance@log_update_user_order_config": "修改订单设置",
"finance_fee@log_update_fee_setting": "修改默认计费方式",
"http_access_log_policy@log_create_http_access_log_policy": "创建访问日志策略 %d",
"http_access_log_policy@log_delete_http_access_log_policy": "删除访问日志策略 %d",
"http_access_log_policy@log_test_http_access_log_policy": "测试向访问日志策略 %d 写入数据",
"http_access_log_policy@log_update_http_access_log_policy": "修改访问日志策略 %d",
"http_auth_policy@log_create_http_auth_policy": "创建HTTP鉴权 %d",
"http_auth_policy@log_update_http_auth_policy": "修改HTTP鉴权 %d",
"http_cache_task@log_create_http_cache_task_fetch": "批量预热缓存Key",
"http_cache_task@log_create_http_cache_task_purge": "批量刷新缓存Key",
"http_cache_task@log_delete_http_cache_task": "删除缓存任务 %d",
"http_cache_task@log_reset_http_cache_task": "重置缓存任务 %d 状态",
"http_fastcgi@log_create_http_fastcgi": "创建Fastcgi %d",
"http_fastcgi@log_update_http_fastcgi": "修改Fastcgi %d",
"http_location@log_create_http_location": "创建路由规则:%s",
"http_location@log_update_http_location": "修改路由规则 %d 设置",
"http_rewrite_rule@log_create_rewrite_rule": "在Web %d 中创建重写规则 %d",
"http_rewrite_rule@log_delete_rewrite_rule": "从Web %d 中删除重写规则 %d",
"http_rewrite_rule@log_sort_rewrite_rules": "对Web %d 中的重写规则进行排序",
"http_rewrite_rule@log_update_rewrite_rule": "修改Web %d 中的重写规则 %d",
"ip_item@log_create_ip_item": "在名单 %d 中创建IP %d",
"ip_item@log_delete_ip_item": "从IP名单 %d 中删除IP %d",
"ip_item@log_read_all_ip_items": "将所有IP名单置为已读",
"ip_item@log_update_ip_item": "修改IP名单中的IP %d",
"ip_library@log_finish_ip_library": "完成IP库%d 制作",
"ip_library_artifact@log_cancel_ip_library_artifact": "取消使用IP库 %d",
"ip_library_artifact@log_delete_ip_library_artifact": "删除IP库 %d",
"ip_library_artifact@log_use_ip_library_artifact": "使用IP库 %d",
"ip_library_file@log_delete_ip_library_file": "删除IP库文件 %d",
"ip_library_file@log_generate_ip_library_file": "重新生成IP库 %d 文件",
"ip_library_file@log_upload_ip_library_file": "上传IP库 %d",
"ip_list@log_bind_ip_list_waf_policy": "绑定IP名单 %d 到WAF策略 %d",
"ip_list@log_create_ip_items_batch": "在IP名单 %d 中批量添加IP",
"ip_list@log_create_ip_list": "创建IP名单 %d",
"ip_list@log_delete_ip_batch": "批量删除IP名单中的IP%s",
"ip_list@log_delete_ip_list": "删除IP名单 %d",
"ip_list@log_export_ip_list": "导出IP名单 %d",
"ip_list@log_import_ip_list": "导入IP名单 %d",
"ip_list@log_unbind_ip_list_waf_policy": "解除绑定IP名单 %d WAF策略 %d",
"ip_list@log_update_ip_list": "修改IP名单 %d",
"level@error": "错误",
"level@info": "信息",
"level@warn": "警告",
"log@log_clean_all_logs": "清除全部日志",
"log@log_clean_logs_days_before": "清除 %d 以前的日志",
"log@log_delete_log": "删除单个操作日志 %d",
"log@log_update_settings": "修改日志相关配置",
"log@tag_access_log": "访问日志",
"log@tag_listener": "端口监听",
"log@tag_script": "脚本",
"log@tag_waf": "WAF",
"message@log_read_all": "将所有消息置为已读",
"message@log_read_messages": "将一组消息置为已读",
"message_media_instance@log_create_message_media_instance": "创建消息媒介 %d",
"message_media_instance@log_delete_message_media_instance": "删除消息媒介 %d",
"message_media_instance@log_update_message_media_instance": "修改消息媒介 %d",
"message_receiver@log_delete_receiver": "删除接收人关联关系 %d",
"message_receiver@log_update_cluster_message_receivers": "修改集群 %d 消息接收人",
"message_recipient@log_create_message_recipient": "创建媒介接收人 %d",
"message_recipient@log_delete_message_recipient": "删除媒介接收人 %d",
"message_recipient@log_update_message_recipient": "修改媒介接收人 %d",
"message_task@log_create_testing_message_task": "创建媒介测试任务 %d",
"message_task@log_delete_message_task": "删除消息发送任务 %d",
"message_task@log_update_message_task_status": "修改消息任务 %d 状态为 %d",
"metric_chart@log_create_metric_chart": "创建指标图表 %d",
"metric_chart@log_delete_metric_chart": "删除指标图表 %d",
"metric_chart@log_update_metric_chart": "修改指标图表 %d",
"metric_item@log_add_metric_item_to_cluster": "添加指标 %d 到集群 %d",
"metric_item@log_create_metric_item": "创建统计指标 %d",
"metric_item@log_delete_metric_item": "删除统计指标",
"metric_item@log_delete_metric_item_from_cluster": "从集群 %d 中移除指标 %d",
"metric_item@log_update_metric_item": "修改统计指标 %d",
"monitor_node@log_create_monitor_node": "创建监控节点 %d",
"monitor_node@log_delete_monitor_node": "删除监控节点 %d",
"monitor_node@log_update_monitor_node": "修改监控节点 %d",
"node@log_create_node": "创建节点 %d",
"node@log_create_node_batch": "批量创建节点",
"node@log_delete_node_from_cluster": "从集群 %d 中删除节点 %d",
"node@log_install_node": "安装节点 %d",
"node@log_install_node_remotely": "远程安装节点 %d",
"node@log_start_node_remotely": "远程启动节点 %d",
"node@log_stop_node_remotely": "远程停止节点 %d",
"node@log_up_node": "手动上线节点 %d",
"node@log_update_node": "修改节点 %d 基本信息",
"node@log_update_node_installation_status": "修改节点安装状态 %d",
"node@log_upgrade_node_remotely": "远程升级节点 %d",
"node@ungrouped_label": "未分组",
"node_action@log_copy_node_actions_to_cluster": "复制节点 %d 调度动作到集群",
"node_action@log_copy_node_actions_to_group": "复制节点 %d 调度动作到分组",
"node_action@log_create_node_action": "创建动作 %d",
"node_action@log_delete_node_action": "删除节点动作 %d",
"node_action@log_sort_node_actions": "修改节点 %d 动作排序",
"node_action@log_update_node_action": "修改节点动作 %d",
"node_cache@log_update_node_cache_settings": "修改节点 %d 缓存设置",
"node_cluster@log_create_cluster": "创建节点集群:%d",
"node_cluster@log_delete_cluster": "删除集群 %d",
"node_cluster@log_pin_cluster": "置顶集群 %d",
"node_cluster@log_run_cluster_health_check": "执行集群健康检查设置 %d",
"node_cluster@log_unpin_cluster": "取消置顶集群 %d",
"node_cluster@log_update_cluster_basic_settings": "修改集群基础设置 %d",
"node_cluster@log_update_cluster_health_check": "修改集群健康检查设置 %d",
"node_cluster_menu@setting_basic": "基础设置",
"node_cluster_menu@setting_cache_policy": "缓存策略",
"node_cluster_menu@setting_cc": "CC防护",
"node_cluster_menu@setting_ddos_protection": "DDoS防护",
"node_cluster_menu@setting_dns": "DNS设置",
"node_cluster_menu@setting_health_check": "健康检查",
"node_cluster_menu@setting_http3": "HTTP/3",
"node_cluster_menu@setting_metrics": "统计指标",
"node_cluster_menu@setting_notification": "消息通知",
"node_cluster_menu@setting_pages": "自定义页面",
"node_cluster_menu@setting_schedule": "智能调度",
"node_cluster_menu@setting_security_policy": "网络安全",
"node_cluster_menu@setting_service_global": "网站设置",
"node_cluster_menu@setting_system_service": "系统服务",
"node_cluster_menu@setting_thresholds": "阈值设置",
"node_cluster_menu@setting_toa": "TOA设置",
"node_cluster_menu@setting_uam": "5秒盾",
"node_cluster_menu@setting_waf_actions": "WAF动作",
"node_cluster_menu@setting_waf_policy": "WAF策略",
"node_cluster_menu@setting_webp": "WebP",
"node_cluster_menu@tab_cluster_dashboard": "集群看板",
"node_cluster_menu@tab_cluster_delete": "删除集群",
"node_cluster_menu@tab_cluster_nodes": "节点列表",
"node_cluster_menu@tab_cluster_settings": "集群设置",
"node_dns@log_update_node_dns": "修改节点 %d DNS设置",
"node_grant@log_create_ssh_grant": "创建SSH认证 %d",
"node_grant@log_delete_ssh_grant": "删除SSH认证 %d",
"node_grant@log_update_ssh_grant": "修改SSH认证 %d",
"node_grant@method_private_key": "私钥",
"node_grant@method_user_password": "用户名+密码",
"node_group@log_create_node_group": "创建节点分组 %d",
"node_group@log_delete_node_group": "删除节点分组 %d",
"node_group@log_sort_node_groups": "修改节点分组排序",
"node_group@log_update_node_group": "修改节点分组 %d",
"node_ip_address@log_delete_node_ip_address": "删除IP地址 %d",
"node_ip_address@log_down_node_ip_address": "手动设置IP地址 %d 下线",
"node_ip_address@log_restore_node_ip_address": "取消IP地址 %d 的备用IP",
"node_ip_address@log_up_node_ip_address": "手动设置IP地址 %d 上线",
"node_log@log_delete_node_logs_batch": "批量删除节点运行日志",
"node_log@log_fix_all_logs": "设置所有日志为已修复",
"node_log@log_fix_node_logs": "设置日志 %s 为已修复",
"node_menu@create_multiple_nodes": "批量创建",
"node_menu@create_single_node": "单个创建",
"node_menu@install_auto_register": "自动注册",
"node_menu@install_manually": "手动安装",
"node_menu@install_remote": "远程安装(%d)",
"node_menu@install_remote_upgrade": "远程升级(%d)",
"node_menu@setting_basic": "基础设置",
"node_menu@setting_cache": "缓存设置",
"node_menu@setting_ddos_protection": "DDoS防护",
"node_menu@setting_dns": "DNS设置",
"node_menu@setting_schedule": "智能调度",
"node_menu@setting_ssh": "SSH设置",
"node_menu@setting_system": "系统设置",
"node_menu@setting_thresholds": "阈值设置",
"node_price_item@log_create_node_price_item_bandwidth": "创建带宽价格项目 %d",
"node_price_item@log_create_node_price_item_traffic": "创建流量价格项目 %d",
"node_price_item@log_delete_node_price_item": "删除流量价格项目 %d",
"node_price_item@log_update_node_price_item_bandwidth": "修改带宽价格项目 %d",
"node_price_item@log_update_node_price_item_traffic": "修改流量价格项目 %d",
"node_region@log_create_node_region": "创建节点区域 %d",
"node_region@log_delete_node_region": "删除节点区域 %d",
"node_region@log_move_node_between_regions": "修改节点 %d 区域到 %d",
"node_region@log_sort_node_regions": "修改节点区域排序",
"node_region@log_update_node_region": "修改节点区域 %d",
"node_region_price@log_update_node_region_price": "修改区域 %d - 价格项 %d 的价格",
"node_schedule@log_reset_node_action_status": "重置节点 %d 动作状态",
"node_schedule@log_update_node_schedule_basic": "修改节点调度基本信息",
"node_ssh@log_update_node_ssh": "修改节点 %d SSH配置",
"node_system@log_update_node_system_settings": "修改节点 %d 系统信息",
"node_systemd@log_update_cluster_systemd_settings": "修改集群 %d 的系统服务设置",
"node_task@log_delete_all_node_tasks": "删除所有节点同步任务",
"node_task@log_delete_node_task": "删除同步任务 %d",
"node_task@log_delete_node_tasks_batch": "批量删除节点同步任务",
"node_threshold@log_create_node_threshold": "创建节点阈值 %d",
"node_threshold@log_delete_node_threshold": "删除阈值 %d",
"node_threshold@log_update_node_threshold": "修改节点阈值 %d",
"node_toa@log_update_cluster_toa": "修改集群 %d 的TOA设置",
"ns@log_create_ns_domains_batch": "批量添加域名",
"ns@log_create_ns_records_batch": "批量添加解析",
"ns@log_delete_ns_domains_batch": "批量删除域名,用户 %d",
"ns@log_delete_ns_records_batch": "批量删除域名记录",
"ns@log_disable_ns_records_batch": "批量停用域名记录",
"ns@log_enable_ns_records_batch": "批量启用域名记录",
"ns@log_import_records_batch": "批量导入记录",
"ns@log_update_ns_records_batch": "批量修改域名记录",
"ns@log_update_ns_user_config": "修改NS全局设置--用户相关设置",
"ns@setting_access_logs": "访问日志设置",
"ns@setting_user": "用户设置",
"ns_cluster@log_create_ns_cluster": "创建域名服务集群 %d",
"ns_cluster@log_delete_ns_cluster": "删除域名服务集群 %d",
"ns_cluster@log_update_ns_cluster_settings_access_log": "修改域名服务集群 %d 访问日志配置",
"ns_cluster@log_update_ns_cluster_settings_answer": "修改NS集群 %d 应答模式设置",
"ns_cluster@log_update_ns_cluster_settings_basic": "修改域名服务集群基本信息 %d",
"ns_cluster@log_update_ns_cluster_settings_ddos_protection": "修改NS集群 %d 的DDOS防护设置",
"ns_cluster@log_update_ns_cluster_settings_doh": "修改NS集群 %d DoH设置",
"ns_cluster@log_update_ns_cluster_settings_recursion": "修改DNS集群 %d 的递归DNS设置",
"ns_cluster@log_update_ns_cluster_settings_soa": "修改NS集群 %d SOA配置",
"ns_cluster@log_update_ns_cluster_settings_tcp": "修理NS集群 %d TCP设置",
"ns_cluster@log_update_ns_cluster_settings_tls": "修改NS集群 %d TLS设置",
"ns_cluster@log_update_ns_cluster_settings_udp": "修改NS集群 %d UDP设置",
"ns_cluster@menu_access_logs": "访问日志",
"ns_cluster@menu_answer_setting": "应答模式",
"ns_cluster@menu_basic": "基础设置",
"ns_cluster@menu_ddos_protection": "DDoS防护",
"ns_cluster@menu_dns_recursion": "递归DNS",
"ns_cluster@menu_doh": "DoH",
"ns_cluster@menu_soa": "SOA",
"ns_cluster@menu_tcp": "TCP",
"ns_cluster@menu_tls": "TLS",
"ns_cluster@menu_udp": "UDP",
"ns_cluster@tab_delete": "删除集群",
"ns_cluster@tab_nodes": "节点列表",
"ns_cluster@tab_setting": "集群设置",
"ns_domain@log_create_ns_domain": "创建域名 %d",
"ns_domain@log_create_ns_domains_batch": "批量添加域名",
"ns_domain@log_delete_ns_domain": "删除域名 %d",
"ns_domain@log_update_ns_domain": "修改域名 %d",
"ns_domain@log_update_ns_domain_health_check": "修改域名 %d 健康检查设置",
"ns_domain@log_update_ns_domain_status": "修改域名 %d 状态为 %s",
"ns_domain@log_update_ns_domain_tsig": "修改域名 %d 的TSIG配置",
"ns_domain@log_validate_ns_domains": "批量验证域名",
"ns_domain_group@log_create_ns_domain_group": "创建域名分组 %d",
"ns_domain_group@log_delete_ns_domain_group": "删除域名分组 %d",
"ns_domain_group@log_update_ns_domain_group": "修改域名分组 %d",
"ns_key@log_create_ns_key": "创建DNS密钥 %d",
"ns_key@log_delete_ns_key": "删除DNS密钥 %d",
"ns_key@log_update_ns_key": "修改DNS密钥 %d",
"ns_node@log_create_ns_node": "创建域名服务节点 %d",
"ns_node@log_delete_ns_node": "删除域名服务节点 %d",
"ns_node@log_install_ns_node_remotely": "安装节点 %d",
"ns_node@log_start_ns_node_remotely": "远程启动节点 %d",
"ns_node@log_stop_ns_node_remotely": "远程停止节点 %d",
"ns_node@log_update_ns_node": "修改节点 %d",
"ns_node@log_update_ns_node_installation_status": "修改节点安装状态 %d",
"ns_node_ssh@log_update_ns_node_ssh": "修改节点 %d SSH配置",
"ns_plan@log_create_ns_plan": "创建套餐 %d",
"ns_plan@log_delete_ns_plan": "删除套餐 %d",
"ns_plan@log_sort_ns_plans": "套餐排序",
"ns_plan@log_update_ns_plan": "修改套餐 %d",
"ns_record@log_create_ns_record": "创建域名记录 %d",
"ns_record@log_create_ns_records_batch": "批量创建域名记录",
"ns_record@log_delete_ns_record": "删除域名记录 %d",
"ns_record@log_up_ns_record": "手动设置DNS记录 %d 为上线状态",
"ns_record@log_update_ns_record": "修改域名记录 %d",
"ns_record@log_update_ns_record_health_check": "修改记录 %d 的健康检查",
"ns_route@log_create_ns_route": "创建域名服务线路 %d",
"ns_route@log_delete_ns_route": "删除域名服务线路 %d",
"ns_route@log_sort_ns_routes": "对线路进行排序",
"ns_route@log_update_ns_route": "修改域名线路 %d",
"ns_route_category@log_create_ns_route_category": "创建NS线路分类 %d",
"ns_route_category@log_delete_ns_route_category": "删除NS线路分类 %d",
"ns_route_category@log_sort_ns_route_categories": "对NS线路分类进行排序",
"ns_route_category@log_update_ns_route_category": "修改NS线路分类 %d",
"ns_user_plan@log_create_ns_user_plan": "为用户 %d 创建DNS套餐 %d",
"ns_user_plan@log_delete_ns_user_plan": "删除用户套餐 %d",
"ns_user_plan@log_update_ns_user_plan": "修改用户DNS套餐 %d",
"order_method@log_create_order_method": "创建支付方式 %d",
"order_method@log_delete_order_method": "删除支付方式 %d",
"order_method@log_update_order_method": "修改支付方式 %d",
"plan@log_create_plan": "创建套餐 %d",
"plan@log_delete_plan": "删除套餐 %d",
"plan@log_sort_plans": "对套餐进行排序",
"plan@log_update_plan": "修改套餐 %d",
"region_city@log_add_region_city_code": "添加城市/市 %d 别名 %s",
"region_city@log_update_region_city_custom": "定制城市 %d 信息",
"region_country@log_add_region_country_code": "添加国家/地区 %d 别名 %s",
"region_country@log_update_region_country_custom": "定制国家/地区 %d 信息",
"region_country@region_china": "中国",
"region_country@region_china_hk": "中国香港",
"region_country@region_china_mainland": "中国内地",
"region_country@region_china_mo": "中国澳门",
"region_country@region_china_tw": "中国台湾",
"region_country@region_greater_china": "中国全境",
"region_provider@log_add_region_provider_code": "添加ISP服务商 %d 别名 %s",
"region_provider@log_update_region_provider_custom": "定制ISP %d 信息",
"region_province@log_add_region_province_code": "添加省份/州 %d 别名 %s",
"region_province@log_update_region_province_custom": "定制省份 %d 信息",
"region_town@log_add_region_town_code": "添加区/县 %d 别名 %s",
"region_town@log_update_region_town_custom": "定制县级 %d 信息",
"report_node@log_create_report_node": "创建监控终端 %d",
"report_node@log_delete_report_node": "删除监控终端 %d",
"report_node@log_update_report_node": "修改监控终端 %d",
"report_node_group@log_create_report_node_group": "创建监控节点分组 %d",
"report_node_group@log_delete_report_node_group": "删除监控节点分组 %d",
"report_node_group@log_update_report_node_group": "修改监控节点分组 %d",
"reverse_proxy@log_update_reverse_proxy_scheduling": "修改反向代理 %d 负载均衡算法",
"script@log_create_script": "创建脚本 %d",
"script@log_delete_script": "删除脚本 %d",
"script@log_publish_scripts": "发布脚本库到边缘节点",
"script@log_update_script": "修改脚本 %d",
"server@copy_setting_current_cluster": "当前集群:%s",
"server@copy_setting_current_group": "当前分组:%s",
"server@copy_setting_current_user": "当前用户:%s",
"server@copy_setting_select_cluster": "选择集群",
"server@copy_setting_select_group": "选择分组",
"server@copy_setting_select_server": "选择网站",
"server@copy_setting_select_user": "选择用户",
"server@log_copy_server_configs": "从网站 %d 中同步配置 %s",
"server@log_create_server": "创建网站 %d",
"server@log_delete_server": "删除网站 %d",
"server@log_disable_server": "停用网站 %d",
"server@log_enable_server": "启用网站 %d",
"server@log_submit_auditing_server": "提交网站 %d 域名审核",
"server@log_update_global_settings": "保存网站全局配置",
"server@log_update_server_basic": "修改网站 %d 基本信息",
"server@log_update_server_groups": "修改网站 %d 所属分组",
"server@log_update_server_is_on": "修改网站 %d 启用状态",
"server@log_update_server_name": "修改网站名称",
"server@menu_accesslog_history": "历史",
"server@menu_accesslog_realtime": "实时",
"server@menu_accesslog_today": "今天",
"server@menu_dashboard": "看板",
"server@menu_setting_access_log": "访问日志",
"server@menu_setting_auth": "访问鉴权",
"server@menu_setting_basic": "基本信息",
"server@menu_setting_cache": "缓存",
"server@menu_setting_cc": "CC防护",
"server@menu_setting_charset": "字符编码",
"server@menu_setting_client_ip": "访客IP地址",
"server@menu_setting_compression": "内容压缩",
"server@menu_setting_delete": "删除",
"server@menu_setting_dns": "DNS",
"server@menu_setting_domains": "域名",
"server@menu_setting_fastcgi": "Fastcgi",
"server@menu_setting_group": "分组",
"server@menu_setting_http": "HTTP",
"server@menu_setting_http_headers": "HTTP报头",
"server@menu_setting_http_proxy": "HTTP代理",
"server@menu_setting_https": "HTTPS",
"server@menu_setting_locations": "路由规则",
"server@menu_setting_optimization": "页面优化",
"server@menu_setting_origins": "源站",
"server@menu_setting_others": "其他设置",
"server@menu_setting_pages": "自定义页面",
"server@menu_setting_plan": "套餐",
"server@menu_setting_redirects": "URL跳转",
"server@menu_setting_referers": "防盗链",
"server@menu_setting_request_limit": "请求限制",
"server@menu_setting_rewrite_rules": "重写规则",
"server@menu_setting_root": "静态分发",
"server@menu_setting_scripts": "边缘脚本",
"server@menu_setting_stat": "统计",
"server@menu_setting_tcp": "TCP",
"server@menu_setting_tcp_proxy": "TCP代理",
"server@menu_setting_tls": "TLS",
"server@menu_setting_traffic_limit": "流量限制",
"server@menu_setting_uam": "5秒盾",
"server@menu_setting_udp": "UDP",
"server@menu_setting_udp_proxy": "UDP代理",
"server@menu_setting_unix": "Unix",
"server@menu_setting_user_agents": "UA名单",
"server@menu_setting_waf": "WAF",
"server@menu_setting_webp": "WebP",
"server@menu_setting_websocket": "Websocket",
"server@menu_stat_clients": "终端",
"server@menu_stat_providers": "运营商",
"server@menu_stat_regions": "地域分布",
"server@menu_stat_traffic": "流量统计",
"server@menu_stat_waf": "WAF",
"server@server_names_log_update_server_names": "修改网站 %d 域名",
"server@tab_access_logs": "日志",
"server@tab_dashboard": "看板",
"server@tab_delete": "删除",
"server@tab_server_list": "网站列表",
"server@tab_settings": "设置",
"server@tab_stat": "统计",
"server_access_log@log_update_access_log_setting": "修改Web %d 的访问日志设置",
"server_auth@log_update_http_auth_settings": "修改Web %d 的鉴权设置",
"server_cache@log_fetch_caches": "预热网站 %d 缓存",
"server_cache@log_purge_caches": "删除网站 %d 缓存",
"server_cache@log_update_cache_settings": "修改Web %d 的缓存设置",
"server_cache@log_update_cluster_cache_policy": "设置集群 %d 的缓存策略为 %d",
"server_cache_policy@log_clean_all": "清除缓存,缓存策略:%d",
"server_cache_policy@log_create_cache_policy": "创建缓存策略:%d",
"server_cache_policy@log_delete_cache_policy": "删除缓存策略:%d",
"server_cache_policy@log_fetch_caches": "预热缓存,缓存策略:%d",
"server_cache_policy@log_purge_caches": "删除缓存,缓存策略:%d",
"server_cache_policy@log_stat_caches": "统计缓存,缓存策略:%d",
"server_cache_policy@log_test_reading": "测试读取,缓存策略:%d",
"server_cache_policy@log_test_writing": "测试写入,缓存策略:%d",
"server_cache_policy@log_update_cache_policy": "修改缓存策略:%d",
"server_cc@log_update_cc_settings": "修改Web %d CC防护配置",
"server_cc@log_update_cluster_http_cc_policy": "修改集群 %d 的HTTP CC策略设置",
"server_charset@log_update_charset_setting": "修改Web %d 的字符集设置",
"server_common@log_update_common_settings": "修改网站Web %d 设置的其他设置",
"server_compression@log_update_compression_settings": "修改Web %d 的压缩设置",
"server_dns@log_regenerate_dns_name": "重新生成网站 %d 的CNAME",
"server_dns@log_update_dns_name": "修改网站 %d CNAME为 %s",
"server_dns@log_update_dns_settings": "修改网站 %d 的DNS设置",
"server_fastcgi@log_update_http_fastcgi": "修改Web %d 的Fastcgi设置",
"server_global_setting@log_update_cluster_global_server_config": "修改集群 %d 全局配置",
"server_group@log_create_server_group": "创建网站分组 %d",
"server_group@log_delete_server_group": "删除网站分组 %d",
"server_group@log_sort_server_groups": "修改网站分组排序",
"server_group@log_update_server_group": "修改网站分组 %d",
"server_http3@log_update_cluster_http3_policy": "修改集群 %d 的HTTP3策略设置",
"server_http@log_update_http_settings": "修改网站 %d 的HTTP设置",
"server_http_header@log_create_deleting_header": "添加删除的报头 HeaderPolicyId: %d, Name: %s",
"server_http_header@log_create_non_standard_header": "添加非标的报头 HeaderPolicyId: %d, Name: %s",
"server_http_header@log_create_setting_request_header": "添加自定义请求报头HeaderPolicyId:%d, Name:%s, Value:%s",
"server_http_header@log_create_setting_response_header": "添加自定义响应报头HeaderPolicyId:%d, Name:%s, Value:%s",
"server_http_header@log_delete_deleting_header": "删除需要删除的报头HeaderPolicyId:%d, HeaderName:%s",
"server_http_header@log_delete_header": "删除报头HeaderPolicyId:%d, HeaderId:%d",
"server_http_header@log_delete_non_standard_header": "删除需要非标的报头HeaderPolicyId:%d, HeaderName:%s",
"server_http_header@log_update_http_headers": "修改Web %d 的报头设置",
"server_http_header@log_update_setting_header": "修改设置报头HeaderId:%d, Name:%s, Value:%s",
"server_https@log_update_https_settings": "修改网站 %d 的HTTPS设置",
"server_optimization@log_update_optimization_settings": "修改Web %d 的页面优化设置",
"server_origin@log_create_origin": "创建源站 %d",
"server_origin@log_delete_origin": "删除源站 %d",
"server_origin@log_update_origin": "修改源站 %d",
"server_page@log_create_page": "创建自定义页面 %d",
"server_page@log_update_cluster_pages": "修改集群 %d 自定义页面策略",
"server_page@log_update_page": "修改自定义页面 %d",
"server_page@log_update_pages": "修改Web %d 的自定义页面设置",
"server_redirect@log_update_redirects": "修改Web %d 的跳转设置",
"server_referer@log_update_referers": "修改Web %d 防盗链设置",
"server_request_limit@log_update_request_limit_settings": "修改Web %d 请求限制",
"server_reverse_proxy@log_update_location_reverse_proxy_settings": "修改路由规则 %d 的反向代理设置",
"server_reverse_proxy@log_update_server_group_reverse_proxy_settings": "修改分组 %d 的反向代理设置",
"server_reverse_proxy@log_update_server_reverse_proxy_settings": "修改网站 %d 的反向代理设置",
"server_root@log_update_root": "修改Web %d 静态分发设置",
"server_script@log_update_scripts": "修改Web %d 边缘脚本",
"server_stat@log_update_stat_settings": "修改Web %d 的统计设置",
"server_tcp@log_update_tcp_settings": "修改网站 %d TCP设置",
"server_tls@log_update_tls_settings": "修改网站 %d TLS设置",
"server_traffic_limit@log_update_traffic_limit_settings": "修改网站 %d 流量限制",
"server_traffic_stat@all_servers": "全部网站(%d",
"server_uam@log_update_cluster_uam_policy": "修改集群 %d 的UAM设置",
"server_uam@log_update_server_uam_settings": "修改网站 %d 全站防护模式",
"server_uam@log_update_uam_settings": "修改Web %d 全站防护模式",
"server_udp@log_update_udp_settings": "修改网站 %d UDP设置",
"server_user_agent@log_update_user_agents": "修改Web %d User-Agent设置",
"server_waf@log_update_waf_settings": "修改Web %d 的WAF设置",
"server_webp@log_update_cluster_webp_policy": "修改集群 %d 的WebP设置",
"server_websocket@log_update_websocket_settings": "修改Web %d 的Websocket设置",
"ssl_cert@log_delete_ssl_cert": "删除SSL证书 %d",
"ssl_cert@log_download_ssl_cert": "下载SSL证书 %d",
"ssl_cert@log_download_ssl_cert_key": "下载SSL密钥 %d",
"ssl_cert@log_download_ssl_cert_zip": "下载SSL证书压缩包 %d",
"ssl_cert@log_ocsp_ignore_ocsp_status": "忽略一组证书的OCSP状态",
"ssl_cert@log_ocsp_reset_all_ocsp_status": "忽略所有证书的OCSP状态",
"ssl_cert@log_ocsp_reset_ocsp_status": "重置一组证书的OCSP状态",
"ssl_cert@log_update_ssl_cert": "修改SSL证书 %d",
"ssl_cert@log_upload_ssl_cert": "上传SSL证书 %d",
"ssl_cert@log_upload_ssl_cert_batch": "批量上传证书",
"ssl_cert@menu_apply": "申请证书",
"ssl_cert@menu_certs": "证书",
"ssl_cert@menu_ocsp": "OCSP日志",
"system@home_page": "https://goedge.cn",
"ticket_category@log_create_ticket_category": "添加工单分类 %d",
"ticket_category@log_delete_ticket_category": "删除工单分类 %d",
"ticket_category@log_update_ticket_category": "修改分类 %d",
"traffic_package@log_create_traffic_package": "创建流量包 %d",
"traffic_package@log_delete_traffic_package": "删除流量包 %d",
"traffic_package@log_update_traffic_package": "修改流量包 %d",
"traffic_package_period@log_create_traffic_package_period": "创建流量包有效期 %d",
"traffic_package_period@log_delete_traffic_package_period": "删除流量包有效期选项 %d",
"traffic_package_period@log_update_traffic_package_period": "修改流量包有效期选项 %d",
"traffic_package_price@log_update_traffic_package_price": "修改流量包 %d 区域 %d x 有效期 %d 的价格",
"user@log_create_user": "创建用户 %d",
"user@log_delete_user": "删除用户 %d",
"user@log_update_user": "修改用户 %d",
"user@log_update_user_email_settings": "修改用户邮件设置",
"user@log_update_user_features": "设置用户 %d 的功能列表",
"user@log_update_user_global_settings": "修改用户设置",
"user@log_update_user_price_period": "修改计费周期为 %s",
"user@log_update_user_price_type": "修改计费类型为 %s",
"user@log_update_user_profile": "修改个人资料",
"user@log_update_user_sms_settings": "修改用户短信设置",
"user@log_verify_user": "审核用户:%d 结果:%s",
"user_access_key@log_create_user_access_key": "创建AccessKey %d",
"user_access_key@log_delete_user_access_key": "删除AccessKey %d",
"user_access_key@log_update_user_access_key_is_on": "设置AccessKey %d 启用状态",
"user_account@log_update_user_account": "操作用户账户 %d: %s",
"user_ad_instance@log_delete_user_ad_instance": "删除用户高防实例 %d",
"user_ad_instance@log_renew_user_ad_instance": "为用户高防实例 %d 续期",
"user_ad_instance@log_update_user_ad_instance_objects": "修改用户高防实例 %d 防护对象",
"user_bill@log_pay_user_bill": "支付账单 %d",
"user_common@canceled": "已取消",
"user_common@log_system_error": "系统发生错误:%s",
"user_common@server_error": "服务器出了点小问题,请联系技术人员处理。",
"user_common@system": "系统",
"user_identity@log_cancel_user_identity": "取消身份认证审核",
"user_identity@log_reject_user_identity": "驳回用户 %d 的实名认证",
"user_identity@log_reset_user_identity": "重置用户 %d 的实名认证",
"user_identity@log_submit_user_identity": "提交身份认证审核",
"user_identity@log_update_user_identity_enterprise": "修改/上传企业实名认证信息",
"user_identity@log_update_user_identity_individual": "修改/上传个人实名认证信息",
"user_identity@log_verify_user_identity": "通过用户 %d 的实名认证",
"user_login@log_update_login": "修改登录设置",
"user_node@log_create_user_node": "创建用户节点 %d",
"user_node@log_delete_user_node": "删除用户节点 %d",
"user_node@log_update_user_node": "修改用户节点 %d",
"user_order@log_finish_user_order": "设置订单 %s 为完成支付",
"user_plan@log_bind_user_plan_to_server": "修改网站 %d 绑定的套餐为 %d",
"user_plan@log_buy_user_plan": "为用户 %d 购买套餐 %d",
"user_plan@log_cancel_user_plan_from_server": "取消网站 %d 绑定的套餐",
"user_plan@log_delete_user_plan": "删除用户已购套餐 %d",
"user_plan@log_renew_user_plan": "续费已购套餐 %d",
"user_ticket@log_create_user_ticket": "创建工单 %d",
"user_ticket_log@log_reply_ticket": "回复工单 %d",
"user_traffic_package@log_create_user_traffic_package": "为用户 %d 创建流量包:%d区域%d有效期%d数量%d",
"user_traffic_package@log_delete_user_traffic_package": "删除用户流量包 %d",
"waf@connector_and": "和(AND)",
"waf@connector_and_description": "所有规则都满足才视为匹配",
"waf@connector_or": "或(OR)",
"waf@connector_or_description": "任一规则满足了就视为匹配",
"waf@log_delete_ip_from_waf_policy": "从WAF策略 %d 名单中删除IP %d",
"waf@log_update_forbidden_countries": "WAF策略 %d 设置禁止访问的国家和地区",
"waf@log_update_forbidden_provinces": "WAF策略 %d 设置禁止访问的省份",
"waf@log_update_ip_from_waf_policy": "修改WAF策略 %d 名单中的IP %d",
"waf_action@log_create_waf_action": "创建集群 %d 的WAF动作",
"waf_action@log_delete_waf_action": "删除WAF动作 %d",
"waf_action@log_update_waf_action": "修改WAF动作 %d",
"waf_policy@log_create_waf_policy": "创建WAF策略 %d",
"waf_policy@log_delete_waf_policy": "删除WAF策略 %d",
"waf_policy@log_export_waf_policy": "导出WAF策略 %d",
"waf_policy@log_import_waf_policy": "从文件中导入规则到WAF策略 %d",
"waf_policy@log_update_cluster_waf_policy": "设置集群 %d 的WAF策略为 %d",
"waf_policy@log_update_waf_policy": "修改WAF策略 %d 基本信息",
"waf_policy@log_upgrade_waf_policy": "升级WAF %d 内置规则",
"waf_rule_group@log_create_rule_group": "创建规则分组 %d名称%s",
"waf_rule_group@log_delete_rule_group": "删除WAF策略 %d 的规则分组 %d",
"waf_rule_group@log_sort_rule_groups": "修改WAF策略 %d 中的规则分组中的排序",
"waf_rule_group@log_update_rule_group": "修改WAF规则分组 %d 基本信息",
"waf_rule_group@log_update_rule_group_is_on": "设置WAF规则分组 %d 开启状态",
"waf_rule_set@log_delete_rule_set": "删除WAF规则分组 %d 中的规则集 %d",
"waf_rule_set@log_sort_rule_sets": "修改WAF规则分组 %d 中的规则集排序",
"waf_rule_set@log_update_rule_set": "修改WAF规则集 %d 基本信息",
"waf_rule_set@log_update_rule_set_is_on": "修改WAF规则集 %d 开启状态",
})
}

View File

@@ -0,0 +1 @@
After change the messages, remember to run 'langs generate' to generate Go codes.

View File

@@ -0,0 +1,5 @@
{
"log_create_acme_provider_account": "创建ACME服务商账号 %d",
"log_delete_acme_provider_account": "删除ACME服务商账号 %d",
"log_update_acme_provider_account": "修改ACME服务商账号 %d"
}

View File

@@ -0,0 +1,6 @@
{
"log_create_acme_task": "创建证书申请任务 %d",
"log_update_acme_task": "修改证书申请任务 %d",
"log_delete_acme_task": "删除证书申请任务 %d",
"log_run_acme_task": "执行ACME任务 %d"
}

View File

@@ -0,0 +1,5 @@
{
"log_create_acme_user": "创建ACME用户 %d",
"log_delete_acme_user": "删除ACME用户 %d",
"log_update_acme_user": "修改ACME用户 %d"
}

View File

@@ -0,0 +1,5 @@
{
"log_delete_ad_network": "删除高防IP线路",
"log_update_ad_network": "修改高防IP线路 %d",
"log_create_ad_network": "创建高防IP线路 %d"
}

View File

@@ -0,0 +1,5 @@
{
"log_create_ad_package": "创建高防产品 %d",
"log_delete_ad_package": "删除高防产品 %d",
"log_update_ad_package": "修改高防产品 %d"
}

View File

@@ -0,0 +1,5 @@
{
"log_delete_ad_package_instance": "删除高防实例 %d",
"log_update_ad_package_instance": "修改高防实例 %d",
"log_create_ad_package_instance": "创建高防实例 %d"
}

View File

@@ -0,0 +1,5 @@
{
"log_create_ad_package_period": "创建高防IP实例有效期 %d",
"log_delete_ad_package_period": "删除高防IP实例有效期选项 %d",
"log_update_ad_package_period": "修改高防IP实例有效期选项 %d"
}

View File

@@ -0,0 +1,4 @@
{
"log_create_ad_package_price": "为用户 %d 创建高防实例:%d有效期%d数量%d",
"log_update_ad_package_price": "修改高防产品 %d 有效期 %d 的价格"
}

View File

@@ -0,0 +1,5 @@
{
"log_create_admin": "创建系统用户 %d",
"log_delete_admin": "删除系统用户 %d",
"log_update_admin": "修改系统用户 %d"
}

View File

@@ -0,0 +1,18 @@
{
"menu_setting_basic": "基础设置",
"menu_setting_dns": "DNS设置",
"menu_setting_health_check": "健康检查",
"menu_setting_cache_policy": "缓存策略",
"menu_setting_waf_policy": "WAF策略",
"menu_setting_webp": "WebP",
"menu_setting_metrics": "统计指标",
"menu_setting_ddos_protection": "DDoS防护",
"menu_setting_security_policy": "网络安全",
"menu_setting_cache": "缓存设置",
"system": "系统",
"canceled": "已取消",
"server_error": "服务器出了点小问题,请联系技术人员处理。",
"log_system_error": "系统发生错误:%s"
}

View File

@@ -0,0 +1,9 @@
{
"ui_overview": "概况",
"ui_waf": "WAF",
"ui_dns": "DNS",
"ui_user": "用户",
"ui_events": "事件",
"disk_usage_warning": "当前服务器磁盘空间不足,请立即扩充容量,文件路径:%s已使用%dG已使用比例%.2f%%,仅剩余空间:%.2f%%。<br/>如果是因为本机数据库数据过多,你可以:<a href=\"/settings/database/clean\">[清理访问日志]</a> &nbsp; &nbsp; <a href=\"${lang.system@home_page}/docs/APINode/QA.md\" target=\"_blank\">[调整数据库binlog设置]</a>"
}

View File

@@ -0,0 +1,8 @@
{
"log_system_error": "登录时发生系统错误:%s",
"log_failed": "登录失败,用户名:%s",
"log_success": "成功登录系统,用户名:%s",
"log_otp_verified_success": "成功通过OTP验证登录系统",
"log_update_login": "修改登录设置"
}

View File

@@ -0,0 +1,72 @@
{
"dashboard": "数据看板",
"servers": "网站列表",
"server_access_logs": "访问日志",
"server_groups": "网站分组",
"server_certs": "证书管理",
"server_traffic_stats": "用量统计",
"server_cache_policies": "缓存策略",
"server_purge_fetch_caches": "刷新预热",
"server_waf_policies": "WAF策略",
"server_ip_lists": "IP名单",
"server_access_log_policies": "日志策略",
"server_metrics": "统计指标",
"server_scripts": "脚本库",
"server_global_settings": "通用设置",
"nodes": "边缘节点",
"node_clusters": "集群列表",
"node_logs": "节点日志",
"node_ip_list": "节点IP",
"node_regions": "区域设置",
"node_ssh_grants": "节点SSH",
"node_distributed_monitors": "区域监控",
"node_anti_ddos_products": "高防IP",
"dns": "域名解析",
"dns_clusters": "集群列表",
"dns_providers": "DNS服务商",
"dns_issues": "问题修复",
"ns": "智能DNS",
"ns_domains": "域名管理",
"ns_domain_groups": "域名分组",
"ns_domain_batch_operations": "批量操作",
"ns_clusters": "集群管理",
"ns_routes": "线路管理",
"ns_settings": "全局配置",
"ns_user_plans": "用户套餐",
"ns_plans": "套餐设置",
"ns_access_logs": "访问日志",
"ns_node_logs": "运行日志",
"ns_resolve_test": "解析测试",
"users": "平台用户",
"user_list": "用户列表",
"user_settings": "用户设置",
"finance": "财务管理",
"finance_bills": "费用账单",
"finance_accounts": "用户账户",
"finance_logs": "收支明细",
"finance_orders": "订单管理",
"finance_income": "统计报表",
"finance_fee": "计费设置",
"finance_packages": "流量包",
"plans": "套餐管理",
"plan_user_plans": "已购套餐",
"tickets": "工单系统",
"ticket_category": "分类",
"admins": "系统用户",
"admin_recipients": "通知媒介",
"logs": "日志审计",
"settings": "系统设置",
"setting_basic_settings": "基础设置",
"setting_advanced_settings": "高级设置",
"setting_authority": "商业版本"
}

View File

@@ -0,0 +1,3 @@
{
"log_update_profile": "修改个人资料"
}

View File

@@ -0,0 +1,3 @@
{
"log_update_security_settings": "修改管理界面安全设置"
}

View File

@@ -0,0 +1,4 @@
{
"log_update_server_http_settings": "修改管理界面的HTTP设置",
"log_update_server_https_settings": "修改管理界面的HTTPS设置"
}

View File

@@ -0,0 +1,20 @@
{
"tab_admin_server": "Web服务",
"tab_admin_ui": "管理界面设置",
"tab_user_ui": "用户界面设置",
"tab_admin_security_settings": "安全设置",
"tab_updates": "检查更新",
"tab_profile": "个人资料",
"tab_login": "登录设置",
"tab_database": "数据库",
"tab_api_nodes": "API节点",
"tab_user_nodes": "用户节点",
"tab_monitor_nodes": "监控节点",
"tab_ip_library": "IP库",
"tab_client_operation_systems": "操作系统库",
"tab_client_browsers": "浏览器库",
"tab_authority": "商业版认证",
"tab_access_log_databases": "日志数据库",
"tab_transfer": "迁移",
"tab_backup": "备份"
}

View File

@@ -0,0 +1,6 @@
{
"default_product_name": "GoEdge",
"default_system_name": "GoEdge管理员系统",
"log_update_ui_settings": "修改管理界面设置"
}

View File

@@ -0,0 +1,5 @@
{
"log_ignore_version": "忽略升级版本 %s",
"log_reset_ignore_version": "重置忽略升级版本",
"log_update_check_settings": "修改检查更新设置"
}

View File

@@ -0,0 +1,5 @@
{
"log_create_api_node": "创建API节点 %d",
"log_update_api_node": "修改API节点 %d",
"log_delete_api_node": "删除API节点 %d"
}

View File

@@ -0,0 +1,4 @@
{
"log_update_client_browser": "修改浏览器信息 %d",
"log_create_browser": "创建浏览器信息 %s"
}

View File

@@ -0,0 +1,4 @@
{
"log_update_client_system": "修改操作系统信息 %d",
"log_create_system": "创建操作系统信息 %s"
}

View File

@@ -0,0 +1,6 @@
{
"log_update_clean_days": "修改数据库自动清理设置",
"log_delete_table": "删除数据表 %s",
"log_truncate_table": "清空数据表 %s 数据",
"log_update_api_node_database_config": "修改API节点数据库设置"
}

View File

@@ -0,0 +1,9 @@
{
"tab_nodes": "数据库节点",
"log_create_db_node": "创建数据库节点 %d",
"log_delete_db_node": "删除数据库节点 %d",
"log_update_db_node": "修改数据库节点 %d",
"log_delete_table": "删除数据库节点 %d 数据表 %s",
"log_truncate_table": "清空数据库节点 %d 数据表 %s 数据"
}

View File

@@ -0,0 +1,4 @@
{
"log_update_cluster_ddos_protection": "修改集群 %d 的DDOS防护设置",
"log_update_node_ddos_protection": "修改节点 %d 的DDOS防护设置"
}

View File

@@ -0,0 +1,10 @@
{
"log_update_cluster_dns": "修改集群 %d DNS设置",
"log_sync_domain": "同步DNS域名数据 %d",
"log_sync_cluster": "同步集群 %d 的DNS设置",
"log_create_domain": "添加管理域名到DNS服务商 %d",
"log_delete_domain": "从DNS服务商中删除域名 %d",
"log_recover_domain": "从DNS服务商中恢复域名 %d",
"log_update_domain": "修改DNS服务商域名 %d",
"log_update_node_dns": "修改节点 %d 的DNS设置"
}

View File

@@ -0,0 +1,5 @@
{
"log_create_dns_provider": "创建DNS服务商 %d",
"log_delete_dns_provider": "删除DNS服务商 %d",
"log_update_dns_provider": "修改DNS服务商 %d"
}

View File

@@ -0,0 +1,4 @@
{
"log_delete_dns_task": "删除DNS同步任务 %d",
"log_delete_all_dns_tasks": "删除所有DNS同步任务"
}

View File

@@ -0,0 +1,4 @@
{
"log_bill_generate_manually": "手动生成上个月 %s 账单",
"log_update_user_order_config": "修改订单设置"
}

View File

@@ -0,0 +1,3 @@
{
"log_update_fee_setting": "修改默认计费方式"
}

View File

@@ -0,0 +1,6 @@
{
"log_create_http_access_log_policy": "创建访问日志策略 %d",
"log_delete_http_access_log_policy": "删除访问日志策略 %d",
"log_test_http_access_log_policy": "测试向访问日志策略 %d 写入数据",
"log_update_http_access_log_policy": "修改访问日志策略 %d"
}

View File

@@ -0,0 +1,4 @@
{
"log_create_http_auth_policy": "创建HTTP鉴权 %d",
"log_update_http_auth_policy": "修改HTTP鉴权 %d"
}

Some files were not shown because too many files have changed in this diff Show More