Compare commits

...

408 Commits

Author SHA1 Message Date
刘祥超
3eaecf2ca0 修复节点列表中百分号显示 2022-04-25 09:24:38 +08:00
刘祥超
07182639f3 优化一个界面显示 2022-04-24 20:13:21 +08:00
刘祥超
046628eda4 管理界面设置中“是否显示底部开源信息”选项变更时即使更新界面底部显示 2022-04-24 18:05:25 +08:00
刘祥超
8f0054b2b0 更新components.js 2022-04-24 15:24:15 +08:00
刘祥超
33f86a7730 健康检查默认只做基础的请求 2022-04-23 13:26:34 +08:00
刘祥超
2487c69136 多个API节点时选择一个作为主节点 2022-04-23 12:32:13 +08:00
刘祥超
492d24f28a 修改一个搜索示例(从http改为https) 2022-04-23 09:09:00 +08:00
刘祥超
9a355da75c 集群概要信息中增加系统服务状态 2022-04-22 22:04:36 +08:00
刘祥超
8bae065572 手动健康检查增加超时时间为60s 2022-04-22 20:56:49 +08:00
刘祥超
efebb5a869 删除不需要的文件 2022-04-22 16:52:17 +08:00
刘祥超
32cc745a85 删除不需要的文件 2022-04-22 16:49:34 +08:00
刘祥超
42bfe3ffac 删除不必要的文件 2022-04-22 16:05:37 +08:00
刘祥超
de3a5f87cf 删除不需要的文件 2022-04-22 15:58:15 +08:00
刘祥超
bd1add6dea 优化代码 2022-04-22 15:58:07 +08:00
刘祥超
d275cc6a8b 修改一处颜色显示 2022-04-22 15:15:24 +08:00
刘祥超
556b5bd2e9 优化代码 2022-04-22 10:38:03 +08:00
刘祥超
e26cf2a19d 优化代码 2022-04-22 09:58:25 +08:00
刘祥超
94f5f1faf6 节点列表页CPU和内容用量增加百分号% 2022-04-22 09:22:15 +08:00
刘祥超
fb3c58bd60 修复IPBox只能显示昨日日志的Bug 2022-04-22 09:15:08 +08:00
刘祥超
e50c918e4d 优化界面显示 2022-04-22 09:14:48 +08:00
刘祥超
eb3ff3369c 增加WAF日志配置/WAF策略中之日志中增加分表查询 2022-04-21 19:45:25 +08:00
刘祥超
a07a08991f 更新components.js 2022-04-21 18:38:47 +08:00
刘祥超
6fcc8f3401 访问日志列表显示WAF相关信息 2022-04-21 16:20:03 +08:00
刘祥超
36b38c6da4 IP列表增加名单类型筛选 2022-04-21 15:09:24 +08:00
刘祥超
7f2f86b7ea 通过IP看访问日志时优先查询IP被加入名单的时间 2022-04-21 09:42:25 +08:00
刘祥超
68514bc7ed 修复访问日志耗时可能显示NaN的Bug 2022-04-21 09:41:59 +08:00
刘祥超
f0dfab536c 修复一个日志提示标签错误 2022-04-19 19:55:03 +08:00
刘祥超
3b75c9999e 自动将端口加入到本地防火墙 2022-04-19 19:51:38 +08:00
刘祥超
084ddf4f5d 更新components.js 2022-04-19 19:51:29 +08:00
刘祥超
6770b1fa72 域名解析增加GoDaddy DNS(目前仅商业版可用) 2022-04-19 16:33:51 +08:00
刘祥超
118d39ed83 修复sendfile功能显示错误 2022-04-19 14:19:06 +08:00
刘祥超
c24df4f876 证书在上传时检查有效期 2022-04-19 11:14:40 +08:00
刘祥超
0ae9c25d6f IP名单增加区域和ISP显示 2022-04-19 10:57:47 +08:00
刘祥超
f99b2a9def 优化界面文字 2022-04-18 21:05:01 +08:00
刘祥超
caf9eeae2e 集群DNS子域名可以随机生成 2022-04-18 18:19:30 +08:00
刘祥超
6afecb5708 单个服务切换集群时可以选择是否保留节点上的配置/总是可以切换集群,不再受所属用户的影响 2022-04-18 17:18:39 +08:00
刘祥超
739d32e2e0 修复访问日志分表为-1时无法切换的问题 2022-04-18 14:32:20 +08:00
刘祥超
4e601298b0 优化界面 2022-04-17 20:49:40 +08:00
刘祥超
74cffced3f 优化访问日志弹窗中的发送字节 2022-04-17 20:22:34 +08:00
刘祥超
f8cc76be35 服务访问日志也支持分表查询 2022-04-17 17:08:39 +08:00
刘祥超
dced2dd418 修复编译脚本无法生成components.js的问题 2022-04-17 16:40:29 +08:00
刘祥超
418fe97c67 Update components.src.js 2022-04-17 16:26:53 +08:00
刘祥超
1440f9b721 优化IPBox访问日志查询 2022-04-17 16:23:52 +08:00
刘祥超
9504c086ea 访问日志可以使用分表查询 2022-04-17 16:18:43 +08:00
刘祥超
3a15d6475c 优化看板界面 2022-04-16 22:23:58 +08:00
刘祥超
18f3a00c0f 修复看板中统计数据可能不显示的问题 2022-04-16 22:22:18 +08:00
刘祥超
5138bbe947 服务列表增加下行带宽 2022-04-15 12:15:09 +08:00
刘祥超
a309feb516 修复服务列表页面中待修复的日志数量显示错误的Bug 2022-04-15 09:58:30 +08:00
刘祥超
e9c172c261 服务列表--选择分组--增加"[未分组]"选项 2022-04-14 16:59:45 +08:00
刘祥超
6ac6842a8d 优化域名设置交互 2022-04-14 16:58:57 +08:00
刘祥超
71dc53ac1f 优化界面/将“反向代理”菜单改为“源站”,将“Web设置”改为“静态分发” 2022-04-14 15:58:39 +08:00
刘祥超
4450fa1379 优化SSH地址自动填充 2022-04-10 16:56:31 +08:00
刘祥超
af0f9489c7 优化本地日志 2022-04-10 15:58:54 +08:00
刘祥超
75b2b2bdb2 修改SSH时自动填入SSH主机地址/节点设置--SSH设置增加连接测试 2022-04-09 21:44:34 +08:00
刘祥超
2d8224fd12 服务设置菜单和路由规则菜单支持拦截函数 2022-04-08 22:09:45 +08:00
刘祥超
c3aea3ba72 支持使用uglifyjs压缩js组件文件 2022-04-08 21:24:54 +08:00
刘祥超
ad1ff29ed2 Update components.js 2022-04-08 20:49:33 +08:00
刘祥超
9887bd99c7 缓存条件增加暂停/恢复功能;缓存条件修改后自动保存 2022-04-08 20:49:31 +08:00
刘祥超
9af9d0192d 判断修改节点级别权限 2022-04-07 21:37:13 +08:00
刘祥超
62933cf637 优化界面 2022-04-07 19:53:10 +08:00
刘祥超
ee0837571d 增加当日统计接口 2022-04-07 19:46:45 +08:00
刘祥超
c2b12419c7 优化代码 2022-04-07 18:53:56 +08:00
刘祥超
991aed7e8c Update components.js 2022-04-07 18:34:34 +08:00
刘祥超
528d5fc5a9 增加节点列表 2022-04-07 18:31:21 +08:00
刘祥超
93569227f3 服务分组开启访问日志设置时,在服务访问日志设置界面有对应提示 2022-04-07 16:19:44 +08:00
刘祥超
46812f9e42 优化界面/删除不需要的文件 2022-04-07 10:21:38 +08:00
刘祥超
61f043319d 对访问日志中的Cookie进行排序显示 2022-04-05 15:58:35 +08:00
刘祥超
77140d01a0 优化访问日志Header显示 2022-04-05 15:43:49 +08:00
刘祥超
beff326001 访问日志中的响应Header和请求Header排序后显示 2022-04-05 15:38:22 +08:00
刘祥超
f2841b7328 优化界面 2022-04-05 15:23:48 +08:00
刘祥超
2ba63b2484 优化界面 2022-04-05 11:33:29 +08:00
刘祥超
f054241a73 优化界面 2022-04-04 19:47:57 +08:00
刘祥超
b5007b9195 缓存文件实现Sendfile 2022-04-04 19:46:12 +08:00
刘祥超
9deea64097 商业版支持L2节点 2022-04-04 12:08:18 +08:00
刘祥超
105777da21 优化界面 2022-04-02 16:33:08 +08:00
刘祥超
e13743dfa8 优化界面 2022-04-02 16:28:22 +08:00
刘祥超
35bdd3a41a 优化界面 2022-04-02 11:09:04 +08:00
刘祥超
464d757800 README增加功能介绍 2022-04-02 10:56:14 +08:00
刘祥超
8076fcd148 集群可以单独设置WebP策略 2022-04-01 16:42:08 +08:00
刘祥超
53b1f07601 优化证书上传和修改界面 2022-04-01 11:12:42 +08:00
刘祥超
2626d78835 只有满足缓存条件的图片内容才会被转换 2022-03-31 16:22:14 +08:00
刘祥超
3442ddfae2 优化代码 2022-03-31 15:21:02 +08:00
刘祥超
5ed863c5ec 特殊页面改为自定义页面 2022-03-31 14:53:07 +08:00
刘祥超
00f6387182 更新README中链接 2022-03-30 22:27:05 +08:00
刘祥超
ca70e8f0c8 全站防护改为5秒盾(商业版可用) 2022-03-30 11:37:55 +08:00
刘祥超
081e95b293 可以用域名搜索DNS账号 2022-03-30 11:15:32 +08:00
刘祥超
1282e610ba DNSPod区域改为中国站和国际站 2022-03-30 10:59:42 +08:00
刘祥超
287c52cef4 IP列表可以使用级别筛选 2022-03-30 09:41:14 +08:00
刘祥超
dd421fec80 支持DNSPod国际版 2022-03-30 09:12:16 +08:00
刘祥超
9b10bdaf0d Update components.js 2022-03-29 21:25:33 +08:00
刘祥超
4b916829a4 商业版增加UAM功能 2022-03-29 21:25:30 +08:00
刘祥超
b33e9d9187 修复服务统计--流量统计--即时的tooltip错误 2022-03-29 10:29:49 +08:00
刘祥超
9d0d323b50 数据库清理界面显示行数 2022-03-28 17:31:37 +08:00
刘祥超
2872569ce0 可以自行设定指标数据保留时间 2022-03-28 09:37:48 +08:00
刘祥超
cd2212b754 优化界面 2022-03-27 17:23:02 +08:00
刘祥超
eb6525b8d9 优化看板打开速度/删除一些不必要的文件 2022-03-27 16:39:20 +08:00
刘祥超
10319a1e3d 优化界面 2022-03-26 22:10:34 +08:00
刘祥超
6bcfea9372 支持路由定义请求脚本 2022-03-26 22:04:26 +08:00
刘祥超
4699d1c2ee 管理界面设置和用户界面设置可以修改时区 2022-03-26 10:23:03 +08:00
刘祥超
68f0b2efc3 反向代理要求必须添加源站 2022-03-25 14:42:28 +08:00
刘祥超
b2af8e196b 优化界面 2022-03-25 14:10:40 +08:00
刘祥超
2e626a915f 优化文字提示 2022-03-25 09:32:46 +08:00
刘祥超
0127050d89 修复无法修复单个日志的Bug 2022-03-25 09:32:35 +08:00
刘祥超
a25938022f 可以修复单页或者全部服务日志 2022-03-23 17:31:53 +08:00
刘祥超
c47b2973b0 优化文字 2022-03-23 16:33:20 +08:00
刘祥超
efd3934470 优化界面 2022-03-23 16:29:56 +08:00
刘祥超
fb7da4e07e 版本号改为0.4.7 2022-03-23 14:45:10 +08:00
刘祥超
d7d209f694 更改版本为0.4.6 2022-03-23 10:03:25 +08:00
刘祥超
83a88299c2 Update components.js 2022-03-21 08:23:34 +08:00
刘祥超
bfae3b86cc Age改为在缓存中的已存活时间 2022-03-20 21:18:51 +08:00
刘祥超
027ee4d336 GRPC通讯支持gzip压缩 2022-03-20 11:28:38 +08:00
刘祥超
b07a65c879 更新相关库 2022-03-20 10:46:09 +08:00
刘祥超
284b2762f5 优化界面 2022-03-18 20:21:00 +08:00
刘祥超
5ba6e3b332 改进文字 2022-03-18 17:02:36 +08:00
刘祥超
8b0e3d960a Update components.js 2022-03-17 19:59:58 +08:00
刘祥超
fba2953974 优化界面 2022-03-17 19:57:41 +08:00
刘祥超
a36b843eff 优化指标统计图表数字展示 2022-03-17 16:01:47 +08:00
刘祥超
0735bc2d8a 源站支持自定义回源主机名 2022-03-17 15:48:08 +08:00
刘祥超
69d6fd645b 修复访问日志按小时搜索无法上翻页的Bug 2022-03-17 12:31:45 +08:00
刘祥超
039b11d434 增加置顶集群功能 2022-03-17 11:12:24 +08:00
刘祥超
485581f680 优化界面 2022-03-17 10:36:30 +08:00
刘祥超
a0c2006e24 Update components.js 2022-03-16 22:48:04 +08:00
刘祥超
7a288c360d IPSet支持IPv6 2022-03-16 20:48:35 +08:00
刘祥超
597d39f651 优化文字 2022-03-16 17:07:46 +08:00
刘祥超
7d27f64b8a 节点可以单独设置缓存目录 2022-03-16 15:24:11 +08:00
刘祥超
bd280bdc53 改进文字 2022-03-14 19:25:19 +08:00
刘祥超
018429866e 优化文字提示 2022-03-14 16:24:09 +08:00
刘祥超
78ad6526b8 缓存策略可以使用存储类型筛选 2022-03-14 15:42:10 +08:00
刘祥超
8c6e960db7 实现回源跟随功能 2022-03-14 15:07:49 +08:00
刘祥超
46d38ff8c0 格式化部分图表中的数字 2022-03-14 12:04:46 +08:00
刘祥超
db6fe469ad 访问日志慢的时候增加指定域名查询建议 2022-03-11 20:35:42 +08:00
刘祥超
5a7630bcd1 增加证书OCSP错误日志管理 2022-03-11 20:27:45 +08:00
刘祥超
099b57169d 上传js 2022-03-10 21:32:56 +08:00
刘祥超
e22dfd3314 优化界面 2022-03-10 15:47:25 +08:00
刘祥超
be71992930 增加OCSP Stapling功能 2022-03-10 11:55:09 +08:00
刘祥超
37433178cb 支持使用小时筛选访问日志 2022-03-09 11:00:49 +08:00
刘祥超
14499a4564 增加对访问日志自动分表配置 2022-03-09 10:01:52 +08:00
刘祥超
0355777f59 优化文字 2022-03-06 19:48:50 +08:00
刘祥超
a55834c78d 优化界面 2022-03-04 17:00:01 +08:00
刘祥超
79bbb47459 更新TeaGo 2022-03-04 15:01:26 +08:00
刘祥超
d3bcf0605b 升级相关依赖库 2022-03-04 12:35:28 +08:00
刘祥超
19a02f3a40 实现基础的区间内容缓存(206 partial content) 2022-03-03 19:32:11 +08:00
刘祥超
db20e9308a 修复选择集群弹窗页面可能只显示前6个集群的Bug 2022-02-28 14:41:55 +08:00
刘祥超
b3f9f28554 优化界面显示 2022-02-27 21:20:49 +08:00
刘祥超
ea5bd3f346 可以在管理界面设置里设置默认每页显示数 2022-02-24 20:52:47 +08:00
刘祥超
5c13797639 缓存可以设置是否使用系统默认设置 2022-02-24 20:39:09 +08:00
刘祥超
f147905532 增加是否同步写入压缩缓存设置 2022-02-24 20:12:15 +08:00
刘祥超
a73314c12a 修改版本为0.4.5 2022-02-24 19:25:12 +08:00
刘祥超
619934a275 修复访问日志XSS漏洞 2022-02-23 17:34:54 +08:00
刘祥超
5fe1384e55 修改版本为0.4.4 2022-02-23 14:48:33 +08:00
刘祥超
9af2c4a18a 修改版本为v0.4.3 2022-02-21 18:32:03 +08:00
刘祥超
8f4c56b24a 更改版本为v0.4.2 2022-02-21 17:52:36 +08:00
刘祥超
5eb0f65934 更新截图 2022-02-21 11:53:51 +08:00
刘祥超
4ed4f165ac URL跳转可以设置是否保留参数 2022-02-20 09:17:30 +08:00
刘祥超
eb8419fda0 Update components.js 2022-02-17 17:24:23 +08:00
刘祥超
b4a7ee0e89 域名支持--(连续的连字符) 2022-02-17 17:24:14 +08:00
刘祥超
83ad1bc529 优化界面 2022-02-15 14:54:45 +08:00
刘祥超
56948d3035 优化菜单中badge显示 2022-02-14 08:58:31 +08:00
刘祥超
e71e5ad57e 支持默认价格设置 2022-01-23 20:16:02 +08:00
刘祥超
e06d1fa5b0 实现带宽计费套餐 2022-01-23 19:16:22 +08:00
刘祥超
460439f6bd 修复服务访问日志不能使用集群、节点筛选的Bug 2022-01-20 16:28:43 +08:00
刘祥超
4b4072a47e 优化界面 2022-01-20 15:53:46 +08:00
刘祥超
65d19d92e9 增加API方法调用耗时统计 2022-01-19 16:53:38 +08:00
刘祥超
79e52d1c6e 优化demo模式进入命令 2022-01-19 15:58:54 +08:00
刘祥超
59963ee7b9 修复检查更新配置不起作用的Bug 2022-01-18 19:29:42 +08:00
刘祥超
88273b1c9b 修改版本为v0.4.1 2022-01-17 10:53:28 +08:00
刘祥超
bc1eb994f9 Update components.js 2022-01-16 20:04:11 +08:00
刘祥超
136f0fd4bd 源站支持客户端证书 2022-01-16 19:51:26 +08:00
刘祥超
021dc13ce9 CAPTCHA增加多个选项 2022-01-16 16:54:20 +08:00
刘祥超
925b71489d Update components.js 2022-01-14 10:46:02 +08:00
刘祥超
580ce9f8f5 可以在IP名单、访问日志中跳到对应的WAF规则集 2022-01-14 10:45:58 +08:00
刘祥超
2b3d8c062d 优化界面 2022-01-13 15:05:57 +08:00
刘祥超
41858e091a Update components.js 2022-01-13 10:03:54 +08:00
刘祥超
5b7eaf08ae 实现open file cache 2022-01-12 21:09:11 +08:00
刘祥超
7362722adb 优化代码 2022-01-11 16:02:27 +08:00
刘祥超
8198ea8819 可以使用集群搜索WAF策略、缓存策略 2022-01-11 15:46:47 +08:00
刘祥超
cb615f4cbc Update components.js 2022-01-11 15:28:50 +08:00
刘祥超
6623fd8362 优化交互 2022-01-11 15:26:43 +08:00
刘祥超
7f8be85116 运行日志可以使用集群、节点筛选 2022-01-11 14:59:19 +08:00
刘祥超
c61381441c 访问日志可以使用集群和节点搜索 2022-01-11 12:04:03 +08:00
刘祥超
b3cecdfea2 实现自动SYN Flood防护 2022-01-10 19:54:29 +08:00
刘祥超
66f582df58 WAF规则增加备注信息/其他界面优化 2022-01-10 10:28:23 +08:00
刘祥超
7f6f7e11ce 生成components.js 2022-01-09 20:13:18 +08:00
刘祥超
0bdda313da WAF策略增加是否使用本地防火墙设置 2022-01-09 17:05:51 +08:00
刘祥超
d5e851cff7 优化界面 2022-01-09 10:47:03 +08:00
刘祥超
92f1ec13f9 优化界面 2022-01-09 10:43:37 +08:00
刘祥超
5376006754 优化文字提示 2022-01-08 16:49:46 +08:00
刘祥超
361979411e IP名单增加未读数、按未读筛选等操作 2022-01-08 16:48:45 +08:00
刘祥超
3981308083 优化代码 2022-01-06 11:13:36 +08:00
刘祥超
facd5e14cc 改进文字 2022-01-05 20:13:22 +08:00
刘祥超
ecce92c528 Update components.js 2022-01-05 15:54:22 +08:00
刘祥超
7696940989 用户列表可以使用待审核、关键词搜索 2022-01-05 11:12:28 +08:00
刘祥超
941ff46c2c 实现用户注册/审核功能 2022-01-05 10:45:28 +08:00
刘祥超
94036073de 优化请求脚本配置交互 2022-01-03 21:48:48 +08:00
刘祥超
13216f481c 尝试自动在firewalld中开放端口 2022-01-03 16:28:27 +08:00
刘祥超
eb35df8720 改进服务访问日志、设置页在手机下的显示 2022-01-03 14:50:11 +08:00
刘祥超
bf500fe1a4 增加格式化数字函数 2022-01-03 12:04:09 +08:00
刘祥超
c173e86e62 创建服务时默认选中统计 2022-01-03 11:27:01 +08:00
刘祥超
d4cb148272 WAF动作中各个超时/有效秒数最大值从10位改成9位 2022-01-03 11:17:33 +08:00
刘祥超
ffc3f8544e 节点运行日志增加标签筛选 2022-01-03 11:08:59 +08:00
刘祥超
b326bfe63a 优化文字 2021-12-31 19:45:14 +08:00
刘祥超
12f3f47ef9 实现初版边缘脚本 2021-12-31 15:20:59 +08:00
刘祥超
45734747c1 修改版本为0.4.0 2021-12-31 15:19:44 +08:00
刘祥超
37933d814c 修改版本号0.3.8 2021-12-31 11:38:55 +08:00
刘祥超
5b26287264 修复路由规则中不能设置响应Header的Bug 2021-12-28 20:12:29 +08:00
刘祥超
bac20b1d1f 自动检查更新被取消时,同时重置已发现的最新版本信息 2021-12-22 10:02:06 +08:00
刘祥超
904c641992 删除不必要的文件 2021-12-22 10:01:35 +08:00
刘祥超
36004bfd94 优化代码 2021-12-21 15:41:43 +08:00
刘祥超
0fcba7e90c 增加自动检查系统更新设置 2021-12-21 15:18:11 +08:00
刘祥超
2e9182933e 修改版本号为0.4.0 2021-12-20 20:01:55 +08:00
刘祥超
e3674fa2c1 优化文字 2021-12-20 09:35:36 +08:00
刘祥超
a14cbe1319 访问日志限制字段 2021-12-20 09:35:25 +08:00
刘祥超
c03c35de88 访问日志限制字段 2021-12-19 20:39:18 +08:00
刘祥超
dc6c649af8 优化文字提示等 2021-12-19 18:56:09 +08:00
刘祥超
b0d2bdb0ba 修复HSTS无法设置有效期的Bug 2021-12-18 17:23:55 +08:00
刘祥超
9239dd9a8b 调整单次通知更新任务的数量为500(原来200) 2021-12-17 14:25:40 +08:00
刘祥超
07a368a0bd 修改相关域名、文字等 2021-12-17 14:17:48 +08:00
刘祥超
b8d6d1c249 访问日志中缓存状态增加STALE 2021-12-17 11:55:12 +08:00
刘祥超
0f1d6a1ad2 实现stale cache配置 2021-12-16 17:27:09 +08:00
刘祥超
37c6928ffc 节点IP显示原始IP(如果已经切换到备用IP的话) 2021-12-16 15:18:21 +08:00
刘祥超
7478b6dbe0 优化代码 2021-12-15 20:46:42 +08:00
刘祥超
e3cd6e1441 增加在线检查最新版本功能 2021-12-15 20:13:10 +08:00
刘祥超
8081d968b6 Update components.js 2021-12-15 09:56:18 +08:00
刘祥超
a226eee6ef HTTP Header:实现请求方法、域名、状态码等限制,实现内容替换功能 2021-12-14 21:26:32 +08:00
刘祥超
8b5a21e593 访问日志查询过慢的时候,提示建议增加新的日志节点 2021-12-14 15:50:21 +08:00
刘祥超
2c71f6c6be 实现访问日志队列 2021-12-14 12:45:09 +08:00
刘祥超
1a50b01edf 优化代码 2021-12-13 10:53:49 +08:00
刘祥超
05f08eeb4c 优化坐标轴单位 2021-12-12 21:13:55 +08:00
刘祥超
fdc3ebb5c5 WAF添加规则:调整界面/增加正则表达式测试功能 2021-12-12 20:56:25 +08:00
刘祥超
8f06bccd48 WAF策略:可以修改分组代号/导入时可以根据名称合并/导出时可以导出停用的分组 2021-12-12 20:24:15 +08:00
刘祥超
16b4eb67d4 Update components.js 2021-12-12 17:14:46 +08:00
刘祥超
ffa545cb41 服务分组可以设置请求限制 2021-12-12 17:07:16 +08:00
刘祥超
f67454d51c 路由规则增加专属域名设置 2021-12-12 16:38:52 +08:00
刘祥超
8ae54c56db 增加排除扩展名条件 2021-12-12 16:14:56 +08:00
刘祥超
9a17adcc6f 请求条件增加不区分大小写选项 2021-12-12 16:11:25 +08:00
刘祥超
4dd6903025 优化界面 2021-12-12 11:58:16 +08:00
刘祥超
085770d0ad 实现请求连接数等限制/容量组件增加EB支持 2021-12-12 11:46:09 +08:00
刘祥超
fbaba7c37d 修复公共黑名单/白名单无法搜索的Bug 2021-12-10 11:12:49 +08:00
刘祥超
d5cea208d2 支持设置单节点最大线程数、单节点TCP最大连接数 2021-12-09 18:49:35 +08:00
刘祥超
36397adca4 修复服务统计图表颜色显示问题/Expires设置增加提示文字 2021-12-08 21:40:01 +08:00
刘祥超
4971e25d44 WAF看板最新拦截记录增加区域信息 2021-12-08 20:28:26 +08:00
刘祥超
150357441d 修复域名为空时点击审核页面为空的Bug 2021-12-08 20:20:10 +08:00
刘祥超
c4ee663285 重新生成components.js 2021-12-08 19:13:54 +08:00
刘祥超
8c95b4a9b9 多处访问日志增加单页显示条数选择 2021-12-08 19:13:34 +08:00
刘祥超
32ba919851 增加部分访问日志条数10->20 2021-12-08 17:49:30 +08:00
刘祥超
eb37345e85 可以在缓存条件里设置Expires Header 2021-12-08 17:41:12 +08:00
刘祥超
681e454917 修复编译时components.js可能没有更新的Bug 2021-12-08 10:03:27 +08:00
刘祥超
7c7b82dee4 优化地图标签 2021-12-08 09:37:46 +08:00
刘祥超
2ae47af8f0 增加批量增加节点IP接口 2021-12-07 18:22:46 +08:00
刘祥超
b72d91d0d4 访问日志实现记录和显示requestBody 2021-12-07 15:03:48 +08:00
刘祥超
56010e7203 缓存默认支持所有请求方法 2021-12-07 10:47:46 +08:00
刘祥超
f658698f7b 缓存支持请求方法设置 2021-12-07 10:45:49 +08:00
刘祥超
b708b9c6df 将缓存默认key改为${scheme}://${host}${requestPath}${isArgs}${args} 2021-12-07 10:04:50 +08:00
刘祥超
c2908e17fa SSH认证支持sudo 2021-12-06 19:24:30 +08:00
刘祥超
302daab824 服务看板增加区域地图 2021-12-06 09:16:23 +08:00
刘祥超
ec49f238d6 优化界面 2021-12-06 08:55:47 +08:00
刘祥超
3e43a5d866 上传components.js/优化地图样式 2021-12-05 20:59:07 +08:00
刘祥超
a99d5e68e9 商业版WAF看板增加地图 2021-12-05 19:38:14 +08:00
刘祥超
28514276ec 商业版首页增加地图/调低各个图表的高度,以便同时可以显示更多的图表 2021-12-05 18:59:20 +08:00
刘祥超
b611427c17 创建新服务时默认开启配置可以选择统计 2021-12-03 14:55:14 +08:00
刘祥超
ce7a4ead04 首页看板显示未审核的服务数、本周流量、昨日流量 2021-12-03 14:54:15 +08:00
刘祥超
5fe15a85fd 优化IPBox交互/优化TOA文字提示 2021-12-03 11:00:20 +08:00
刘祥超
ae412909f6 优化服务设置界面顶部菜单 2021-12-02 17:41:51 +08:00
刘祥超
c6ed579797 IPBox把IP加入黑名单可以选择过期时间/可以从已经添加的名单中删除/已经添加的名单中显示过期时间 2021-12-02 17:11:44 +08:00
刘祥超
b2a525268e WAF规则集中增加是否忽略局域网IP选项 2021-12-02 16:09:15 +08:00
刘祥超
eb78b4881c 多个提示页面增加请求ID 2021-12-02 14:45:51 +08:00
刘祥超
4ce6e5a9f6 访问日志弹窗中加入请求ID/优化Header添加/修改文字提示 2021-12-02 11:49:36 +08:00
刘祥超
15d7e75555 优化URL跳转文字提示 2021-12-02 10:39:22 +08:00
刘祥超
6453cc6ccc 缓存配置增加是否支持Cache-Control: max-age=... 2021-12-02 10:18:22 +08:00
刘祥超
d882a2eb63 缓存配置增加Age Header配置 2021-12-02 09:54:31 +08:00
刘祥超
065ac4aa25 增加generate.sh脚本用来生成文件 2021-12-02 09:46:46 +08:00
刘祥超
dea54fc55e 增加是否记录499选项 2021-12-01 21:13:15 +08:00
刘祥超
8f425bd9c7 当用户提交待审核域名时,给管理员发送消息 2021-12-01 17:19:50 +08:00
刘祥超
9d909d73b8 审核中服务增加提交审核时间/已通过域名标绿 2021-12-01 17:06:05 +08:00
刘祥超
b417d50a28 优化节点日志:可以批量设置服务错误日志为已修复等 2021-11-30 16:43:44 +08:00
刘祥超
aa93a2f702 优化编译脚本 2021-11-30 11:05:58 +08:00
刘祥超
ba7125e773 优化文字 2021-11-30 11:05:24 +08:00
刘祥超
46a7eaa4bb 商业版认证增加申请页面 2021-11-30 10:02:31 +08:00
刘祥超
887439a6fe 优化代码 2021-11-29 20:35:47 +08:00
刘祥超
0e2b07d06d 优化提示文字 2021-11-29 16:41:32 +08:00
刘祥超
b7d4bde11b 修改部分菜单名 2021-11-29 12:00:23 +08:00
刘祥超
9707360948 完善套餐 2021-11-28 20:11:48 +08:00
刘祥超
103a8eb092 修改版本号为0.3.7 2021-11-28 14:28:53 +08:00
刘祥超
dcba4f9376 节点同步不在图标上提示IP名单相关更新 2021-11-27 17:06:36 +08:00
刘祥超
12aaa6fcb1 更新生成的components.js 2021-11-27 17:06:03 +08:00
刘祥超
d8393481a4 修改域名变更时是否需要审核的初始状态 2021-11-25 12:10:23 +08:00
刘祥超
70d3202a2c 服务增加是否合并URL中的多余分隔符选项 2021-11-24 14:49:42 +08:00
刘祥超
c7f1bbc03d 版本号改为0.3.6 2021-11-24 14:03:56 +08:00
刘祥超
61a55cb3f4 优化命名 2021-11-24 11:58:01 +08:00
刘祥超
6a484af775 将版本修改为0.3.5.2 2021-11-24 10:29:59 +08:00
刘祥超
3074d41cf2 改进源站专属域名的文字提示和交互 2021-11-22 18:46:08 +08:00
刘祥超
54199058e3 修复服务无法创建的Bug 2021-11-22 14:34:32 +08:00
刘祥超
87a533791b 版本改为0.3.5.1 2021-11-22 14:34:20 +08:00
刘祥超
c3109bb2c6 编译时生成components.js 2021-11-22 12:08:53 +08:00
刘祥超
031cb836d2 安装时等API节点启动完毕后才进行下一步,避免因为未启动完整而导致的错误 2021-11-21 19:26:05 +08:00
刘祥超
aa0a9134cb 迁移后确认API节点界面可以跳转到安装界面 2021-11-21 19:25:42 +08:00
刘祥超
749eac74fe 当迁移了管理平台后,自动跳转到确认API配置页 2021-11-21 15:57:13 +08:00
刘祥超
ac39908737 优化删除IP时成功消息提示时间 2021-11-21 09:45:11 +08:00
刘祥超
bb8f4bf488 增加批量删除IP名单中的IP的功能 2021-11-21 09:43:14 +08:00
刘祥超
db0d157a74 修复时间输入组件时间戳总是多一秒的Bug 2021-11-21 08:44:03 +08:00
刘祥超
bd8e1bbe71 优化RPC客户端锁 2021-11-20 19:17:52 +08:00
刘祥超
7aba898cf5 优化文字 2021-11-20 19:17:16 +08:00
刘祥超
baf039755f 实现迁移辅助功能(系统设置 -- 高级设置 -- 迁移) 2021-11-20 18:58:58 +08:00
刘祥超
70977f7d80 IP地址“健康检查失败”阈值改为“健康检查结果” 2021-11-18 14:47:48 +08:00
刘祥超
884342d6af 节点IP阈值增加节点健康检查失败 2021-11-18 14:30:46 +08:00
刘祥超
411b0fb4c2 修复看板--事件中无法单条已读操作的Bug 2021-11-18 08:52:29 +08:00
刘祥超
8053fb2399 IP名单中显示已过期标签 2021-11-17 21:18:33 +08:00
刘祥超
ed42dcab9c IP名单增加是否只显示自动拦截名单选项 2021-11-17 20:25:31 +08:00
刘祥超
866b5b0f2f IP名单中增加搜索按钮 2021-11-17 20:15:37 +08:00
刘祥超
5834a1a0fa 增加全局查看、检索IP功能 2021-11-17 19:50:52 +08:00
刘祥超
8c6d845603 将公用IP名单默认改成全局名单,自动应用于所有服务 2021-11-17 16:14:37 +08:00
刘祥超
667f363f3c WAF block动作默认封锁范围为global 2021-11-16 19:17:08 +08:00
刘祥超
e209ff38d9 IP名单中显示IP创建时相关的服务、WAF策略等信息 2021-11-16 16:11:29 +08:00
刘祥超
ea915993b6 节点运行日志中显示相关服务链接 2021-11-15 16:51:39 +08:00
刘祥超
72d0468c6a IP名单中的IP增加添加日期显示 2021-11-15 11:31:54 +08:00
刘祥超
35ae13b1c3 优化缓存配置界面 2021-11-14 16:21:04 +08:00
刘祥超
1d9460f565 优化RPC连接 2021-11-10 22:22:27 +08:00
刘祥超
e9a3ed71b4 将带宽限制改为流量限制 2021-11-09 17:36:38 +08:00
刘祥超
e344e5b7e6 支持套餐相关操作 2021-11-09 15:36:18 +08:00
刘祥超
e814064403 编译时删除.js.map文件 2021-11-09 14:19:42 +08:00
刘祥超
f5aeb5cbcd 支持购买套餐/续费套餐/用户账户操作等 2021-11-08 20:52:02 +08:00
刘祥超
f41164b892 删除不需要的文件 2021-11-07 09:11:30 +08:00
刘祥超
03073c8364 删除不需要的文件 2021-11-07 08:42:11 +08:00
刘祥超
a359bff531 安装时自动检查服务器上安装的MySQL 2021-11-06 18:35:22 +08:00
刘祥超
3789ac6433 域名解析中可以删除和恢复某个域名 2021-11-06 16:23:38 +08:00
刘祥超
9f53f59f18 SSH登录支持Passphrase 2021-11-06 15:31:07 +08:00
刘祥超
521bd746e3 当证书被API节点或者用户节点使用时不允许删除/规范命名 2021-11-05 17:56:17 +08:00
刘祥超
3d8e43a42b 修改文字 2021-11-05 17:13:43 +08:00
刘祥超
9452e1852d 规范API命名 2021-11-05 15:34:48 +08:00
刘祥超
3920d24af6 只有一个可用的API节点时不允许删除 2021-11-05 15:29:37 +08:00
刘祥超
3af11e6ba8 修改版本号为0.3.5 2021-11-05 14:59:10 +08:00
刘祥超
7bcde46d49 修复缓存条件可能无法保存的Bug 2021-11-04 11:23:29 +08:00
刘祥超
0b73041718 支持info指令查询PID、版本号等信息 2021-11-04 11:13:54 +08:00
刘祥超
dcdc0cb8c1 修复无法设置缓存条件的Bug 2021-11-04 11:12:59 +08:00
刘祥超
ad6ac1aad6 优化安装程序 2021-11-01 21:09:52 +08:00
刘祥超
249dc6accd 修改版本为0.3.4 2021-11-01 10:45:41 +08:00
刘祥超
939e5999ca 优化细节 2021-10-30 22:33:56 +08:00
刘祥超
1f91e57d56 增加套餐相关代码 2021-10-29 14:02:01 +08:00
刘祥超
81e749dc60 支持gif转webp 2021-10-29 12:21:52 +08:00
刘祥超
241b2afda8 Update .gitignore 2021-10-26 20:43:59 +08:00
刘祥超
94cc29f227 删除不必要的文件 2021-10-26 20:43:47 +08:00
刘祥超
6d6659eee1 WAF增加显示网页动作 2021-10-25 19:40:28 +08:00
刘祥超
5220be0775 优化代码 2021-10-25 19:01:56 +08:00
刘祥超
07ebbf0863 WAF模板中有新的规则时,可以在界面上收到提醒并点击加入 2021-10-25 12:02:03 +08:00
刘祥超
b60c767fc5 创建网站服务时增加缓存、WAF、从上级代理中读取IP等选项 2021-10-25 09:06:23 +08:00
刘祥超
371c3b78c3 DNS记录名支持下划线 2021-10-25 09:05:26 +08:00
刘祥超
6a3aa219d5 优化HTTP客户端IP配置交互 2021-10-22 14:40:39 +08:00
刘祥超
df586ddfdd 优化代码 2021-10-22 13:57:04 +08:00
刘祥超
f3b2bbfec0 删除不需要的文件 2021-10-22 13:20:02 +08:00
刘祥超
3ea2114798 IP名单列表可以搜索关键词 2021-10-22 12:38:52 +08:00
刘祥超
809cf70e0e 可以在IP名单中搜索IP 2021-10-22 12:19:02 +08:00
刘祥超
390619535f 实现单个服务的带宽限制(商业版) 2021-10-21 17:10:08 +08:00
刘祥超
3392ac1fa8 优化交互 2021-10-20 09:59:08 +08:00
刘祥超
b09d94abbe 网站服务--访问日志增加服务链接 2021-10-19 16:32:08 +08:00
刘祥超
03ac01d21f 健康检查支持UserAgent和是否基础请求设置 2021-10-19 16:31:45 +08:00
刘祥超
12b1d785e5 增加防盗链规则参数 2021-10-19 11:38:56 +08:00
刘祥超
71c58e9d2e WAF阻止动作增加封锁范围 2021-10-18 20:09:06 +08:00
刘祥超
13c2997a52 内容压缩支持对已压缩内容重新压缩 2021-10-18 16:49:19 +08:00
刘祥超
47b840cac9 优化文字提示 2021-10-18 16:49:04 +08:00
刘祥超
3f7f243f50 默认的内容压缩算法从gzip改为brotli 2021-10-18 11:59:50 +08:00
刘祥超
1d1e83b18d 增加PURGE某个URL缓存功能 2021-10-17 20:22:57 +08:00
刘祥超
6f3a602c76 优化分页条数修改/在弹窗下不运行某些任务 2021-10-16 12:45:56 +08:00
刘祥超
afb7a4c6a7 修复选择集群弹窗无法修改分页条数的问题 2021-10-16 12:45:25 +08:00
刘祥超
d0c950d4ca 优化界面 2021-10-16 12:06:55 +08:00
刘祥超
63ee7d5211 支持任意域名通过CNAME访问服务(开启选项后)/可以重新生成服务CNAME 2021-10-16 12:03:21 +08:00
刘祥超
6b0d875745 优化界面显示 2021-10-16 10:29:14 +08:00
刘祥超
a47a9b9c0c 删除CodeMirror中没用的代码 2021-10-16 10:26:14 +08:00
刘祥超
27040a3e5c 节点运行日志增加本页已读 2021-10-15 13:05:02 +08:00
刘祥超
c7a8a40e22 运行日志显示未读的日志数量 2021-10-15 12:54:23 +08:00
刘祥超
408de6af63 优化文字提示 2021-10-15 09:42:55 +08:00
刘祥超
8a324afaa1 数据看板增加事件列表(商业版) 2021-10-14 17:29:30 +08:00
刘祥超
54bc4cede0 修复无法编译amd64以外架构的Bug 2021-10-13 18:06:38 +08:00
刘祥超
4cfbea80b0 支持PROXY Protocol 2021-10-12 20:18:29 +08:00
刘祥超
64cb8286bd 集群非上海时区的在列表里显示时区 2021-10-12 14:39:13 +08:00
刘祥超
3e92e0afc6 优化修改时区交互 2021-10-12 11:49:26 +08:00
刘祥超
8a91308280 可以在集群中指定节点时区 2021-10-12 11:43:53 +08:00
刘祥超
3da861d71e 选择线路的时候关键词可以搜索域名 2021-10-12 08:41:02 +08:00
刘祥超
3566e18e99 WebP压缩支持ico 2021-10-11 14:51:50 +08:00
刘祥超
6e608e627a WebP默认mimeTypes从image/*改为image/png等 2021-10-11 13:56:51 +08:00
刘祥超
65f7fb979b 修改版本为0.3.3 2021-10-11 13:56:20 +08:00
刘祥超
558b5e14f1 优化细节 2021-10-10 20:17:40 +08:00
刘祥超
a5ee2dd03b 页面底部增加GoEdge官网和文档链接 2021-10-10 16:38:45 +08:00
刘祥超
9e9fe78b8d 优化升级提示文字 2021-10-10 16:33:27 +08:00
刘祥超
265e126faf TCP、TLS、UDP支持端口范围 2021-10-10 16:30:21 +08:00
刘祥超
af440e5c5b 服务分组增加特殊页面设置 2021-10-10 10:52:58 +08:00
刘祥超
32b8c91113 特殊页面可以直接使用HTML 2021-10-10 10:35:14 +08:00
刘祥超
dbc60ccca4 证书上传时可以选择输入文本内容 2021-10-09 17:30:05 +08:00
刘祥超
d5b5af5d3a 数据看板-WAF看板增加节点拦截排行和域名拦截排行 2021-10-09 16:01:17 +08:00
刘祥超
8c1bd3bc4e 增加新的界面风格theme4, theme5 2021-10-09 11:50:02 +08:00
刘祥超
8f638186a3 在服务看板中可以切换到附近的服务 2021-10-08 14:36:57 +08:00
刘祥超
0b5a27e674 支持更多的分组全局设置功能 2021-10-07 16:47:14 +08:00
刘祥超
870f1aaaec WAF模式从pass改为bypass 2021-10-07 13:55:00 +08:00
刘祥超
23cb4dcbe5 服务支持自定义访客IP地址获取方式 2021-10-06 11:40:24 +08:00
刘祥超
4f125b4244 添加源站时自动去除专属域名中的末尾斜杠 2021-10-06 09:29:44 +08:00
刘祥超
2b9de7938f 在服务设置里也显示WAF策略的模式 2021-10-06 09:08:09 +08:00
刘祥超
75bb07184f ACME使用EAB申请的账号只能绑定一个用户 2021-10-03 14:43:29 +08:00
刘祥超
1fb491d2e1 ACME证书增加ZeroSSL支持 2021-10-03 13:09:49 +08:00
刘祥超
0bc8bdd841 支持自动转换图像文件为WebP 2021-10-01 16:24:42 +08:00
刘祥超
71d4e2626e 自建DNS改成智能DNS 2021-09-30 13:20:50 +08:00
刘祥超
0b26cbdd01 WAF策略增加观察模式和通过模式 2021-09-30 11:30:36 +08:00
刘祥超
ca72b3c18b 内容压缩支持brotli和deflate 2021-09-29 20:12:27 +08:00
刘祥超
8676f2711b 在WAF规则产生错误时给予提示 2021-09-27 10:11:37 +08:00
刘祥超
788a86bdcf 看板增加离线节点数字 2021-09-27 09:23:48 +08:00
刘祥超
0df6b4b220 优化安装界面--设置管理员账号的交互 2021-09-26 15:08:53 +08:00
刘祥超
f14dcd5c28 缓存条件增加最小内容尺寸配置 2021-09-26 15:01:52 +08:00
刘祥超
f86548e046 版本改为0.3.2 2021-09-26 10:09:51 +08:00
刘祥超
9dea11ab11 节点设置中不显示阈值设置 2021-09-25 19:57:22 +08:00
刘祥超
00749f806c 优化编译脚本 2021-09-25 19:57:10 +08:00
1097 changed files with 44601 additions and 16044 deletions

7
.gitignore vendored
View File

@@ -1,3 +1,8 @@
*_plus.go
*-plus.sh
*@plus.js
*_plus.html
*_plus.js
*@plus.js
*_plus.less
*_plus.css
*_plus.css.map

View File

@@ -8,13 +8,42 @@
* `简单` - 架构简单清晰,安装简单,使用简单,运维简单
* `高扩展性` - 可以自由扩展新的节点,支持亿级数据
## 功能介绍
* 多用户
* 日志审计
* 集群管理
* HTTP/HTTPS/TCP/UDP等协议支持
* WAF
* 缓存
* DNS自动解析
* 多域名绑定
* 免费证书申请
* IP黑白名单
* 访问日志
* 统计
* 内容压缩
* Protocol Proxy协议
* 本地静态文件
* URL跳转
* 路由规则
* 重写规则
* 访问控制
* 字符编码
* 自定义页面
* 自定义HTTP Header
* Websocket
* WebP自动转换
* Fastcgi
* 请求限制
* 流量限制
## 在线演示
* [http://demo.goedge.cn](http://demo.goedge.cn)
## 文档
* [新手指南](https://edge.teaos.cn/docs/QuickStart/Index.md)
* [完整文档](https://edge.teaos.cn/docs)
* [开发者指南](https://edge.teaos.cn/docs/Developer/Build.md)
* [新手指南](https://goedge.cn/docs/QuickStart/Index.md)
* [完整文档](https://goedge.cn/docs)
* [开发者指南](https://goedge.cn/docs/Developer/Build.md)
## 架构
![架构](doc/architect-zh.jpg)
@@ -25,8 +54,7 @@
* [管理平台](https://github.com/TeaOSLab/EdgeAdmin)
## 联系我们
有什么问题和建议都可以加入QQ群 `659832182`
有什么问题和建议都可以加入QQ群 `659832182` 或者 [Telegram群](https://t.me/+5kVCMGxQhZxiODY9)
## 感谢
* 感谢[JetBrains公司](https://www.jetbrains.com/)提供免费的IDE开发Licence。
* 感谢[Gitee](https://gitee.com/)提供国内源代码托管平台
* 感谢 [Gitee](https://gitee.com/) 提供国内源代码托管平台

View File

@@ -2,6 +2,7 @@
function build() {
ROOT=$(dirname $0)
JS_ROOT=$ROOT/../web/public/js
NAME="edge-admin"
DIST=$ROOT/"../dist/${NAME}"
OS=${1}
@@ -20,6 +21,18 @@ function build() {
TAG="community"
fi
# checking environment
echo "checking required commands ..."
commands=("zip" "unzip" "go" "find" "sed")
for cmd in "${commands[@]}"; do
if [ `which ${cmd}` ]; then
echo "checking ${cmd}: ok"
else
echo "checking ${cmd}: not found"
return
fi
done
VERSION=$(lookup-version $ROOT/../internal/const/const.go)
ZIP="${NAME}-${OS}-${ARCH}-${TAG}-v${VERSION}.zip"
@@ -38,6 +51,17 @@ function build() {
echo "=============================="
cd -
# generate files
echo "generating files ..."
go run -tags $TAG $ROOT/../cmd/edge-admin/main.go generate
if [ `which uglifyjs` ]; then
echo "compress to component.js ..."
uglifyjs --compress --mangle -- ${JS_ROOT}/components.src.js > ${JS_ROOT}/components.js
else
echo "copy to component.js ..."
cp ${JS_ROOT}/components.src.js ${JS_ROOT}/components.js
fi
# create dir & copy files
echo "copying ..."
if [ ! -d $DIST ]; then
@@ -49,8 +73,23 @@ function build() {
cp -R $ROOT/../web $DIST/
rm -f $DIST/web/tmp/*
rm -rf $DIST/web/public/js/components
rm -f $DIST/web/public/js/components.src.js
cp $ROOT/configs/server.template.yaml $DIST/configs/
# change _plus.[ext] to .[ext]
if [ "${TAG}" = "plus" ]; then
echo "converting filenames ..."
exts=("html" "js" "css")
for ext in "${exts[@]}"; do
pattern="*_plus."${ext}
find $DIST/web/views -type f -name $pattern | \
while read filename; do
mv ${filename} "${filename/_plus."${ext}"/."${ext}"}"
done
done
fi
EDGE_API_ZIP_FILE=$ROOT"/../../EdgeAPI/dist/edge-api-${OS}-${ARCH}-${TAG}-v${APINodeVersion}.zip"
cp $EDGE_API_ZIP_FILE $DIST/
cd $DIST/
@@ -60,11 +99,14 @@ function build() {
# build
echo "building "${NAME}" ..."
env GOOS=$OS GOARCH=$GOARCH go build -tags $TAG -ldflags="-s -w" -o $DIST/bin/${NAME} $ROOT/../cmd/edge-admin/main.go
env GOOS=$OS GOARCH=$ARCH go build -tags $TAG -ldflags="-s -w" -o $DIST/bin/${NAME} $ROOT/../cmd/edge-admin/main.go
# delete hidden files
find $DIST -name ".DS_Store" -delete
find $DIST -name ".gitignore" -delete
find $DIST -name "*.less" -delete
find $DIST -name "*.css.map" -delete
find $DIST -name "*.js.map" -delete
# zip
echo "zip files ..."

16
build/generate.sh Executable file
View File

@@ -0,0 +1,16 @@
#!/usr/bin/env bash
JS_ROOT=../web/public/js
echo "generate component.src.js ..."
go run -tags=community ../cmd/edge-admin/main.go generate
if [ `which uglifyjs` ]; then
echo "compress to component.js ..."
uglifyjs --compress --mangle -- ${JS_ROOT}/components.src.js > ${JS_ROOT}/components.js
else
echo "copy to component.js ..."
cp ${JS_ROOT}/components.src.js ${JS_ROOT}/components.js
fi
echo "ok"

View File

@@ -5,9 +5,11 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/apps"
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/gen"
"github.com/TeaOSLab/EdgeAdmin/internal/nodes"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/gosock/pkg/gosock"
)
@@ -63,12 +65,24 @@ func main() {
fmt.Println("[ERROR]the service not started yet, you should start the service first")
return
}
_, err := sock.Send(&gosock.Command{Code: "demo"})
reply, err := sock.Send(&gosock.Command{Code: "demo"})
if err != nil {
fmt.Println("[ERROR]change demo mode failed: " + err.Error())
return
}
fmt.Println("change demo mode successfully")
var isDemo = maps.NewMap(reply.Params).GetBool("isDemo")
if isDemo {
fmt.Println("change demo mode to: on")
} else {
fmt.Println("change demo mode to: off")
}
})
app.On("generate", func() {
err := gen.Generate()
if err != nil {
fmt.Println("generate failed: " + err.Error())
return
}
})
app.Run(func() {
adminNode := nodes.NewAdminNode()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 203 KiB

After

Width:  |  Height:  |  Size: 225 KiB

16
go.mod
View File

@@ -8,17 +8,17 @@ require (
github.com/TeaOSLab/EdgeCommon v0.0.0-00010101000000-000000000000
github.com/cespare/xxhash v1.1.0
github.com/go-sql-driver/mysql v1.5.0
github.com/go-yaml/yaml v2.1.0+incompatible
github.com/google/go-cmp v0.5.6 // indirect
github.com/iwind/TeaGo v0.0.0-20210720011303-fc255c995afa
github.com/iwind/gosock v0.0.0-20210722083328-12b2d66abec3
github.com/iwind/TeaGo v0.0.0-20220304043459-0dd944a5b475
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4
github.com/json-iterator/go v1.1.12 // indirect
github.com/miekg/dns v1.1.35
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/tealeg/xlsx/v3 v3.2.3
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119
golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced // indirect
google.golang.org/grpc v1.38.0
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8
google.golang.org/grpc v1.45.0
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
)

98
go.sum
View File

@@ -1,10 +1,12 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
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/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
@@ -14,6 +16,10 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
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/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -21,18 +27,18 @@ github.com/dgryski/go-rendezvous v0.0.0-20200624174652-8d2f3be8b2d9/go.mod h1:cu
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.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
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/frankban/quicktest v1.5.0 h1:Tb4jWdSpdjKzTUicPnY61PZxKbDoGa7ABbrReT3gQVY=
github.com/frankban/quicktest v1.5.0/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
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 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
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/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=
@@ -45,6 +51,7 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
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/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=
@@ -60,14 +67,20 @@ github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iwind/TeaGo v0.0.0-20210628135026-38575a4ab060/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/TeaGo v0.0.0-20210720011303-fc255c995afa h1:woN88uEmRRUNFD7pRZEtX9heDcjFn0ClMxjF5ButKow=
github.com/iwind/TeaGo v0.0.0-20210720011303-fc255c995afa/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/gosock v0.0.0-20210722083328-12b2d66abec3 h1:aBSonas7vFcgTj9u96/bWGILGv1ZbUSTLiOzcI1ZT6c=
github.com/iwind/gosock v0.0.0-20210722083328-12b2d66abec3/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/iwind/TeaGo v0.0.0-20211026123858-7de7a21cad24 h1:1cGulkD2SNJJRok5OKwyhP/Ddm+PgSWKOupn0cR36/A=
github.com/iwind/TeaGo v0.0.0-20211026123858-7de7a21cad24/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/TeaGo v0.0.0-20220304042834-d8b5103e9027 h1:4knh28YgldmEtu8QG8pgQhUaI3i//qRjOdEyj9OVqXA=
github.com/iwind/TeaGo v0.0.0-20220304042834-d8b5103e9027/go.mod h1:IyKPaIhjbjsTctPguMWJZDHuBHu+XiYcV5ZEDxQpq94=
github.com/iwind/TeaGo v0.0.0-20220304043459-0dd944a5b475 h1:EseyfFaQOjWanGiby9KMw7PjDBMg/95tLDgIw/ns0Cw=
github.com/iwind/TeaGo v0.0.0-20220304043459-0dd944a5b475/go.mod h1:HRHK0zoC/og3c9/hKosD9yYVMTnnzm3PgXUdhRYHaLc=
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4 h1:VWGsCqTzObdlbf7UUE3oceIpcEKi4C/YBUszQXk118A=
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -75,10 +88,12 @@ 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/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs=
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
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=
@@ -108,17 +123,19 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO
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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tealeg/xlsx/v3 v3.2.3 h1:MXnVh+9Y8cUglowItTy2HL3Kv6z+q/0aNjeKuTsVqZQ=
github.com/tealeg/xlsx/v3 v3.2.3/go.mod h1:0hGmAEoZ48SS1ZAE6eqZJkJVXgOMY+8a33vjXa8S8HA=
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119 h1:YyPWX3jLOtYKulBR6AScGIs74lLrJcgeKRwcbAuQOG4=
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119/go.mod h1:/nuTSlK+okRfR/vnIPqR89fFKonnWPiZymN5ydRJkX8=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opentelemetry.io/otel v0.7.0/go.mod h1:aZMyHG5TqDOXEgH2tyLiXSUKly1jT3yqE9PmrzIeCdo=
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 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
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=
@@ -127,27 +144,28 @@ golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+o
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/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
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/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
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/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
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.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
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/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -165,30 +183,29 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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=
@@ -198,17 +215,23 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
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-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-20210617175327-b9e0b3197ced h1:c5geK1iMU3cDKtFrCVQIcjR3W+JOZMuhIyICMCTbtus=
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
google.golang.org/genproto v0.0.0-20220303160752-862486edd9cc h1:fb/ViRpv3ln/LvbqZtTpoOd1YQDNH12gaGZreoSFovE=
google.golang.org/genproto v0.0.0-20220303160752-862486edd9cc/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
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/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
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.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg=
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
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=
@@ -219,8 +242,9 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
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 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
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=
@@ -229,13 +253,15 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
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.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/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 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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

@@ -1,51 +1,108 @@
package apps
import (
"github.com/TeaOSLab/EdgeAdmin/internal/goman"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/sizes"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/files"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/utils/time"
timeutil "github.com/iwind/TeaGo/utils/time"
"log"
"os"
"runtime"
"strconv"
"strings"
)
type LogWriter struct {
fileAppender *files.Appender
fp *os.File
c chan string
}
func (this *LogWriter) Init() {
// 创建目录
dir := files.NewFile(Tea.LogDir())
var dir = files.NewFile(Tea.LogDir())
if !dir.Exists() {
err := dir.Mkdir()
if err != nil {
log.Println("[error]" + err.Error())
log.Println("[LOG]create log dir failed: " + err.Error())
}
}
logFile := files.NewFile(Tea.LogFile("run.log"))
// 打开要写入的日志文件
appender, err := logFile.Appender()
var logPath = Tea.LogFile("run.log")
fp, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
logs.Error(err)
log.Println("[LOG]open log file failed: " + err.Error())
} else {
this.fileAppender = appender
this.fp = fp
}
this.c = make(chan string, 1024)
// 异步写入文件
var maxFileSize = 2 * sizes.G // 文件最大尺寸,超出此尺寸则清空
if fp != nil {
goman.New(func() {
var totalSize int64 = 0
stat, err := fp.Stat()
if err == nil {
totalSize = stat.Size()
}
for message := range this.c {
totalSize += int64(len(message))
_, err := fp.WriteString(timeutil.Format("Y/m/d H:i:s ") + message + "\n")
if err != nil {
log.Println("[LOG]write log failed: " + err.Error())
} else {
// 如果太大则Truncate
if totalSize > maxFileSize {
_ = fp.Truncate(0)
totalSize = 0
}
}
}
})
}
}
func (this *LogWriter) Write(message string) {
log.Println(message)
backgroundEnv, _ := os.LookupEnv("EdgeBackground")
if backgroundEnv != "on" {
// 文件和行号
var file string
var line int
if Tea.IsTesting() {
var callDepth = 3
var ok bool
_, file, line, ok = runtime.Caller(callDepth)
if ok {
file = this.packagePath(file)
}
}
if this.fileAppender != nil {
_, err := this.fileAppender.AppendString(timeutil.Format("Y/m/d H:i:s ") + message + "\n")
if err != nil {
log.Println("[error]" + err.Error())
if len(file) > 0 {
log.Println(message + " (" + file + ":" + strconv.Itoa(line) + ")")
} else {
log.Println(message)
}
}
this.c <- message
}
func (this *LogWriter) Close() {
if this.fileAppender != nil {
_ = this.fileAppender.Close()
if this.fp != nil {
_ = this.fp.Close()
}
close(this.c)
}
func (this *LogWriter) packagePath(path string) string {
var pieces = strings.Split(path, "/")
if len(pieces) >= 2 {
return strings.Join(pieces[len(pieces)-2:], "/")
}
return path
}

View File

@@ -19,6 +19,7 @@ const (
AdminModuleCodeAdmin AdminModuleCode = "admin" // 系统用户
AdminModuleCodeUser AdminModuleCode = "user" // 平台用户
AdminModuleCodeFinance AdminModuleCode = "finance" // 财务
AdminModuleCodePlan AdminModuleCode = "plan" // 套餐
AdminModuleCodeLog AdminModuleCode = "log" // 日志
AdminModuleCodeSetting AdminModuleCode = "setting" // 设置
AdminModuleCodeCommon AdminModuleCode = "common" // 只要登录就可以访问的模块
@@ -182,7 +183,7 @@ func AllModuleMaps() []maps.Map {
}
if teaconst.IsPlus {
m = append(m, maps.Map{
"name": "自建DNS",
"name": "智能DNS",
"code": AdminModuleCodeNS,
"url": "/ns",
})
@@ -203,6 +204,11 @@ func AllModuleMaps() []maps.Map {
"code": AdminModuleCodeFinance,
"url": "/finance",
},
{
"name": "套餐管理",
"code": AdminModuleCodePlan,
"url": "/plans",
},
{
"name": "日志审计",
"code": AdminModuleCodeLog,

View File

@@ -3,18 +3,16 @@ package configloaders
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/iwind/TeaGo/logs"
"reflect"
"time"
)
var sharedAdminUIConfig *systemconfigs.AdminUIConfig = nil
const (
AdminUISettingName = "adminUIConfig"
)
func LoadAdminUIConfig() (*systemconfigs.AdminUIConfig, error) {
locker.Lock()
defer locker.Unlock()
@@ -41,7 +39,7 @@ func UpdateAdminUIConfig(uiConfig *systemconfigs.AdminUIConfig) error {
return err
}
_, err = rpcClient.SysSettingRPC().UpdateSysSetting(rpcClient.Context(0), &pb.UpdateSysSettingRequest{
Code: AdminUISettingName,
Code: systemconfigs.SettingCodeAdminUIConfig,
ValueJSON: valueJSON,
})
if err != nil {
@@ -49,10 +47,13 @@ func UpdateAdminUIConfig(uiConfig *systemconfigs.AdminUIConfig) error {
}
sharedAdminUIConfig = uiConfig
// timezone
updateTimeZone(uiConfig)
return nil
}
// 是否显示财务信息
// ShowFinance 是否显示财务信息
func ShowFinance() bool {
config, _ := LoadAdminUIConfig()
if config != nil && !config.ShowFinance {
@@ -70,7 +71,7 @@ func loadAdminUIConfig() (*systemconfigs.AdminUIConfig, error) {
return nil, err
}
resp, err := rpcClient.SysSettingRPC().ReadSysSetting(rpcClient.Context(0), &pb.ReadSysSettingRequest{
Code: AdminUISettingName,
Code: systemconfigs.SettingCodeAdminUIConfig,
})
if err != nil {
return nil, err
@@ -80,13 +81,17 @@ func loadAdminUIConfig() (*systemconfigs.AdminUIConfig, error) {
return sharedAdminUIConfig, nil
}
config := &systemconfigs.AdminUIConfig{}
var config = &systemconfigs.AdminUIConfig{}
err = json.Unmarshal(resp.ValueJSON, config)
if err != nil {
logs.Println("[UI_MANAGER]" + err.Error())
sharedAdminUIConfig = defaultAdminUIConfig()
return sharedAdminUIConfig, nil
}
// timezone
updateTimeZone(config)
sharedAdminUIConfig = config
return sharedAdminUIConfig, nil
}
@@ -98,5 +103,17 @@ func defaultAdminUIConfig() *systemconfigs.AdminUIConfig {
ShowOpenSourceInfo: true,
ShowVersion: true,
ShowFinance: true,
DefaultPageSize: 10,
TimeZone: nodeconfigs.DefaultTimeZoneLocation,
}
}
// 修改时区
func updateTimeZone(config *systemconfigs.AdminUIConfig) {
if len(config.TimeZone) > 0 {
location, err := time.LoadLocation(config.TimeZone)
if err == nil && time.Local != location {
time.Local = location
}
}
}

View File

@@ -2,8 +2,8 @@ package configs
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/go-yaml/yaml"
"github.com/iwind/TeaGo/Tea"
"gopkg.in/yaml.v3"
"io/ioutil"
"os"
"path/filepath"

View File

@@ -11,7 +11,9 @@ import (
var plusConfigFile = "plus.cache.json"
type PlusConfig struct {
IsPlus bool `json:"isPlus"`
IsPlus bool `json:"isPlus"`
Components []string `json:"components"`
DayTo string `json:"dayTo"`
}
func ReadPlusConfig() *PlusConfig {

View File

@@ -1,9 +1,9 @@
package teaconst
const (
Version = "0.3.1"
Version = "0.4.7.1"
APINodeVersion = "0.3.1"
APINodeVersion = "0.4.7"
ProductName = "Edge Admin"
ProcessName = "edge-admin"
@@ -18,4 +18,5 @@ const (
CookieSID = "edgesid"
SystemdServiceName = "edge-admin"
UpdatesURL = "https://goedge.cn/api/boot/versions?os=${os}&arch=${arch}"
)

View File

@@ -1,6 +1,7 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build community
// +build community
package teaconst
// IsPlus 是否为企业版
var IsPlus = false

View File

@@ -4,9 +4,10 @@ package teaconst
var (
IsRecoverMode = false
)
var (
IsDemoMode = false
ErrorDemoOperation = "DEMO模式下无法进行创建、修改、删除等操作"
NewVersionCode = "" // 有新的版本
NewVersionDownloadURL = "" // 新版本下载地址
)

129
internal/gen/generate.go Normal file
View File

@@ -0,0 +1,129 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package gen
import (
"bytes"
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/conds/condutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/files"
"github.com/iwind/TeaGo/logs"
"io"
"os"
"path/filepath"
)
func Generate() error {
err := generateComponentsJSFile()
if err != nil {
return errors.New("generate 'components.src.js' failed: " + err.Error())
}
return nil
}
// 生成Javascript文件
func generateComponentsJSFile() error {
var buffer = bytes.NewBuffer([]byte{})
var webRoot string
if Tea.IsTesting() {
webRoot = Tea.Root + "/../web/public/js/components/"
} else {
webRoot = Tea.Root + "/web/public/js/components/"
}
f := files.NewFile(webRoot)
f.Range(func(file *files.File) {
if !file.IsFile() {
return
}
if file.Ext() != ".js" {
return
}
data, err := file.ReadAll()
if err != nil {
logs.Error(err)
return
}
buffer.Write(data)
buffer.Write([]byte{'\n', '\n'})
})
// 条件组件
typesJSON, err := json.Marshal(condutils.ReadAllAvailableCondTypes())
if err != nil {
logs.Println("ComponentsAction marshal request cond types failed: " + err.Error())
} else {
buffer.WriteString("window.REQUEST_COND_COMPONENTS = ")
buffer.Write(typesJSON)
buffer.Write([]byte{'\n', '\n'})
}
// 条件操作符
requestOperatorsJSON, err := json.Marshal(shared.AllRequestOperators())
if err != nil {
logs.Println("ComponentsAction marshal request operators failed: " + err.Error())
} else {
buffer.WriteString("window.REQUEST_COND_OPERATORS = ")
buffer.Write(requestOperatorsJSON)
buffer.Write([]byte{'\n', '\n'})
}
// 请求变量
requestVariablesJSON, err := json.Marshal(shared.DefaultRequestVariables())
if err != nil {
logs.Println("ComponentsAction marshal request variables failed: " + err.Error())
} else {
buffer.WriteString("window.REQUEST_VARIABLES = ")
buffer.Write(requestVariablesJSON)
buffer.Write([]byte{'\n', '\n'})
}
// 指标
metricHTTPKeysJSON, err := json.Marshal(serverconfigs.FindAllMetricKeyDefinitions(serverconfigs.MetricItemCategoryHTTP))
if err != nil {
logs.Println("ComponentsAction marshal metric http keys failed: " + err.Error())
} else {
buffer.WriteString("window.METRIC_HTTP_KEYS = ")
buffer.Write(metricHTTPKeysJSON)
buffer.Write([]byte{'\n', '\n'})
}
// IP地址阈值项目
ipAddrThresholdItemsJSON, err := json.Marshal(nodeconfigs.FindAllIPAddressThresholdItems())
if err != nil {
logs.Println("ComponentsAction marshal ip addr threshold items failed: " + err.Error())
} else {
buffer.WriteString("window.IP_ADDR_THRESHOLD_ITEMS = ")
buffer.Write(ipAddrThresholdItemsJSON)
buffer.Write([]byte{'\n', '\n'})
}
// IP地址阈值动作
ipAddrThresholdActionsJSON, err := json.Marshal(nodeconfigs.FindAllIPAddressThresholdActions())
if err != nil {
logs.Println("ComponentsAction marshal ip addr threshold actions failed: " + err.Error())
} else {
buffer.WriteString("window.IP_ADDR_THRESHOLD_ACTIONS = ")
buffer.Write(ipAddrThresholdActionsJSON)
buffer.Write([]byte{'\n', '\n'})
}
fp, err := os.OpenFile(filepath.Clean(Tea.PublicFile("/js/components.src.js")), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0777)
if err != nil {
return err
}
_, err = io.Copy(fp, buffer)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,13 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package gen
import "testing"
func TestGenerate(t *testing.T) {
err := Generate()
if err != nil {
t.Fatal(err)
}
t.Log("ok")
}

View File

@@ -0,0 +1,12 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package goman
import "time"
type Instance struct {
Id uint64
CreatedTime time.Time
File string
Line int
}

81
internal/goman/lib.go Normal file
View File

@@ -0,0 +1,81 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package goman
import (
"runtime"
"sync"
"time"
)
var locker = &sync.Mutex{}
var instanceMap = map[uint64]*Instance{} // id => *Instance
var instanceId = uint64(0)
// New 新创建goroutine
func New(f func()) {
_, file, line, _ := runtime.Caller(1)
go func() {
locker.Lock()
instanceId++
var instance = &Instance{
Id: instanceId,
CreatedTime: time.Now(),
}
instance.File = file
instance.Line = line
instanceMap[instanceId] = instance
locker.Unlock()
// run function
f()
locker.Lock()
delete(instanceMap, instanceId)
locker.Unlock()
}()
}
// NewWithArgs 创建带有参数的goroutine
func NewWithArgs(f func(args ...interface{}), args ...interface{}) {
_, file, line, _ := runtime.Caller(1)
go func() {
locker.Lock()
instanceId++
var instance = &Instance{
Id: instanceId,
CreatedTime: time.Now(),
}
instance.File = file
instance.Line = line
instanceMap[instanceId] = instance
locker.Unlock()
// run function
f(args...)
locker.Lock()
delete(instanceMap, instanceId)
locker.Unlock()
}()
}
// List 列出所有正在运行goroutine
func List() []*Instance {
locker.Lock()
defer locker.Unlock()
var result = []*Instance{}
for _, instance := range instanceMap {
result = append(result, instance)
}
return result
}

View File

@@ -0,0 +1,28 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package goman
import (
"testing"
"time"
)
func TestNew(t *testing.T) {
New(func() {
t.Log("Hello")
t.Log(List())
})
time.Sleep(1 * time.Second)
t.Log(List())
time.Sleep(1 * time.Second)
}
func TestNewWithArgs(t *testing.T) {
NewWithArgs(func(args ...interface{}) {
t.Log(args[0], args[1])
}, 1, 2)
time.Sleep(1 * time.Second)
}

View File

@@ -13,9 +13,12 @@ import (
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/sessions"
"github.com/iwind/TeaGo/types"
"github.com/iwind/gosock/pkg/gosock"
"gopkg.in/yaml.v3"
"io/ioutil"
"log"
"net"
"os"
"os/exec"
"os/signal"
@@ -58,6 +61,9 @@ func (this *AdminNode) Run() {
return
}
// 添加端口到防火墙
this.addPortsToFirewall()
// 监听信号
sigQueue := make(chan os.Signal)
signal.Notify(sigQueue, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL, syscall.SIGQUIT)
@@ -201,6 +207,44 @@ https:
return nil
}
// 添加端口到防火墙
func (this *AdminNode) addPortsToFirewall() {
var configFile = Tea.ConfigFile("server.yaml")
data, err := ioutil.ReadFile(configFile)
if err != nil {
return
}
var config = &TeaGo.ServerConfig{}
err = yaml.Unmarshal(data, config)
if err != nil {
return
}
var ports = []int{}
if config.Http.On {
for _, listen := range config.Http.Listen {
_, portString, _ := net.SplitHostPort(listen)
var port = types.Int(portString)
if port > 0 && !lists.ContainsInt(ports, port) {
ports = append(ports, port)
}
}
}
if config.Https.On {
for _, listen := range config.Https.Listen {
_, portString, _ := net.SplitHostPort(listen)
var port = types.Int(portString)
if port > 0 && !lists.ContainsInt(ports, port) {
ports = append(ports, port)
}
}
}
utils.AddPortsToFirewall(ports)
}
// 启动API节点
func (this *AdminNode) startAPINode() {
configPath := Tea.Root + "/edge-api/configs/api.yaml"
@@ -325,7 +369,19 @@ func (this *AdminNode) listenSock() error {
_ = cmd.ReplyOk()
case "demo":
teaconst.IsDemoMode = !teaconst.IsDemoMode
_ = cmd.ReplyOk()
_ = cmd.Reply(&gosock.Command{
Params: map[string]interface{}{"isDemo": teaconst.IsDemoMode},
})
case "info":
exePath, _ := os.Executable()
_ = cmd.Reply(&gosock.Command{
Code: "info",
Params: map[string]interface{}{
"pid": os.Getpid(),
"version": teaconst.Version,
"path": exePath,
},
})
}
})

View File

@@ -16,6 +16,8 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/encoding/gzip"
"google.golang.org/grpc/metadata"
"net/url"
"sync"
@@ -27,11 +29,11 @@ type RPCClient struct {
apiConfig *configs.APIConfig
conns []*grpc.ClientConn
locker sync.Mutex
locker sync.RWMutex
}
// NewRPCClient 构造新的RPC客户端
func NewRPCClient(apiConfig *configs.APIConfig) (*RPCClient, error) {
func NewRPCClient(apiConfig *configs.APIConfig, isPrimary bool) (*RPCClient, error) {
if apiConfig == nil {
return nil, errors.New("api config should not be nil")
}
@@ -46,7 +48,9 @@ func NewRPCClient(apiConfig *configs.APIConfig) (*RPCClient, error) {
}
// 设置RPC
dao.SetRPC(client)
if isPrimary {
dao.SetRPC(client)
}
return client, nil
}
@@ -159,6 +163,10 @@ func (this *RPCClient) APINodeRPC() pb.APINodeServiceClient {
return pb.NewAPINodeServiceClient(this.pickConn())
}
func (this *RPCClient) APIMethodStatRPC() pb.APIMethodStatServiceClient {
return pb.NewAPIMethodStatServiceClient(this.pickConn())
}
func (this *RPCClient) UserNodeRPC() pb.UserNodeServiceClient {
return pb.NewUserNodeServiceClient(this.pickConn())
}
@@ -364,6 +372,14 @@ func (this *RPCClient) ACMETaskRPC() pb.ACMETaskServiceClient {
return pb.NewACMETaskServiceClient(this.pickConn())
}
func (this *RPCClient) ACMEProviderRPC() pb.ACMEProviderServiceClient {
return pb.NewACMEProviderServiceClient(this.pickConn())
}
func (this *RPCClient) ACMEProviderAccountRPC() pb.ACMEProviderAccountServiceClient {
return pb.NewACMEProviderAccountServiceClient(this.pickConn())
}
func (this *RPCClient) UserRPC() pb.UserServiceClient {
return pb.NewUserServiceClient(this.pickConn())
}
@@ -372,6 +388,22 @@ func (this *RPCClient) UserBillRPC() pb.UserBillServiceClient {
return pb.NewUserBillServiceClient(this.pickConn())
}
func (this *RPCClient) ServerBillRPC() pb.ServerBillServiceClient {
return pb.NewServerBillServiceClient(this.pickConn())
}
func (this *RPCClient) UserAccountRPC() pb.UserAccountServiceClient {
return pb.NewUserAccountServiceClient(this.pickConn())
}
func (this *RPCClient) UserAccountLogRPC() pb.UserAccountLogServiceClient {
return pb.NewUserAccountLogServiceClient(this.pickConn())
}
func (this *RPCClient) UserAccountDailyStatRPC() pb.UserAccountDailyStatServiceClient {
return pb.NewUserAccountDailyStatServiceClient(this.pickConn())
}
func (this *RPCClient) UserAccessKeyRPC() pb.UserAccessKeyServiceClient {
return pb.NewUserAccessKeyServiceClient(this.pickConn())
}
@@ -452,10 +484,26 @@ func (this *RPCClient) ServerStatBoardRPC() pb.ServerStatBoardServiceClient {
return pb.NewServerStatBoardServiceClient(this.pickConn())
}
func (this *RPCClient) ServerDomainHourlyStatRPC() pb.ServerDomainHourlyStatServiceClient {
return pb.NewServerDomainHourlyStatServiceClient(this.pickConn())
}
func (this *RPCClient) ServerStatBoardChartRPC() pb.ServerStatBoardChartServiceClient {
return pb.NewServerStatBoardChartServiceClient(this.pickConn())
}
func (this *RPCClient) PlanRPC() pb.PlanServiceClient {
return pb.NewPlanServiceClient(this.pickConn())
}
func (this *RPCClient) UserPlanRPC() pb.UserPlanServiceClient {
return pb.NewUserPlanServiceClient(this.pickConn())
}
func (this *RPCClient) TrafficDailyStatRPC() pb.TrafficDailyStatServiceClient {
return pb.NewTrafficDailyStatServiceClient(this.pickConn())
}
// Context 构造Admin上下文
func (this *RPCClient) Context(adminId int64) context.Context {
ctx := context.Background()
@@ -505,7 +553,11 @@ func (this *RPCClient) APIContext(apiNodeId int64) context.Context {
// UpdateConfig 修改配置
func (this *RPCClient) UpdateConfig(config *configs.APIConfig) error {
this.apiConfig = config
return this.init()
this.locker.Lock()
err := this.init()
this.locker.Unlock()
return err
}
// 初始化
@@ -518,12 +570,14 @@ func (this *RPCClient) init() error {
return errors.New("parse endpoint failed: " + err.Error())
}
var conn *grpc.ClientConn
var callOptions = grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(128*1024*1024),
grpc.UseCompressor(gzip.Name))
if u.Scheme == "http" {
conn, err = grpc.Dial(u.Host, grpc.WithInsecure(), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(128*1024*1024)))
conn, err = grpc.Dial(u.Host, grpc.WithTransportCredentials(insecure.NewCredentials()), callOptions)
} else if u.Scheme == "https" {
conn, err = grpc.Dial(u.Host, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
InsecureSkipVerify: true,
})), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(128*1024*1024)))
})), callOptions)
} else {
return errors.New("parse endpoint failed: invalid scheme '" + u.Scheme + "'")
}
@@ -583,3 +637,20 @@ func (this *RPCClient) pickConn() *grpc.ClientConn {
return this.conns[rands.Int(0, len(this.conns)-1)]
}
// Close 关闭
func (this *RPCClient) Close() error {
this.locker.Lock()
defer this.locker.Unlock()
var lastErr error
for _, conn := range this.conns {
var err = conn.Close()
if err != nil {
lastErr = err
continue
}
}
return lastErr
}

View File

@@ -18,7 +18,7 @@ func TestRPCClient_NodeRPC(t *testing.T) {
if err != nil {
t.Fatal(err)
}
rpc, err := NewRPCClient(config)
rpc, err := NewRPCClient(config, true)
if err != nil {
t.Fatal(err)
}
@@ -41,7 +41,7 @@ func TestRPC_Dial_HTTP(t *testing.T) {
},
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
})
}, true)
if err != nil {
t.Fatal(err)
}
@@ -62,7 +62,7 @@ func TestRPC_Dial_HTTP_2(t *testing.T) {
},
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
})
}, true)
if err != nil {
t.Fatal(err)
}
@@ -83,7 +83,7 @@ func TestRPC_Dial_HTTPS(t *testing.T) {
},
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
})
}, true)
if err != nil {
t.Fatal(err)
}

View File

@@ -20,7 +20,7 @@ func SharedRPC() (*RPCClient, error) {
if err != nil {
return nil, err
}
client, err := NewRPCClient(config)
client, err := NewRPCClient(config, true)
if err != nil {
return nil, err
}

View File

@@ -2,6 +2,8 @@ package setup
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"os"
)
var isConfigured bool
@@ -16,3 +18,16 @@ func IsConfigured() bool {
isConfigured = err == nil
return isConfigured
}
// IsNewInstalled IsNew 检查是否新安装
func IsNewInstalled() bool {
homeDir, err := os.UserHomeDir()
if err != nil {
return false
}
_, err = os.Stat(homeDir + "/." + teaconst.ProcessName + "/api.yaml")
if err != nil {
return true
}
return false
}

View File

@@ -0,0 +1,131 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package tasks
import (
"encoding/json"
"errors"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/events"
"github.com/TeaOSLab/EdgeAdmin/internal/goman"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
stringutil "github.com/iwind/TeaGo/utils/string"
"io/ioutil"
"net/http"
"runtime"
"strings"
"time"
)
func init() {
events.On(events.EventStart, func() {
var task = NewCheckUpdatesTask()
goman.New(func() {
task.Start()
})
})
}
type CheckUpdatesTask struct {
ticker *time.Ticker
}
func NewCheckUpdatesTask() *CheckUpdatesTask {
return &CheckUpdatesTask{}
}
func (this *CheckUpdatesTask) Start() {
this.ticker = time.NewTicker(12 * time.Hour)
for range this.ticker.C {
err := this.Loop()
if err != nil {
logs.Println("[TASK][CHECK_UPDATES_TASK]" + err.Error())
}
}
}
func (this *CheckUpdatesTask) Loop() error {
// 检查是否开启
rpcClient, err := rpc.SharedRPC()
if err != nil {
return err
}
valueResp, err := rpcClient.SysSettingRPC().ReadSysSetting(rpcClient.Context(0), &pb.ReadSysSettingRequest{Code: systemconfigs.SettingCodeCheckUpdates})
if err != nil {
return err
}
var valueJSON = valueResp.ValueJSON
var config = &systemconfigs.CheckUpdatesConfig{AutoCheck: false}
if len(valueJSON) > 0 {
err = json.Unmarshal(valueJSON, config)
if err != nil {
return errors.New("decode config failed: " + err.Error())
}
if !config.AutoCheck {
return nil
}
} else {
return nil
}
// 开始检查
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
// 目前支持Linux
if runtime.GOOS != "linux" {
return nil
}
var apiURL = teaconst.UpdatesURL
apiURL = strings.ReplaceAll(apiURL, "${os}", runtime.GOOS)
apiURL = strings.ReplaceAll(apiURL, "${arch}", runtime.GOARCH)
resp, err := http.Get(apiURL)
if err != nil {
return errors.New("read api failed: " + err.Error())
}
defer func() {
_ = resp.Body.Close()
}()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return errors.New("read api failed: " + err.Error())
}
var apiResponse = &Response{}
err = json.Unmarshal(data, apiResponse)
if err != nil {
return errors.New("decode version data failed: " + err.Error())
}
if apiResponse.Code != 200 {
return errors.New("invalid response: " + apiResponse.Message)
}
var m = maps.NewMap(apiResponse.Data)
var dlHost = m.GetString("host")
var versions = m.GetSlice("versions")
if len(versions) > 0 {
for _, version := range versions {
var vMap = maps.NewMap(version)
if vMap.GetString("code") == "admin" {
var latestVersion = vMap.GetString("version")
if stringutil.VersionCompare(teaconst.Version, latestVersion) < 0 {
teaconst.NewVersionCode = latestVersion
teaconst.NewVersionDownloadURL = dlHost + vMap.GetString("url")
return nil
}
}
}
}
return nil
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/events"
"github.com/TeaOSLab/EdgeAdmin/internal/goman"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeAdmin/internal/setup"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
@@ -23,7 +24,9 @@ import (
func init() {
events.On(events.EventStart, func() {
task := NewSyncAPINodesTask()
go task.Start()
goman.New(func() {
task.Start()
})
})
}
@@ -66,7 +69,7 @@ func (this *SyncAPINodesTask) Loop() error {
}
newEndpoints := []string{}
for _, node := range resp.Nodes {
for _, node := range resp.ApiNodes {
if !node.IsOn {
continue
}

View File

@@ -3,6 +3,7 @@ package tasks
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/events"
"github.com/TeaOSLab/EdgeAdmin/internal/goman"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeAdmin/internal/setup"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/nodeutils"
@@ -17,7 +18,9 @@ import (
func init() {
events.On(events.EventStart, func() {
task := NewSyncClusterTask()
go task.Start()
goman.New(func() {
task.Start()
})
})
}
@@ -51,7 +54,7 @@ func (this *SyncClusterTask) loop() error {
}
ctx := rpcClient.Context(0)
tasksResp, err := rpcClient.NodeTaskRPC().FindNotifyingNodeTasks(ctx, &pb.FindNotifyingNodeTasksRequest{Size: 100})
tasksResp, err := rpcClient.NodeTaskRPC().FindNotifyingNodeTasks(ctx, &pb.FindNotifyingNodeTasksRequest{Size: 500})
if err != nil {
return err
}

View File

@@ -0,0 +1,28 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package utils
import (
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/types"
"os/exec"
"runtime"
)
func AddPortsToFirewall(ports []int) {
for _, port := range ports {
// Linux
if runtime.GOOS == "linux" {
// firewalld
firewallCmd, _ := exec.LookPath("firewall-cmd")
if len(firewallCmd) > 0 {
err := exec.Command(firewallCmd, "--add-port="+types.String(port)+"/tcp").Run()
if err == nil {
logs.Println("ADMIN_NODE", "add port '"+types.String(port)+"' to firewalld")
_ = exec.Command(firewallCmd, "--add-port="+types.String(port)+"/tcp", "--permanent").Run()
}
}
}
}
}

View File

@@ -1,14 +1,17 @@
package utils
import (
"bytes"
"encoding/binary"
"errors"
"github.com/iwind/TeaGo/types"
"math/big"
"net"
"regexp"
"strings"
)
// 将IP转换为整型
// IP2Long 将IP转换为整型
func IP2Long(ip string) uint64 {
s := net.ParseIP(ip)
if len(s) != 16 {
@@ -23,7 +26,7 @@ func IP2Long(ip string) uint64 {
return uint64(binary.BigEndian.Uint32(s.To4()))
}
// 判断是否为IPv4
// IsIPv4 判断是否为IPv4
func IsIPv4(ip string) bool {
if !regexp.MustCompile(`^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$`).MatchString(ip) {
return false
@@ -34,10 +37,100 @@ func IsIPv4(ip string) bool {
return true
}
// 判断是否为IPv6
// IsIPv6 判断是否为IPv6
func IsIPv6(ip string) bool {
if !strings.Contains(ip, ":") {
return false
}
return len(net.ParseIP(ip)) == net.IPv6len
}
// ExtractIP 分解IP
// 只支持D段掩码的CIDR
// 最多只记录255个值
func ExtractIP(ipStrings string) ([]string, error) {
ipStrings = strings.ReplaceAll(ipStrings, " ", "")
// CIDR
if strings.Contains(ipStrings, "/") {
_, cidrNet, err := net.ParseCIDR(ipStrings)
if err != nil {
return nil, err
}
var index = strings.Index(ipStrings, "/")
var ipFrom = ipStrings[:index]
var bits = types.Int(ipStrings[index+1:])
if bits < 24 {
return nil, errors.New("CIDR bits should be greater than 24")
}
var ipv4 = net.ParseIP(ipFrom).To4()
if len(ipv4) == 0 {
return nil, errors.New("support IPv4 only")
}
var result = []string{}
ipv4[3] = 0 // 从0开始
for i := 0; i <= 255; i++ {
if cidrNet.Contains(ipv4) {
result = append(result, ipv4.String())
}
ipv4 = NextIP(ipv4)
}
return result, nil
}
// IP Range
if strings.Contains(ipStrings, "-") {
var index = strings.Index(ipStrings, "-")
var ipFromString = ipStrings[:index]
var ipToString = ipStrings[index+1:]
var ipFrom = net.ParseIP(ipFromString).To4()
if len(ipFrom) == 0 {
return nil, errors.New("invalid ip '" + ipFromString + "'")
}
var ipTo = net.ParseIP(ipToString).To4()
if len(ipTo) == 0 {
return nil, errors.New("invalid ip '" + ipToString + "'")
}
if bytes.Compare(ipFrom, ipTo) > 0 {
ipFrom, ipTo = ipTo, ipFrom
}
var result = []string{}
for i := 0; i < 255; i++ {
if bytes.Compare(ipFrom, ipTo) > 0 {
break
}
result = append(result, ipFrom.String())
ipFrom = NextIP(ipFrom)
}
return result, nil
}
return []string{ipStrings}, nil
}
// NextIP IP增加1
func NextIP(prevIP net.IP) net.IP {
var ip = make(net.IP, len(prevIP))
copy(ip, prevIP)
var index = len(ip) - 1
for {
if ip[index] == 255 {
ip[index] = 0
index--
if index < 0 {
break
}
} else {
ip[index]++
break
}
}
return ip
}

View File

@@ -1,6 +1,7 @@
package utils
import (
"net"
"testing"
)
@@ -83,3 +84,27 @@ func TestIsIPv6(t *testing.T) {
}
}
}
func TestExtractIP(t *testing.T) {
t.Log(ExtractIP("192.168.1.100"))
}
func TestExtractIP_CIDR(t *testing.T) {
t.Log(ExtractIP("192.168.2.100/24"))
}
func TestExtractIP_Range(t *testing.T) {
t.Log(ExtractIP("192.168.2.100 - 192.168.4.2"))
}
func TestNextIP(t *testing.T) {
for _, ip := range []string{"192.168.1.1", "0.0.0.0", "255.255.255.255", "192.168.2.255", "192.168.255.255"} {
t.Log(ip+":", NextIP(net.ParseIP(ip).To4()))
}
}
func TestNextIP_Copy(t *testing.T) {
var ip = net.ParseIP("192.168.1.100")
var nextIP = NextIP(ip)
t.Log(ip, nextIP)
}

24
internal/utils/json.go Normal file
View File

@@ -0,0 +1,24 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package utils
import (
"encoding/json"
"reflect"
)
// JSONClone 使用JSON克隆对象
func JSONClone(v interface{}) (interface{}, error) {
data, err := json.Marshal(v)
if err != nil {
return nil, err
}
var nv = reflect.New(reflect.TypeOf(v).Elem()).Interface()
err = json.Unmarshal(data, nv)
if err != nil {
return nil, err
}
return nv, nil
}

View File

@@ -0,0 +1,25 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package utils_test
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"testing"
)
func TestJSONClone(t *testing.T) {
type A struct {
B int `json:"b"`
C string `json:"c"`
}
var a = &A{B: 123, C: "456"}
for i := 0; i < 5; i++ {
c, err := utils.JSONClone(a)
if err != nil {
t.Fatal(err)
}
t.Logf("%p, %#v", c, c)
}
}

View File

@@ -1,6 +1,7 @@
package utils
import (
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/miekg/dns"
)
@@ -16,13 +17,19 @@ func LookupCNAME(host string) (string, error) {
m.SetQuestion(host+".", dns.TypeCNAME)
m.RecursionDesired = true
r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port)
if err != nil {
return "", err
}
if len(r.Answer) == 0 {
return "", nil
}
return r.Answer[0].(*dns.CNAME).Target, nil
var lastErr error
for _, serverAddr := range config.Servers {
r, _, err := c.Exchange(m, configutils.QuoteIP(serverAddr)+":"+config.Port)
if err != nil {
lastErr = err
continue
}
if len(r.Answer) == 0 {
continue
}
return r.Answer[0].(*dns.CNAME).Target, nil
}
return "", lastErr
}

View File

@@ -0,0 +1,27 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build community
// +build community
package nodelogutils
import (
"github.com/iwind/TeaGo/maps"
)
// FindCommonTags 查找常用的标签
func FindNodeCommonTags() []maps.Map {
return []maps.Map{
{
"name": "端口监听",
"code": "LISTENER",
},
{
"name": "WAF",
"code": "WAF",
},
{
"name": "访问日志",
"code": "ACCESS_LOG",
},
}
}

View File

@@ -2,6 +2,7 @@ package numberutils
import (
"fmt"
"github.com/iwind/TeaGo/types"
"strconv"
)
@@ -13,38 +14,63 @@ func FormatInt(value int) string {
return strconv.Itoa(value)
}
func Pow1024(n int) int64 {
if n <= 0 {
return 1
}
if n == 1 {
return 1024
}
return Pow1024(n-1) * 1024
}
func FormatBytes(bytes int64) string {
if bytes < 1024 {
if bytes < Pow1024(1) {
return FormatInt64(bytes) + "B"
} else if bytes < 1024*1024 {
return fmt.Sprintf("%.2fKB", float64(bytes)/1024)
} else if bytes < 1024*1024*1024 {
return fmt.Sprintf("%.2fMB", float64(bytes)/1024/1024)
} else if bytes < 1024*1024*1024*1024 {
return fmt.Sprintf("%.2fGB", float64(bytes)/1024/1024/1024)
} else if bytes < 1024*1024*1024*1024*1024 {
return fmt.Sprintf("%.2fTB", float64(bytes)/1024/1024/1024/1024)
} else if bytes < 1024*1024*1024*1024*1024*1024 {
return fmt.Sprintf("%.2fPB", float64(bytes)/1024/1024/1024/1024/1024)
} else if bytes < Pow1024(2) {
return fmt.Sprintf("%.2fKB", float64(bytes)/float64(Pow1024(1)))
} else if bytes < Pow1024(3) {
return fmt.Sprintf("%.2fMB", float64(bytes)/float64(Pow1024(2)))
} else if bytes < Pow1024(4) {
return fmt.Sprintf("%.2fGB", float64(bytes)/float64(Pow1024(3)))
} else if bytes < Pow1024(5) {
return fmt.Sprintf("%.2fTB", float64(bytes)/float64(Pow1024(4)))
} else if bytes < Pow1024(6) {
return fmt.Sprintf("%.2fPB", float64(bytes)/float64(Pow1024(5)))
} else {
return fmt.Sprintf("%.2fEB", float64(bytes)/1024/1024/1024/1024/1024/1024)
return fmt.Sprintf("%.2fEB", float64(bytes)/float64(Pow1024(6)))
}
}
func FormatBits(bits int64) string {
if bits < 1000 {
return FormatInt64(bits) + "B"
} else if bits < 1000*1000 {
return fmt.Sprintf("%.2fKB", float64(bits)/1000)
} else if bits < 1000*1000*1000 {
return fmt.Sprintf("%.2fMB", float64(bits)/1000/1000)
} else if bits < 1000*1000*1000*1000 {
return fmt.Sprintf("%.2fGB", float64(bits)/1000/1000/1000)
} else if bits < 1000*1000*1000*1000*1000 {
return fmt.Sprintf("%.2fTB", float64(bits)/1000/1000/1000/1000)
} else if bits < 1000*1000*1000*1000*1000*1000 {
return fmt.Sprintf("%.2fPB", float64(bits)/1000/1000/1000/1000/1000)
} else {
return fmt.Sprintf("%.2fEB", float64(bits)/1000/1000/1000/1000/1000/1000)
func FormatCount(count int64) string {
if count < 1000 {
return types.String(count)
}
if count < 1000*1000 {
return fmt.Sprintf("%.1fK", float32(count)/1000)
}
if count < 1000*1000*1000 {
return fmt.Sprintf("%.1fM", float32(count)/1000/1000)
}
return fmt.Sprintf("%.1fB", float32(count)/1000/1000/1000)
}
func FormatFloat(f interface{}, decimal int) string {
if f == nil {
return ""
}
switch x := f.(type) {
case float32, float64:
var s = fmt.Sprintf("%."+types.String(decimal)+"f", x)
return s
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
return types.String(x)
case string:
return x
}
return ""
}
func FormatFloat2(f interface{}) string {
return FormatFloat(f, 2)
}

View File

@@ -0,0 +1,36 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package numberutils_test
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"testing"
)
func TestFormatBytes(t *testing.T) {
t.Log(numberutils.FormatBytes(1))
t.Log(numberutils.FormatBytes(1000))
t.Log(numberutils.FormatBytes(1_000_000))
t.Log(numberutils.FormatBytes(1_000_000_000))
t.Log(numberutils.FormatBytes(1_000_000_000_000))
t.Log(numberutils.FormatBytes(1_000_000_000_000_000))
t.Log(numberutils.FormatBytes(1_000_000_000_000_000_000))
t.Log(numberutils.FormatBytes(9_000_000_000_000_000_000))
}
func TestFormatCount(t *testing.T) {
t.Log(numberutils.FormatCount(1))
t.Log(numberutils.FormatCount(1000))
t.Log(numberutils.FormatCount(1500))
t.Log(numberutils.FormatCount(1_000_000))
t.Log(numberutils.FormatCount(1_500_000))
t.Log(numberutils.FormatCount(1_000_000_000))
t.Log(numberutils.FormatCount(1_500_000_000))
}
func TestFormatFloat(t *testing.T) {
t.Log(numberutils.FormatFloat(1, 2))
t.Log(numberutils.FormatFloat(100.23456, 2))
t.Log(numberutils.FormatFloat(100.000023, 2))
t.Log(numberutils.FormatFloat(100.012, 2))
}

View File

@@ -0,0 +1,10 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package sizes
const (
K int64 = 1024
M = 1024 * K
G = 1024 * M
T = 1024 * G
)

View File

@@ -0,0 +1,17 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package sizes_test
import (
"github.com/TeaOSLab/EdgeNode/internal/utils/sizes"
"github.com/iwind/TeaGo/assert"
"testing"
)
func TestSizes(t *testing.T) {
var a = assert.NewAssertion(t)
a.IsTrue(sizes.K == 1024)
a.IsTrue(sizes.M == 1024*1024)
a.IsTrue(sizes.G == 1024*1024*1024)
a.IsTrue(sizes.T == 1024*1024*1024*1024)
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
@@ -19,6 +20,8 @@ type ParentAction struct {
actions.ActionObject
rpcClient *rpc.RPCClient
ctx context.Context
}
// Parent 可以调用自身的一个简便方法
@@ -53,7 +56,14 @@ func (this *ParentAction) NewPage(total int64, size ...int64) *Page {
if len(size) > 0 {
return NewActionPage(this, total, size[0])
}
return NewActionPage(this, total, 10)
var pageSize int64 = 10
adminConfig, err := configloaders.LoadAdminUIConfig()
if err == nil && adminConfig.DefaultPageSize > 0 {
pageSize = int64(adminConfig.DefaultPageSize)
}
return NewActionPage(this, total, pageSize)
}
func (this *ParentAction) Nav(mainMenu string, tab string, firstMenu string) {
@@ -117,6 +127,10 @@ func (this *ParentAction) RPC() *rpc.RPCClient {
// AdminContext 获取Context
func (this *ParentAction) AdminContext() context.Context {
if this.ctx != nil {
return this.ctx
}
if this.rpcClient == nil {
rpcClient, err := rpc.SharedRPC()
if err != nil {
@@ -125,7 +139,8 @@ func (this *ParentAction) AdminContext() context.Context {
}
this.rpcClient = rpcClient
}
return this.rpcClient.Context(this.AdminId())
this.ctx = this.rpcClient.Context(this.AdminId())
return this.ctx
}
// ViewData 视图里可以使用的数据

View File

@@ -13,16 +13,35 @@ type DeleteAction struct {
func (this *DeleteAction) RunPost(params struct {
NodeId int64
}) {
// TODO 检查权限
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "删除API节点 %d", params.NodeId)
_, err := this.RPC().APINodeRPC().DeleteAPINode(this.AdminContext(), &pb.DeleteAPINodeRequest{NodeId: params.NodeId})
// 检查是否是唯一的节点
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
var apiNode = nodeResp.ApiNode
if apiNode == nil {
this.Success()
}
if apiNode.IsOn {
countResp, err := this.RPC().APINodeRPC().CountAllEnabledAndOnAPINodes(this.AdminContext(), &pb.CountAllEnabledAndOnAPINodesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
if countResp.Count == 1 {
this.Fail("无法删除此节点必须保留至少一个可用的API节点")
}
}
_, err = this.RPC().APINodeRPC().DeleteAPINode(this.AdminContext(), &pb.DeleteAPINodeRequest{ApiNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "删除API节点 %d", params.NodeId)
this.Success()
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
@@ -26,11 +27,11 @@ func (this *IndexAction) RunGet(params struct{}) {
this.ErrorPage(err)
return
}
count := countResp.Count
page := this.NewPage(count)
var count = countResp.Count
var page = this.NewPage(count)
this.Data["page"] = page.AsHTML()
nodeMaps := []maps.Map{}
var nodeMaps = []maps.Map{}
if count > 0 {
nodesResp, err := this.RPC().APINodeRPC().ListEnabledAPINodes(this.AdminContext(), &pb.ListEnabledAPINodesRequest{
Offset: page.Offset,
@@ -41,7 +42,7 @@ func (this *IndexAction) RunGet(params struct{}) {
return
}
for _, node := range nodesResp.Nodes {
for _, node := range nodesResp.ApiNodes {
// 状态
status := &nodeconfigs.NodeStatus{}
if len(node.StatusJSON) > 0 {
@@ -91,6 +92,7 @@ func (this *IndexAction) RunGet(params struct{}) {
"name": node.Name,
"accessAddrs": node.AccessAddrs,
"restAccessAddrs": restAccessAddrs,
"isPrimary": node.IsPrimary,
"status": maps.Map{
"isActive": status.IsActive,
"updatedAt": status.UpdatedAt,
@@ -106,5 +108,13 @@ func (this *IndexAction) RunGet(params struct{}) {
}
this.Data["nodes"] = nodeMaps
// 检查是否有调试数据
countMethodStatsResp, err := this.RPC().APIMethodStatRPC().CountAPIMethodStatsWithDay(this.AdminContext(), &pb.CountAPIMethodStatsWithDayRequest{Day: timeutil.Format("Ymd")})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["hasMethodStats"] = countMethodStatsResp.Count > 0
this.Show()
}

View File

@@ -16,6 +16,7 @@ func init() {
Helper(settingutils.NewAdvancedHelper("apiNodes")).
Prefix("/api").
Get("", new(IndexAction)).
Get("/methodStats", new(MethodStatsAction)).
GetPost("/node/createPopup", new(node.CreatePopupAction)).
Post("/delete", new(DeleteAction)).
EndAll()

View File

@@ -0,0 +1,78 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package api
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"sort"
"strings"
)
type MethodStatsAction struct {
actionutils.ParentAction
}
func (this *MethodStatsAction) Init() {
this.Nav("", "", "")
}
func (this *MethodStatsAction) RunGet(params struct {
Order string
Method string
Tag string
}) {
this.Data["order"] = params.Order
this.Data["method"] = params.Method
this.Data["tag"] = params.Tag
statsResp, err := this.RPC().APIMethodStatRPC().FindAPIMethodStatsWithDay(this.AdminContext(), &pb.FindAPIMethodStatsWithDayRequest{Day: timeutil.Format("Ymd")})
if err != nil {
this.ErrorPage(err)
return
}
var pbStats = statsResp.ApiMethodStats
switch params.Order {
case "method":
sort.Slice(pbStats, func(i, j int) bool {
return pbStats[i].Method < pbStats[j].Method
})
case "costMs.desc":
sort.Slice(pbStats, func(i, j int) bool {
return pbStats[i].CostMs > pbStats[j].CostMs
})
case "peekMs.desc":
sort.Slice(pbStats, func(i, j int) bool {
return pbStats[i].PeekMs > pbStats[j].PeekMs
})
case "calls.desc":
sort.Slice(pbStats, func(i, j int) bool {
return pbStats[i].CountCalls > pbStats[j].CountCalls
})
}
var statMaps = []maps.Map{}
for _, stat := range pbStats {
if len(params.Method) > 0 && !strings.Contains(strings.ToLower(stat.Method), strings.ToLower(params.Method)) {
continue
}
if len(params.Tag) > 0 && !strings.Contains(strings.ToLower(stat.Tag), strings.ToLower(params.Tag)) {
continue
}
statMaps = append(statMaps, maps.Map{
"id": stat.Id,
"method": stat.Method,
"tag": stat.Tag,
"costMs": stat.CostMs,
"peekMs": stat.PeekMs,
"countCalls": stat.CountCalls,
})
}
this.Data["stats"] = statMaps
this.Show()
}

View File

@@ -180,7 +180,7 @@ func (this *CreatePopupAction) RunPost(params struct {
}
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "创建API节点 %d", createResp.NodeId)
defer this.CreateLog(oplogs.LevelInfo, "创建API节点 %d", createResp.ApiNodeId)
this.Success()
}

View File

@@ -20,12 +20,12 @@ func (this *IndexAction) Init() {
func (this *IndexAction) RunGet(params struct {
NodeId int64
}) {
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{NodeId: params.NodeId})
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
node := nodeResp.Node
node := nodeResp.ApiNode
if node == nil {
this.NotFound("apiNode", params.NodeId)
return
@@ -129,6 +129,7 @@ func (this *IndexAction) RunGet(params struct {
"restAccessAddrs": restAccessAddrs,
"hasHTTPS": hasHTTPS,
"certs": certs,
"isPrimary": node.IsPrimary,
}
this.Show()

View File

@@ -21,12 +21,12 @@ func (this *InstallAction) RunGet(params struct {
NodeId int64
}) {
// API节点信息
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{NodeId: params.NodeId})
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
node := nodeResp.Node
node := nodeResp.ApiNode
if node == nil {
this.NotFound("apiNode", params.NodeId)
return

View File

@@ -31,12 +31,12 @@ func (this *LogsAction) RunGet(params struct {
this.Data["keyword"] = params.Keyword
this.Data["level"] = params.Level
apiNodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{NodeId: params.NodeId})
apiNodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
apiNode := apiNodeResp.Node
apiNode := apiNodeResp.ApiNode
if apiNode == nil {
this.NotFound("apiNode", params.NodeId)
return

View File

@@ -23,19 +23,19 @@ func (this *UpdateAction) RunGet(params struct {
NodeId int64
}) {
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{
NodeId: params.NodeId,
ApiNodeId: params.NodeId,
})
if err != nil {
this.ErrorPage(err)
return
}
node := nodeResp.Node
var node = nodeResp.ApiNode
if node == nil {
this.WriteString("要操作的节点不存在")
return
}
httpConfig := &serverconfigs.HTTPProtocolConfig{}
var httpConfig = &serverconfigs.HTTPProtocolConfig{}
if len(node.HttpJSON) > 0 {
err = json.Unmarshal(node.HttpJSON, httpConfig)
if err != nil {
@@ -43,7 +43,7 @@ func (this *UpdateAction) RunGet(params struct {
return
}
}
httpsConfig := &serverconfigs.HTTPSProtocolConfig{}
var httpsConfig = &serverconfigs.HTTPSProtocolConfig{}
if len(node.HttpsJSON) > 0 {
err = json.Unmarshal(node.HttpsJSON, httpsConfig)
if err != nil {
@@ -53,11 +53,11 @@ func (this *UpdateAction) RunGet(params struct {
}
// 监听地址
listens := []*serverconfigs.NetworkAddressConfig{}
var listens = []*serverconfigs.NetworkAddressConfig{}
listens = append(listens, httpConfig.Listen...)
listens = append(listens, httpsConfig.Listen...)
restHTTPConfig := &serverconfigs.HTTPProtocolConfig{}
var restHTTPConfig = &serverconfigs.HTTPProtocolConfig{}
if len(node.RestHTTPJSON) > 0 {
err = json.Unmarshal(node.RestHTTPJSON, restHTTPConfig)
if err != nil {
@@ -65,7 +65,7 @@ func (this *UpdateAction) RunGet(params struct {
return
}
}
restHTTPSConfig := &serverconfigs.HTTPSProtocolConfig{}
var restHTTPSConfig = &serverconfigs.HTTPSProtocolConfig{}
if len(node.RestHTTPSJSON) > 0 {
err = json.Unmarshal(node.RestHTTPSJSON, restHTTPSConfig)
if err != nil {
@@ -75,20 +75,20 @@ func (this *UpdateAction) RunGet(params struct {
}
// 监听地址
restListens := []*serverconfigs.NetworkAddressConfig{}
var restListens = []*serverconfigs.NetworkAddressConfig{}
restListens = append(restListens, restHTTPConfig.Listen...)
restListens = append(restListens, restHTTPSConfig.Listen...)
// 证书信息
certs := []*sslconfigs.SSLCertConfig{}
sslPolicyId := int64(0)
var certs = []*sslconfigs.SSLCertConfig{}
var sslPolicyId = int64(0)
if httpsConfig.SSLPolicyRef != nil && httpsConfig.SSLPolicyRef.SSLPolicyId > 0 {
sslPolicyConfigResp, err := this.RPC().SSLPolicyRPC().FindEnabledSSLPolicyConfig(this.AdminContext(), &pb.FindEnabledSSLPolicyConfigRequest{SslPolicyId: httpsConfig.SSLPolicyRef.SSLPolicyId})
if err != nil {
this.ErrorPage(err)
return
}
sslPolicyConfigJSON := sslPolicyConfigResp.SslPolicyJSON
var sslPolicyConfigJSON = sslPolicyConfigResp.SslPolicyJSON
if len(sslPolicyConfigJSON) > 0 {
sslPolicyId = httpsConfig.SSLPolicyRef.SSLPolicyId
@@ -102,7 +102,7 @@ func (this *UpdateAction) RunGet(params struct {
}
}
accessAddrs := []*serverconfigs.NetworkAddressConfig{}
var accessAddrs = []*serverconfigs.NetworkAddressConfig{}
if len(node.AccessAddrsJSON) > 0 {
err = json.Unmarshal(node.AccessAddrsJSON, &accessAddrs)
if err != nil {
@@ -122,6 +122,7 @@ func (this *UpdateAction) RunGet(params struct {
"certs": certs,
"sslPolicyId": sslPolicyId,
"accessAddrs": accessAddrs,
"isPrimary": node.IsPrimary,
}
this.Show()
@@ -139,6 +140,7 @@ func (this *UpdateAction) RunPost(params struct {
AccessAddrsJSON []byte
Description string
IsOn bool
IsPrimary bool
Must *actions.Must
}) {
@@ -146,11 +148,11 @@ func (this *UpdateAction) RunPost(params struct {
Field("name", params.Name).
Require("请输入API节点名称")
httpConfig := &serverconfigs.HTTPProtocolConfig{}
httpsConfig := &serverconfigs.HTTPSProtocolConfig{}
var httpConfig = &serverconfigs.HTTPProtocolConfig{}
var httpsConfig = &serverconfigs.HTTPSProtocolConfig{}
// 监听地址
listens := []*serverconfigs.NetworkAddressConfig{}
var listens = []*serverconfigs.NetworkAddressConfig{}
err := json.Unmarshal(params.ListensJSON, &listens)
if err != nil {
this.ErrorPage(err)
@@ -170,8 +172,8 @@ func (this *UpdateAction) RunPost(params struct {
}
// Rest监听地址
restHTTPConfig := &serverconfigs.HTTPProtocolConfig{}
restHTTPSConfig := &serverconfigs.HTTPSProtocolConfig{}
var restHTTPConfig = &serverconfigs.HTTPProtocolConfig{}
var restHTTPSConfig = &serverconfigs.HTTPSProtocolConfig{}
if params.RestIsOn {
restListens := []*serverconfigs.NetworkAddressConfig{}
err = json.Unmarshal(params.RestListensJSON, &restListens)
@@ -191,7 +193,7 @@ func (this *UpdateAction) RunPost(params struct {
}
// 证书
certIds := []int64{}
var certIds = []int64{}
if len(params.CertIdsJSON) > 0 {
err = json.Unmarshal(params.CertIdsJSON, &certIds)
if err != nil {
@@ -203,7 +205,7 @@ func (this *UpdateAction) RunPost(params struct {
this.Fail("请添加至少一个证书")
}
certRefs := []*sslconfigs.SSLCertRef{}
var certRefs = []*sslconfigs.SSLCertRef{}
for _, certId := range certIds {
certRefs = append(certRefs, &sslconfigs.SSLCertRef{
IsOn: true,
@@ -217,7 +219,7 @@ func (this *UpdateAction) RunPost(params struct {
}
// 创建策略
sslPolicyId := params.SslPolicyId
var sslPolicyId = params.SslPolicyId
if sslPolicyId == 0 {
if len(certIds) > 0 {
sslPolicyCreateResp, err := this.RPC().SSLPolicyRPC().CreateSSLPolicy(this.AdminContext(), &pb.CreateSSLPolicyRequest{
@@ -249,7 +251,7 @@ func (this *UpdateAction) RunPost(params struct {
}
// 访问地址
accessAddrs := []*serverconfigs.NetworkAddressConfig{}
var accessAddrs = []*serverconfigs.NetworkAddressConfig{}
err = json.Unmarshal(params.AccessAddrsJSON, &accessAddrs)
if err != nil {
this.ErrorPage(err)
@@ -281,7 +283,7 @@ func (this *UpdateAction) RunPost(params struct {
}
_, err = this.RPC().APINodeRPC().UpdateAPINode(this.AdminContext(), &pb.UpdateAPINodeRequest{
NodeId: params.NodeId,
ApiNodeId: params.NodeId,
Name: params.Name,
Description: params.Description,
HttpJSON: httpJSON,
@@ -291,6 +293,7 @@ func (this *UpdateAction) RunPost(params struct {
RestHTTPSJSON: restHTTPSJSON,
AccessAddrsJSON: params.AccessAddrsJSON,
IsOn: params.IsOn,
IsPrimary: params.IsPrimary,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -3,6 +3,7 @@ package cluster
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
@@ -75,7 +76,7 @@ func (this *CreateNodeAction) RunGet(params struct {
this.ErrorPage(err)
return
}
apiNodes := apiNodesResp.Nodes
apiNodes := apiNodesResp.ApiNodes
apiEndpoints := []string{}
for _, apiNode := range apiNodes {
if !apiNode.IsOn {
@@ -167,9 +168,13 @@ func (this *CreateNodeAction) RunPost(params struct {
nodeId := createResp.NodeId
// IP地址
var resultIPAddresses = []string{}
for _, addr := range ipAddresses {
var resultAddrIds = []int64{}
addrId := addr.GetInt64("id")
if addrId > 0 {
resultAddrIds = append(resultAddrIds, addrId)
_, err = this.RPC().NodeIPAddressRPC().UpdateNodeIPAddressNodeId(this.AdminContext(), &pb.UpdateNodeIPAddressNodeIdRequest{
NodeIPAddressId: addrId,
NodeId: nodeId,
@@ -178,20 +183,50 @@ func (this *CreateNodeAction) RunPost(params struct {
this.ErrorPage(err)
return
}
resultIPAddresses = append(resultIPAddresses, addr.GetString("ip"))
} else {
createResp, err := this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{
NodeId: nodeId,
Role: nodeconfigs.NodeRoleNode,
Name: addr.GetString("name"),
Ip: addr.GetString("ip"),
CanAccess: addr.GetBool("canAccess"),
IsUp: addr.GetBool("isUp"),
})
var ipStrings = addr.GetString("ip")
result, err := utils.ExtractIP(ipStrings)
if err != nil {
this.ErrorPage(err)
return
this.Fail("节点创建成功但是保存IP失败" + err.Error())
}
resultIPAddresses = append(resultIPAddresses, result...)
if len(result) == 1 {
// 单个创建
createResp, err := this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{
NodeId: nodeId,
Role: nodeconfigs.NodeRoleNode,
Name: addr.GetString("name"),
Ip: result[0],
CanAccess: addr.GetBool("canAccess"),
IsUp: addr.GetBool("isUp"),
})
if err != nil {
this.ErrorPage(err)
return
}
addrId = createResp.NodeIPAddressId
resultAddrIds = append(resultAddrIds, addrId)
} else if len(result) > 1 {
// 批量创建
createResp, err := this.RPC().NodeIPAddressRPC().CreateNodeIPAddresses(this.AdminContext(), &pb.CreateNodeIPAddressesRequest{
NodeId: nodeId,
Role: nodeconfigs.NodeRoleNode,
Name: addr.GetString("name"),
IpList: result,
CanAccess: addr.GetBool("canAccess"),
IsUp: addr.GetBool("isUp"),
GroupValue: ipStrings,
})
if err != nil {
this.ErrorPage(err)
return
}
resultAddrIds = append(resultAddrIds, createResp.NodeIPAddressIds...)
}
addrId = createResp.NodeIPAddressId
}
// 阈值
@@ -202,13 +237,16 @@ func (this *CreateNodeAction) RunPost(params struct {
this.ErrorPage(err)
return
}
_, err = this.RPC().NodeIPAddressThresholdRPC().UpdateAllNodeIPAddressThresholds(this.AdminContext(), &pb.UpdateAllNodeIPAddressThresholdsRequest{
NodeIPAddressId: addrId,
NodeIPAddressThresholdsJSON: thresholdsJSON,
})
if err != nil {
this.ErrorPage(err)
return
for _, addrId := range resultAddrIds {
_, err = this.RPC().NodeIPAddressThresholdRPC().UpdateAllNodeIPAddressThresholds(this.AdminContext(), &pb.UpdateAllNodeIPAddressThresholdsRequest{
NodeIPAddressId: addrId,
NodeIPAddressThresholdsJSON: thresholdsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
}
}
}
@@ -224,11 +262,6 @@ func (this *CreateNodeAction) RunPost(params struct {
return
}
if nodeResp.Node != nil {
var addresses = []string{}
for _, addrMap := range ipAddresses {
addresses = append(addresses, addrMap.GetString("ip"))
}
var grantMap maps.Map = nil
grantId := params.GrantId
if grantId > 0 {
@@ -253,7 +286,7 @@ func (this *CreateNodeAction) RunPost(params struct {
"name": nodeResp.Node.Name,
"uniqueId": nodeResp.Node.UniqueId,
"secret": nodeResp.Node.Secret,
"addresses": addresses,
"addresses": resultIPAddresses,
"login": maps.Map{
"id": 0,
"name": "SSH",

View File

@@ -19,6 +19,7 @@ func init() {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
Helper(clusters.NewClusterHelper()).
Data("teaMenu", "clusters").
Prefix("/clusters/cluster").
Get("", new(IndexAction)).
Get("/nodes", new(NodesAction)).
@@ -53,6 +54,7 @@ func init() {
GetPost("/settings/dns", new(dns.IndexAction)).
GetPost("/settings/system", new(system.IndexAction)).
GetPost("/settings/ssh", new(ssh.IndexAction)).
GetPost("/settings/ssh/test", new(ssh.TestAction)).
GetPost("/settings/thresholds", new(thresholds.IndexAction)).
// 分组相关

View File

@@ -49,7 +49,7 @@ func (this *InstallNodesAction) RunGet(params struct {
this.ErrorPage(err)
return
}
for _, apiNode := range apiNodesResp.Nodes {
for _, apiNode := range apiNodesResp.ApiNodes {
if !apiNode.IsOn {
continue
}

View File

@@ -33,7 +33,7 @@ func (this *DetailAction) RunGet(params struct {
this.ErrorPage(err)
return
}
node := nodeResp.Node
var node = nodeResp.Node
if node == nil {
this.WriteString("找不到要操作的节点")
return
@@ -86,9 +86,16 @@ func (this *DetailAction) RunGet(params struct {
return
}
// 是否有备用IP
var originIP = addr.Ip
if len(addr.BackupIP) > 0 {
addr.Ip = addr.BackupIP
}
ipAddressMaps = append(ipAddressMaps, maps.Map{
"id": addr.Id,
"name": addr.Name,
"originIP": originIP,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
"isOn": addr.IsOn,
@@ -190,7 +197,7 @@ func (this *DetailAction) RunGet(params struct {
}
// 运行状态
status := &nodeconfigs.NodeStatus{}
var status = &nodeconfigs.NodeStatus{}
if len(node.StatusJSON) > 0 {
err = json.Unmarshal(node.StatusJSON, &status)
if err != nil {
@@ -278,6 +285,8 @@ func (this *DetailAction) RunGet(params struct {
"isOn": node.IsOn,
"records": recordMaps,
"routes": routeMaps,
"level": node.Level,
"levelInfo": nodeconfigs.FindNodeLevel(int(node.Level)),
"status": maps.Map{
"isActive": status.IsActive,
@@ -291,9 +300,9 @@ func (this *DetailAction) RunGet(params struct {
"buildVersion": status.BuildVersion,
"cpuPhysicalCount": status.CPUPhysicalCount,
"cpuLogicalCount": status.CPULogicalCount,
"load1m": fmt.Sprintf("%.2f", status.Load1m),
"load5m": fmt.Sprintf("%.2f", status.Load5m),
"load15m": fmt.Sprintf("%.2f", status.Load15m),
"load1m": numberutils.FormatFloat2(status.Load1m),
"load5m": numberutils.FormatFloat2(status.Load5m),
"load15m": numberutils.FormatFloat2(status.Load15m),
"cacheTotalDiskSize": numberutils.FormatBytes(status.CacheTotalDiskSize),
"cacheTotalMemorySize": numberutils.FormatBytes(status.CacheTotalMemorySize),
},

View File

@@ -70,7 +70,7 @@ func (this *InstallAction) RunGet(params struct {
this.ErrorPage(err)
return
}
apiNodes := apiNodesResp.Nodes
apiNodes := apiNodesResp.ApiNodes
apiEndpoints := []string{}
for _, apiNode := range apiNodes {
if !apiNode.IsOn {

View File

@@ -1,8 +1,10 @@
package node
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils/nodelogutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
@@ -23,6 +25,7 @@ func (this *LogsAction) RunGet(params struct {
DayTo string
Keyword string
Level string
Tag string
}) {
// 初始化节点信息(用于菜单)
_, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
@@ -31,19 +34,23 @@ func (this *LogsAction) RunGet(params struct {
return
}
this.Data["tags"] = nodelogutils.FindNodeCommonTags()
this.Data["nodeId"] = params.NodeId
this.Data["dayFrom"] = params.DayFrom
this.Data["dayTo"] = params.DayTo
this.Data["keyword"] = params.Keyword
this.Data["level"] = params.Level
this.Data["tag"] = params.Tag
countResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
Role: "node",
Role: nodeconfigs.NodeRoleNode,
NodeId: params.NodeId,
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
Tag: params.Tag,
})
if err != nil {
this.ErrorPage(err)
@@ -59,6 +66,7 @@ func (this *LogsAction) RunGet(params struct {
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
Tag: params.Tag,
Offset: page.Offset,
Size: page.Size,
})
@@ -69,6 +77,20 @@ func (this *LogsAction) RunGet(params struct {
logs := []maps.Map{}
for _, log := range logsResp.NodeLogs {
// 服务信息
var serverMap = maps.Map{"id": 0}
if log.ServerId > 0 {
serverResp, err := this.RPC().ServerRPC().FindEnabledUserServerBasic(this.AdminContext(), &pb.FindEnabledUserServerBasicRequest{ServerId: log.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
var server = serverResp.Server
if server != nil {
serverMap = maps.Map{"id": server.Id, "name": server.Name}
}
}
logs = append(logs, maps.Map{
"tag": log.Tag,
"description": log.Description,
@@ -76,6 +98,7 @@ func (this *LogsAction) RunGet(params struct {
"level": log.Level,
"isToday": timeutil.FormatTime("Y-m-d", log.CreatedAt) == timeutil.Format("Y-m-d"),
"count": log.Count,
"server": serverMap,
})
}
this.Data["logs"] = logs

View File

@@ -4,6 +4,7 @@ package nodeutils
import (
"errors"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
@@ -37,6 +38,7 @@ func InitNodeInfo(parentAction *actionutils.ParentAction, nodeId int64) (*pb.Nod
"isOn": node.IsOn,
"isUp": node.IsUp,
"group": groupMap,
"level": node.Level,
}
var clusterId int64 = 0
if node.NodeCluster != nil {
@@ -49,7 +51,7 @@ func InitNodeInfo(parentAction *actionutils.ParentAction, nodeId int64) (*pb.Nod
var query = "clusterId=" + types.String(clusterId) + "&nodeId=" + types.String(nodeId)
var menuItem = parentAction.Data.GetString("secondMenuItem")
parentAction.Data["leftMenuItems"] = []maps.Map{
var menuItems = []maps.Map{
{
"name": "基础设置",
"url": prefix + "/update?" + query,
@@ -59,28 +61,41 @@ func InitNodeInfo(parentAction *actionutils.ParentAction, nodeId int64) (*pb.Nod
"name": "DNS设置",
"url": prefix + "/settings/dns?" + query,
"isActive": menuItem == "dns",
"isOn": len(node.DnsRoutes) > 0,
},
{
"name": "缓存设置",
"url": prefix + "/settings/cache?" + query,
"isActive": menuItem == "cache",
"isOn": len(node.CacheDiskDir) > 0 ||
(node.MaxCacheDiskCapacity != nil && node.MaxCacheDiskCapacity.Count > 0) ||
(node.MaxCacheMemoryCapacity != nil && node.MaxCacheMemoryCapacity.Count > 0),
},
{
"name": "阈值设置",
"url": prefix + "/settings/thresholds?" + query,
"isActive": menuItem == "threshold",
},
}
if teaconst.IsPlus {
menuItems = append(menuItems, []maps.Map{
{
"name": "阈值设置",
"url": prefix + "/settings/thresholds?" + query,
"isActive": menuItem == "threshold",
},
}...)
}
menuItems = append(menuItems, []maps.Map{
{
"name": "SSH设置",
"url": prefix + "/settings/ssh?" + query,
"isActive": menuItem == "ssh",
"isOn": node.NodeLogin != nil,
},
{
"name": "系统设置",
"url": prefix + "/settings/system?" + query,
"isActive": menuItem == "system",
"isOn": node.MaxCPU > 0,
},
}
}...)
parentAction.Data["leftMenuItems"] = menuItems
return nodeResp.Node, nil
}

View File

@@ -17,7 +17,7 @@ type IndexAction struct {
}
func (this *IndexAction) Init() {
this.Nav("", "", "update")
this.Nav("", "node", "update")
this.SecondMenu("cache")
}
@@ -59,6 +59,7 @@ func (this *IndexAction) RunGet(params struct {
var nodeMap = this.Data["node"].(maps.Map)
nodeMap["maxCacheDiskCapacity"] = maxCacheDiskCapacity
nodeMap["cacheDiskDir"] = node.CacheDiskDir
nodeMap["maxCacheMemoryCapacity"] = maxCacheMemoryCapacity
this.Show()
@@ -67,6 +68,7 @@ func (this *IndexAction) RunGet(params struct {
func (this *IndexAction) RunPost(params struct {
NodeId int64
MaxCacheDiskCapacityJSON []byte
CacheDiskDir string
MaxCacheMemoryCapacityJSON []byte
Must *actions.Must
@@ -106,6 +108,7 @@ func (this *IndexAction) RunPost(params struct {
_, err := this.RPC().NodeRPC().UpdateNodeCache(this.AdminContext(), &pb.UpdateNodeCacheRequest{
NodeId: params.NodeId,
MaxCacheDiskCapacity: pbMaxCacheDiskCapacity,
CacheDiskDir: params.CacheDiskDir,
MaxCacheMemoryCapacity: pbMaxCacheMemoryCapacity,
})
if err != nil {

View File

@@ -16,7 +16,7 @@ type IndexAction struct {
}
func (this *IndexAction) Init() {
this.Nav("", "", "update")
this.Nav("", "node", "update")
this.SecondMenu("dns")
}

View File

@@ -10,6 +10,8 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"net"
"regexp"
)
type IndexAction struct {
@@ -17,7 +19,7 @@ type IndexAction struct {
}
func (this *IndexAction) Init() {
this.Nav("", "", "update")
this.Nav("", "node", "update")
this.SecondMenu("ssh")
}
@@ -33,7 +35,7 @@ func (this *IndexAction) RunGet(params struct {
// 登录信息
var loginMap maps.Map = nil
if node.NodeLogin != nil {
loginParams := maps.Map{}
var loginParams = maps.Map{}
if len(node.NodeLogin.Params) > 0 {
err = json.Unmarshal(node.NodeLogin.Params, &loginParams)
if err != nil {
@@ -42,7 +44,7 @@ func (this *IndexAction) RunGet(params struct {
}
}
grantMap := maps.Map{}
var grantMap = maps.Map{}
grantId := loginParams.GetInt64("grantId")
if grantId > 0 {
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: grantId})
@@ -70,6 +72,45 @@ func (this *IndexAction) RunGet(params struct {
}
}
if loginMap == nil {
addressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledNodeIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledNodeIPAddressesWithNodeIdRequest{NodeId: node.Id})
if err != nil {
this.ErrorPage(err)
return
}
if len(addressesResp.NodeIPAddresses) > 0 {
loginMap = maps.Map{
"id": 0,
"name": "",
"type": "ssh",
"params": maps.Map{
"host": addressesResp.NodeIPAddresses[0].Ip,
"port": 22,
"grantId": 0,
},
"grant": nil,
}
}
} else {
var loginParams = loginMap.GetMap("params")
if len(loginParams.GetString("host")) == 0 {
addressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledNodeIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledNodeIPAddressesWithNodeIdRequest{NodeId: node.Id})
if err != nil {
this.ErrorPage(err)
return
}
if len(addressesResp.NodeIPAddresses) > 0 {
loginParams["host"] = addressesResp.NodeIPAddresses[0].Ip
}
}
if loginParams.GetInt("port") == 0 {
loginParams["port"] = 22
}
loginMap["params"] = loginParams
}
var nodeMap = this.Data["node"].(maps.Map)
nodeMap["login"] = loginMap
@@ -89,8 +130,13 @@ func (this *IndexAction) RunPost(params struct {
}) {
defer this.CreateLogInfo("修改节点 %d SSH登录信息", params.NodeId)
// 检查IP地址
if regexp.MustCompile(`^\d+\.\d+\.\d+\.\d+$`).MatchString(params.SshHost) && net.ParseIP(params.SshHost) == nil {
this.Fail("SSH主机地址 '" + params.SshHost + "' IP格式错误")
}
// TODO 检查登录授权
loginInfo := &pb.NodeLogin{
var loginInfo = &pb.NodeLogin{
Id: params.LoginId,
Name: "SSH",
Type: "ssh",

View File

@@ -0,0 +1,31 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package ssh
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type TestAction struct {
actionutils.ParentAction
}
func (this *TestAction) RunPost(params struct {
GrantId int64
Host string
Port int32
}) {
resp, err := this.RPC().NodeGrantRPC().TestNodeGrant(this.AdminContext(), &pb.TestNodeGrantRequest{
NodeGrantId: params.GrantId,
Host: params.Host,
Port: params.Port,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["isOk"] = resp.IsOk
this.Data["error"] = resp.Error
this.Success()
}

View File

@@ -15,7 +15,7 @@ type IndexAction struct {
}
func (this *IndexAction) Init() {
this.Nav("", "", "update")
this.Nav("", "node", "update")
this.SecondMenu("system")
}

View File

@@ -15,7 +15,7 @@ type IndexAction struct {
}
func (this *IndexAction) Init() {
this.Nav("", "", "update")
this.Nav("", "node", "update")
this.SecondMenu("threshold")
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
)
type UpdateAction struct {
@@ -37,7 +38,7 @@ func (this *UpdateAction) RunGet(params struct {
this.ErrorPage(err)
return
}
node := nodeResp.Node
var node = nodeResp.Node
if node == nil {
this.WriteString("找不到要操作的节点")
return
@@ -97,7 +98,7 @@ func (this *UpdateAction) RunGet(params struct {
}
}
var m = maps.Map{
var nodeMap = maps.Map{
"id": node.Id,
"name": node.Name,
"ipAddresses": ipAddressMaps,
@@ -105,15 +106,16 @@ func (this *UpdateAction) RunGet(params struct {
"isOn": node.IsOn,
"group": groupMap,
"region": regionMap,
"level": node.Level,
}
if node.NodeCluster != nil {
m["primaryCluster"] = maps.Map{
nodeMap["primaryCluster"] = maps.Map{
"id": node.NodeCluster.Id,
"name": node.NodeCluster.Name,
}
} else {
m["primaryCluster"] = nil
nodeMap["primaryCluster"] = nil
}
if len(node.SecondaryNodeClusters) > 0 {
@@ -124,12 +126,14 @@ func (this *UpdateAction) RunGet(params struct {
"name": cluster.Name,
})
}
m["secondaryClusters"] = secondaryClusterMaps
nodeMap["secondaryClusters"] = secondaryClusterMaps
} else {
m["secondaryClusters"] = []interface{}{}
nodeMap["secondaryClusters"] = []interface{}{}
}
this.Data["node"] = m
this.Data["node"] = nodeMap
this.Data["canUpdateLevel"] = this.CanUpdateLevel(2)
this.Show()
}
@@ -144,11 +148,12 @@ func (this *UpdateAction) RunPost(params struct {
PrimaryClusterId int64
SecondaryClusterIds []byte
IsOn bool
Level int32
Must *actions.Must
}) {
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "修改节点 %d", params.NodeId)
defer this.CreateLog(oplogs.LevelInfo, "修改节点 %d 基本信息", params.NodeId)
if params.NodeId <= 0 {
this.Fail("要操作的节点不存在")
@@ -186,6 +191,10 @@ func (this *UpdateAction) RunPost(params struct {
}
// 保存
if !this.CanUpdateLevel(params.Level) {
this.Fail("没有权限修改节点级别:" + types.String(params.Level))
}
_, err := this.RPC().NodeRPC().UpdateNode(this.AdminContext(), &pb.UpdateNodeRequest{
NodeId: params.NodeId,
NodeGroupId: params.GroupId,
@@ -194,6 +203,7 @@ func (this *UpdateAction) RunPost(params struct {
NodeClusterId: params.PrimaryClusterId,
SecondaryNodeClusterIds: secondaryClusterIds,
IsOn: params.IsOn,
Level: params.Level,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -0,0 +1,9 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build !plus
// +build !plus
package node
func (this *UpdateAction) CanUpdateLevel(level int32) bool {
return level <= 1
}

View File

@@ -3,6 +3,8 @@ package cluster
import (
"encoding/json"
"fmt"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
@@ -29,17 +31,21 @@ func (this *NodesAction) RunGet(params struct {
InstalledState int
ActiveState int
Keyword string
Level int32
CpuOrder string
MemoryOrder string
TrafficInOrder string
TrafficOutOrder string
LoadOrder string
}) {
this.Data["groupId"] = params.GroupId
this.Data["regionId"] = params.RegionId
this.Data["installState"] = params.InstalledState
this.Data["activeState"] = params.ActiveState
this.Data["keyword"] = params.Keyword
this.Data["level"] = params.Level
this.Data["hasOrder"] = len(params.CpuOrder) > 0 || len(params.MemoryOrder) > 0 || len(params.TrafficInOrder) > 0 || len(params.TrafficOutOrder) > 0 || len(params.LoadOrder) > 0
// 集群是否已经设置了线路
clusterDNSResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeClusterDNS(this.AdminContext(), &pb.FindEnabledNodeClusterDNSRequest{NodeClusterId: params.ClusterId})
@@ -63,6 +69,7 @@ func (this *NodesAction) RunGet(params struct {
NodeClusterId: params.ClusterId,
NodeGroupId: params.GroupId,
NodeRegionId: params.RegionId,
Level: params.Level,
InstallState: types.Int32(params.InstalledState),
ActiveState: types.Int32(params.ActiveState),
Keyword: params.Keyword,
@@ -72,7 +79,7 @@ func (this *NodesAction) RunGet(params struct {
return
}
page := this.NewPage(countResp.Count)
var page = this.NewPage(countResp.Count)
this.Data["page"] = page.AsHTML()
var req = &pb.ListEnabledNodesMatchRequest{
@@ -81,6 +88,7 @@ func (this *NodesAction) RunGet(params struct {
NodeClusterId: params.ClusterId,
NodeGroupId: params.GroupId,
NodeRegionId: params.RegionId,
Level: params.Level,
InstallState: types.Int32(params.InstalledState),
ActiveState: types.Int32(params.ActiveState),
Keyword: params.Keyword,
@@ -101,13 +109,17 @@ func (this *NodesAction) RunGet(params struct {
req.TrafficOutAsc = true
} else if params.TrafficOutOrder == "desc" {
req.TrafficOutDesc = true
} else if params.LoadOrder == "asc" {
req.LoadAsc = true
} else if params.LoadOrder == "desc" {
req.LoadDesc = true
}
nodesResp, err := this.RPC().NodeRPC().ListEnabledNodesMatch(this.AdminContext(), req)
if err != nil {
this.ErrorPage(err)
return
}
nodeMaps := []maps.Map{}
var nodeMaps = []maps.Map{}
for _, node := range nodesResp.Nodes {
// 状态
isSynced := false
@@ -199,6 +211,7 @@ func (this *NodesAction) RunGet(params struct {
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
"trafficInBytes": status.TrafficInBytes,
"trafficOutBytes": status.TrafficOutBytes,
"load1m": numberutils.FormatFloat2(status.Load1m),
},
"cluster": maps.Map{
"id": node.NodeCluster.Id,
@@ -210,12 +223,13 @@ func (this *NodesAction) RunGet(params struct {
"group": groupMap,
"region": regionMap,
"dnsRouteNames": dnsRouteNames,
"level": node.Level,
})
}
this.Data["nodes"] = nodeMaps
// 所有分组
groupMaps := []maps.Map{}
var groupMaps = []maps.Map{}
groupsResp, err := this.RPC().NodeGroupRPC().FindAllEnabledNodeGroupsWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledNodeGroupsWithNodeClusterIdRequest{
NodeClusterId: params.ClusterId,
})
@@ -224,12 +238,12 @@ func (this *NodesAction) RunGet(params struct {
return
}
for _, group := range groupsResp.NodeGroups {
countResp, err := this.RPC().NodeRPC().CountAllEnabledNodesWithNodeGroupId(this.AdminContext(), &pb.CountAllEnabledNodesWithNodeGroupIdRequest{NodeGroupId: group.Id})
countNodesInGroupResp, err := this.RPC().NodeRPC().CountAllEnabledNodesWithNodeGroupId(this.AdminContext(), &pb.CountAllEnabledNodesWithNodeGroupIdRequest{NodeGroupId: group.Id})
if err != nil {
this.ErrorPage(err)
return
}
countNodes := countResp.Count
countNodes := countNodesInGroupResp.Count
groupName := group.Name
if countNodes > 0 {
groupName += "(" + strconv.FormatInt(countNodes, 10) + ")"
@@ -248,7 +262,7 @@ func (this *NodesAction) RunGet(params struct {
this.ErrorPage(err)
return
}
regionMaps := []maps.Map{}
var regionMaps = []maps.Map{}
for _, region := range regionsResp.NodeRegions {
regionMaps = append(regionMaps, maps.Map{
"id": region.Id,
@@ -257,6 +271,12 @@ func (this *NodesAction) RunGet(params struct {
}
this.Data["regions"] = regionMaps
// 级别
this.Data["levels"] = []maps.Map{}
if teaconst.IsPlus {
this.Data["levels"] = nodeconfigs.FindAllNodeLevels()
}
// 记录最近访问
_, err = this.RPC().LatestItemRPC().IncreaseLatestItem(this.AdminContext(), &pb.IncreaseLatestItemRequest{
ItemType: "cluster",

View File

@@ -0,0 +1,18 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package dns
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/rands"
)
type RandomNameAction struct {
actionutils.ParentAction
}
func (this *RandomNameAction) RunPost(params struct{}) {
this.Data["name"] = "cluster" + rands.HexString(8)
this.Success()
}

View File

@@ -34,6 +34,8 @@ func (this *CreatePopupAction) RunPost(params struct {
// ipset
IpsetWhiteName string
IpsetBlackName string
IpsetWhiteNameIPv6 string
IpsetBlackNameIPv6 string
IpsetAutoAddToIPTables bool
IpsetAutoAddToFirewalld bool
@@ -49,7 +51,7 @@ func (this *CreatePopupAction) RunPost(params struct {
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo("创建WAF动作")
defer this.CreateLogInfo("创建集群 %d 的WAF动作", params.ClusterId)
params.Must.
Field("name", params.Name).
@@ -66,11 +68,19 @@ func (this *CreatePopupAction) RunPost(params struct {
Match(`^\w+$`, "请输入正确的IPSet白名单名称").
Field("ipsetBlackName", params.IpsetBlackName).
Require("请输入IPSet黑名单名称").
Match(`^\w+$`, "请输入正确的IPSet黑名单名称")
Match(`^\w+$`, "请输入正确的IPSet黑名单名称").
Field("ipsetWhiteNameIPv6", params.IpsetWhiteNameIPv6).
Require("请输入IPSet IPv6白名单名称").
Match(`^\w+$`, "请输入正确的IPSet IPv6白名单名称").
Field("ipsetBlackNameIPv6", params.IpsetBlackNameIPv6).
Require("请输入IPSet IPv6黑名单名称").
Match(`^\w+$`, "请输入正确的IPSet IPv6黑名单名称")
actionParams = &firewallconfigs.FirewallActionIPSetConfig{
WhiteName: params.IpsetWhiteName,
BlackName: params.IpsetBlackName,
WhiteNameIPv6: params.IpsetWhiteNameIPv6,
BlackNameIPv6: params.IpsetBlackNameIPv6,
AutoAddToIPTables: params.IpsetAutoAddToIPTables,
AutoAddToFirewalld: params.IpsetAutoAddToFirewalld,
}

View File

@@ -63,6 +63,8 @@ func (this *UpdatePopupAction) RunPost(params struct {
// ipset
IpsetWhiteName string
IpsetBlackName string
IpsetWhiteNameIPv6 string
IpsetBlackNameIPv6 string
IpsetAutoAddToIPTables bool
IpsetAutoAddToFirewalld bool
@@ -95,11 +97,19 @@ func (this *UpdatePopupAction) RunPost(params struct {
Match(`^\w+$`, "请输入正确的IPSet白名单名称").
Field("ipsetBlackName", params.IpsetBlackName).
Require("请输入IPSet黑名单名称").
Match(`^\w+$`, "请输入正确的IPSet黑名单名称")
Match(`^\w+$`, "请输入正确的IPSet黑名单名称").
Field("ipsetWhiteNameIPv6", params.IpsetWhiteNameIPv6).
Require("请输入IPSet IPv6白名单名称").
Match(`^\w+$`, "请输入正确的IPSet IPv6白名单名称").
Field("ipsetBlackNameIPv6", params.IpsetBlackNameIPv6).
Require("请输入IPSet IPv6黑名单名称").
Match(`^\w+$`, "请输入正确的IPSet IPv6黑名单名称")
actionParams = &firewallconfigs.FirewallActionIPSetConfig{
WhiteName: params.IpsetWhiteName,
BlackName: params.IpsetBlackName,
WhiteNameIPv6: params.IpsetWhiteNameIPv6,
BlackNameIPv6: params.IpsetBlackNameIPv6,
AutoAddToIPTables: params.IpsetAutoAddToIPTables,
AutoAddToFirewalld: params.IpsetAutoAddToFirewalld,
}

View File

@@ -4,9 +4,11 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
)
type IndexAction struct {
@@ -21,6 +23,7 @@ func (this *IndexAction) Init() {
func (this *IndexAction) RunGet(params struct {
ClusterId int64
}) {
// 基本信息
clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
@@ -53,21 +56,44 @@ func (this *IndexAction) RunGet(params struct {
}
this.Data["grant"] = grantMap
this.Data["cluster"] = maps.Map{
"id": cluster.Id,
"name": cluster.Name,
"installDir": cluster.InstallDir,
// 时区
this.Data["timeZoneGroups"] = nodeconfigs.FindAllTimeZoneGroups()
this.Data["timeZoneLocations"] = nodeconfigs.FindAllTimeZoneLocations()
if len(cluster.TimeZone) == 0 {
cluster.TimeZone = nodeconfigs.DefaultTimeZoneLocation
}
this.Data["timeZoneLocation"] = nodeconfigs.FindTimeZoneLocation(cluster.TimeZone)
this.Data["cluster"] = maps.Map{
"id": cluster.Id,
"name": cluster.Name,
"installDir": cluster.InstallDir,
"timeZone": cluster.TimeZone,
"nodeMaxThreads": cluster.NodeMaxThreads,
"nodeTCPMaxConnections": cluster.NodeTCPMaxConnections,
"autoOpenPorts": cluster.AutoOpenPorts,
}
// 默认值
this.Data["defaultNodeMaxThreads"] = nodeconfigs.DefaultMaxThreads
this.Data["defaultNodeMaxThreadsMin"] = nodeconfigs.DefaultMaxThreadsMin
this.Data["defaultNodeMaxThreadsMax"] = nodeconfigs.DefaultMaxThreadsMax
this.Data["defaultNodeTCPMaxConnections"] = nodeconfigs.DefaultTCPMaxConnections
this.Show()
}
// RunPost 保存设置
func (this *IndexAction) RunPost(params struct {
ClusterId int64
Name string
GrantId int64
InstallDir string
ClusterId int64
Name string
GrantId int64
InstallDir string
TimeZone string
NodeMaxThreads int32
NodeTCPMaxConnections int32
AutoOpenPorts bool
Must *actions.Must
}) {
@@ -78,11 +104,22 @@ func (this *IndexAction) RunPost(params struct {
Field("name", params.Name).
Require("请输入集群名称")
if params.NodeMaxThreads > 0 {
params.Must.
Field("nodeMaxThreads", params.NodeMaxThreads).
Gte(int64(nodeconfigs.DefaultMaxThreadsMin), "单节点最大线程数最小值不能小于"+types.String(nodeconfigs.DefaultMaxThreadsMin)).
Lte(int64(nodeconfigs.DefaultMaxThreadsMax), "单节点最大线程数最大值不能大于"+types.String(nodeconfigs.DefaultMaxThreadsMax))
}
_, err := this.RPC().NodeClusterRPC().UpdateNodeCluster(this.AdminContext(), &pb.UpdateNodeClusterRequest{
NodeClusterId: params.ClusterId,
Name: params.Name,
NodeGrantId: params.GrantId,
InstallDir: params.InstallDir,
NodeClusterId: params.ClusterId,
Name: params.Name,
NodeGrantId: params.GrantId,
InstallDir: params.InstallDir,
TimeZone: params.TimeZone,
NodeMaxThreads: params.NodeMaxThreads,
NodeTCPMaxConnections: params.NodeTCPMaxConnections,
AutoOpenPorts: params.AutoOpenPorts,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -12,6 +12,7 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/thresholds"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/toa"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/waf"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/webp"
clusters "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/clusterutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
@@ -38,6 +39,7 @@ func init() {
// DNS
Prefix("/clusters/cluster/settings/dns").
GetPost("", new(dns.IndexAction)).
Post("/randomName", new(dns.RandomNameAction)).
// 消息
Prefix("/clusters/cluster/settings/message").
@@ -74,6 +76,9 @@ func init() {
GetPost("/createPopup", new(metrics.CreatePopupAction)).
Post("/delete", new(metrics.DeleteAction)).
// WebP
Prefix("/clusters/cluster/settings/webp").
GetPost("", new(webp.IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,98 @@
package webp
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/actions"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "")
this.SecondMenu("webp")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
}) {
webpResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeClusterWebPPolicy(this.AdminContext(), &pb.FindEnabledNodeClusterWebPPolicyRequest{NodeClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
if len(webpResp.WebpPolicyJSON) == 0 {
this.Data["webpPolicy"] = nodeconfigs.DefaultWebPImagePolicy
} else {
var config = &nodeconfigs.WebPImagePolicy{}
err = json.Unmarshal(webpResp.WebpPolicyJSON, config)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webpPolicy"] = config
}
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ClusterId int64
IsOn bool
RequireCache bool
MinLengthJSON []byte
MaxLengthJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo("修改集群 %d 的WebP设置", params.ClusterId)
var config = &nodeconfigs.WebPImagePolicy{
IsOn: params.IsOn,
RequireCache: params.RequireCache,
}
if len(params.MinLengthJSON) > 0 {
var minLength = &shared.SizeCapacity{}
err := json.Unmarshal(params.MinLengthJSON, minLength)
if err != nil {
this.ErrorPage(err)
return
}
config.MinLength = minLength
}
if len(params.MaxLengthJSON) > 0 {
var maxLength = &shared.SizeCapacity{}
err := json.Unmarshal(params.MaxLengthJSON, maxLength)
if err != nil {
this.ErrorPage(err)
return
}
config.MaxLength = maxLength
}
configJSON, err := json.Marshal(config)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().NodeClusterRPC().UpdateNodeClusterWebPPolicy(this.AdminContext(), &pb.UpdateNodeClusterWebPPolicyRequest{
NodeClusterId: params.ClusterId,
WebpPolicyJSON: configJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -8,6 +8,8 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"net"
"regexp"
)
type UpdateNodeSSHAction struct {
@@ -31,7 +33,7 @@ func (this *UpdateNodeSSHAction) RunGet(params struct {
return
}
node := nodeResp.Node
var node = nodeResp.Node
this.Data["node"] = maps.Map{
"id": node.Id,
"name": node.Name,
@@ -43,7 +45,7 @@ func (this *UpdateNodeSSHAction) RunGet(params struct {
}
// SSH
loginParams := maps.Map{
var loginParams = maps.Map{
"host": "",
"port": "",
"grantId": 0,
@@ -59,10 +61,22 @@ func (this *UpdateNodeSSHAction) RunGet(params struct {
}
}
}
if len(loginParams.GetString("host")) == 0 {
addressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledNodeIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledNodeIPAddressesWithNodeIdRequest{NodeId: node.Id})
if err != nil {
this.ErrorPage(err)
return
}
if len(addressesResp.NodeIPAddresses) > 0 {
loginParams["host"] = addressesResp.NodeIPAddresses[0].Ip
}
}
this.Data["params"] = loginParams
// 认证信息
grantId := loginParams.GetInt64("grantId")
var grantId = loginParams.GetInt64("grantId")
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: grantId})
if err != nil {
this.ErrorPage(err)
@@ -101,7 +115,12 @@ func (this *UpdateNodeSSHAction) RunPost(params struct {
this.Fail("需要选择或填写至少一个认证信息")
}
login := &pb.NodeLogin{
// 检查IP地址
if regexp.MustCompile(`^\d+\.\d+\.\d+\.\d+$`).MatchString(params.SshHost) && net.ParseIP(params.SshHost) == nil {
this.Fail("SSH主机地址 '" + params.SshHost + "' IP格式错误")
}
var login = &pb.NodeLogin{
Id: params.LoginId,
Name: "SSH",
Type: "ssh",

View File

@@ -13,7 +13,7 @@ import (
"strconv"
)
// 单个集群的帮助
// ClusterHelper 单个集群的帮助
type ClusterHelper struct {
}
@@ -65,18 +65,23 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
tabbar.Add("集群节点", "", "/clusters/cluster/nodes?clusterId="+clusterIdString, "server", selectedTabbar == "node")
tabbar.Add("集群设置", "", "/clusters/cluster/settings?clusterId="+clusterIdString, "setting", selectedTabbar == "setting")
tabbar.Add("删除集群", "", "/clusters/cluster/delete?clusterId="+clusterIdString, "trash", selectedTabbar == "delete")
{
m := tabbar.Add("当前集群:"+cluster.Name, "", "/clusters/cluster?clusterId="+clusterIdString, "", false)
m["right"] = true
}
actionutils.SetTabbar(action, tabbar)
// 左侧菜单
secondMenuItem := action.Data.GetString("secondMenuItem")
switch selectedTabbar {
case "setting":
action.Data["leftMenuItems"] = this.createSettingMenu(cluster, clusterInfo, secondMenuItem)
var menuItems = this.createSettingMenu(cluster, clusterInfo, secondMenuItem)
action.Data["leftMenuItems"] = menuItems
// 当前菜单
action.Data["leftMenuActiveItem"] = nil
for _, item := range menuItems {
if item.GetBool("isActive") {
action.Data["leftMenuActiveItem"] = item
break
}
}
}
}
@@ -90,6 +95,7 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, info *pb.F
"name": "基础设置",
"url": "/clusters/cluster/settings?clusterId=" + clusterId,
"isActive": selectedItem == "basic",
"isOn": true,
})
items = append(items, maps.Map{
"name": "缓存设置",
@@ -124,6 +130,12 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, info *pb.F
"isActive": selectedItem == "dns",
"isOn": cluster.DnsDomainId > 0 || len(cluster.DnsName) > 0,
})
items = append(items, maps.Map{
"name": "WebP",
"url": "/clusters/cluster/settings/webp?clusterId=" + clusterId,
"isActive": selectedItem == "webp",
"isOn": info != nil && info.WebpIsOn,
})
items = append(items, maps.Map{
"name": "统计指标",
"url": "/clusters/cluster/settings/metrics?clusterId=" + clusterId,
@@ -157,6 +169,7 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, info *pb.F
"name": "系统服务",
"url": "/clusters/cluster/settings/services?clusterId=" + clusterId,
"isActive": selectedItem == "service",
"isOn": info != nil && info.HasSystemServices,
})
{
items = append(items, maps.Map{

View File

@@ -12,7 +12,8 @@ func NewClustersHelper() *ClustersHelper {
return &ClustersHelper{}
}
func (this *ClustersHelper) BeforeAction(action *actions.ActionObject) {
func (this *ClustersHelper) BeforeAction(actionPtr actions.ActionWrapper) {
var action = actionPtr.Object()
if action.Request.Method != http.MethodGet {
return
}

View File

@@ -27,6 +27,22 @@ func (this *CreateAction) RunGet(params struct{}) {
}
this.Data["hasDomains"] = hasDomainsResp.Exist
// 菜单:集群总数
totalResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClusters(this.AdminContext(), &pb.CountAllEnabledNodeClustersRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["totalNodeClusters"] = totalResp.Count
// 菜单:节点总数
totalNodesResp, err := this.RPC().NodeRPC().CountAllEnabledNodes(this.AdminContext(), &pb.CountAllEnabledNodesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["totalNodes"] = totalNodesResp.Count
this.Show()
}

View File

@@ -28,7 +28,9 @@ func (this *CreateAction) RunPost(params struct {
Username string
Password string
PrivateKey string
Passphrase string
Description string
Su bool
Must *actions.Must
}) {
@@ -58,7 +60,9 @@ func (this *CreateAction) RunPost(params struct {
Username: params.Username,
Password: params.Password,
PrivateKey: params.PrivateKey,
Passphrase: params.Passphrase,
Description: params.Description,
Su: params.Su,
NodeId: 0,
})
if err != nil {

View File

@@ -29,7 +29,9 @@ func (this *CreatePopupAction) RunPost(params struct {
Username string
Password string
PrivateKey string
Passphrase string
Description string
Su bool
Must *actions.Must
}) {
@@ -59,7 +61,9 @@ func (this *CreatePopupAction) RunPost(params struct {
Username: params.Username,
Password: params.Password,
PrivateKey: params.PrivateKey,
Passphrase: params.Passphrase,
Description: params.Description,
Su: params.Su,
NodeId: 0,
})
if err != nil {
@@ -72,6 +76,7 @@ func (this *CreatePopupAction) RunPost(params struct {
"name": params.Name,
"method": params.Method,
"methodName": grantutils.FindGrantMethodName(params.Method),
"username": params.Username,
}
// 创建日志

View File

@@ -40,6 +40,7 @@ func (this *GrantAction) RunGet(params struct {
"username": grant.Username,
"password": strings.Repeat("*", len(grant.Password)),
"privateKey": grant.PrivateKey,
"passphrase": strings.Repeat("*", len(grant.Passphrase)),
"description": grant.Description,
"su": grant.Su,
}

View File

@@ -12,6 +12,7 @@ func init() {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
Helper(clusterutils.NewClustersHelper()).
Data("teaMenu", "clusters").
Data("teaSubMenu", "grant").
Prefix("/clusters/grants").

View File

@@ -43,6 +43,7 @@ func (this *UpdateAction) RunGet(params struct {
"username": grant.Username,
"password": grant.Password,
"privateKey": grant.PrivateKey,
"passphrase": grant.Passphrase,
"description": grant.Description,
"su": grant.Su,
}
@@ -57,7 +58,9 @@ func (this *UpdateAction) RunPost(params struct {
Username string
Password string
PrivateKey string
Passphrase string
Description string
Su bool
Must *actions.Must
}) {
@@ -93,7 +96,9 @@ func (this *UpdateAction) RunPost(params struct {
Username: params.Username,
Password: params.Password,
PrivateKey: params.PrivateKey,
Passphrase: params.Passphrase,
Description: params.Description,
Su: params.Su,
NodeId: 0,
})
if err != nil {

View File

@@ -43,6 +43,8 @@ func (this *UpdatePopupAction) RunGet(params struct {
"password": grant.Password,
"description": grant.Description,
"privateKey": grant.PrivateKey,
"passphrase": grant.Passphrase,
"su": grant.Su,
}
this.Show()
@@ -56,7 +58,9 @@ func (this *UpdatePopupAction) RunPost(params struct {
Username string
Password string
PrivateKey string
Passphrase string
Description string
Su bool
Must *actions.Must
}) {
@@ -91,8 +95,10 @@ func (this *UpdatePopupAction) RunPost(params struct {
Username: params.Username,
Password: params.Password,
PrivateKey: params.PrivateKey,
Passphrase: params.Passphrase,
Description: params.Description,
NodeId: params.NodeId,
Su: params.Su,
})
if err != nil {
this.ErrorPage(err)
@@ -105,6 +111,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
"name": params.Name,
"method": params.Method,
"methodName": grantutils.FindGrantMethodName(params.Method),
"username": params.Username,
}
this.Success()

View File

@@ -1,15 +1,12 @@
package clusters
import (
"encoding/json"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"time"
)
type IndexAction struct {
@@ -24,11 +21,27 @@ func (this *IndexAction) RunGet(params struct {
Keyword string
SearchType string
}) {
isSearching := len(params.Keyword) > 0
var isSearching = len(params.Keyword) > 0
this.Data["keyword"] = params.Keyword
this.Data["searchType"] = params.SearchType
this.Data["isSearching"] = isSearching
// 集群总数
totalClustersResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClusters(this.AdminContext(), &pb.CountAllEnabledNodeClustersRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["totalNodeClusters"] = totalClustersResp.Count
// 节点总数
totalNodesResp, err := this.RPC().NodeRPC().CountAllEnabledNodes(this.AdminContext(), &pb.CountAllEnabledNodesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["totalNodes"] = totalNodesResp.Count
// 常用的集群
latestClusterMaps := []maps.Map{}
if !isSearching {
@@ -46,12 +59,6 @@ func (this *IndexAction) RunGet(params struct {
}
this.Data["latestClusters"] = latestClusterMaps
// 搜索节点
if params.SearchType == "node" && len(params.Keyword) > 0 {
this.searchNodes(params.Keyword)
return
}
// 搜索集群
countResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClusters(this.AdminContext(), &pb.CountAllEnabledNodeClustersRequest{
Keyword: params.Keyword,
@@ -121,6 +128,10 @@ func (this *IndexAction) RunGet(params struct {
this.ErrorPage(err)
}
if cluster.TimeZone == nodeconfigs.DefaultTimeZoneLocation {
cluster.TimeZone = ""
}
clusterMaps = append(clusterMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
@@ -132,6 +143,8 @@ func (this *IndexAction) RunGet(params struct {
"dnsName": cluster.DnsName,
"dnsDomainName": dnsDomainName,
"countServers": countServersResp.Count,
"timeZone": cluster.TimeZone,
"isPinned": cluster.IsPinned,
})
}
}
@@ -152,149 +165,3 @@ func (this *IndexAction) RunGet(params struct {
this.Show()
}
func (this *IndexAction) searchNodes(keyword string) {
// 搜索节点
countResp, err := this.RPC().NodeRPC().CountAllEnabledNodesMatch(this.AdminContext(), &pb.CountAllEnabledNodesMatchRequest{
Keyword: keyword,
})
if err != nil {
this.ErrorPage(err)
return
}
count := countResp.Count
page := this.NewPage(count)
this.Data["page"] = page.AsHTML()
this.Data["countNodes"] = count
nodesResp, err := this.RPC().NodeRPC().ListEnabledNodesMatch(this.AdminContext(), &pb.ListEnabledNodesMatchRequest{
Offset: page.Offset,
Size: page.Size,
Keyword: keyword,
})
if err != nil {
this.ErrorPage(err)
return
}
nodeMaps := []maps.Map{}
for _, node := range nodesResp.Nodes {
// 状态
isSynced := false
status := &nodeconfigs.NodeStatus{}
if len(node.StatusJSON) > 0 {
err = json.Unmarshal(node.StatusJSON, &status)
if err != nil {
this.ErrorPage(err)
return
}
status.IsActive = status.IsActive && time.Now().Unix()-status.UpdatedAt <= 60 // N秒之内认为活跃
isSynced = status.ConfigVersion == node.Version
}
// IP
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledNodeIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledNodeIPAddressesWithNodeIdRequest{
NodeId: node.Id,
Role: nodeconfigs.NodeRoleNode,
})
if err != nil {
this.ErrorPage(err)
return
}
ipAddresses := []maps.Map{}
for _, addr := range ipAddressesResp.NodeIPAddresses {
ipAddresses = append(ipAddresses, maps.Map{
"id": addr.Id,
"name": addr.Name,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
"isOn": addr.IsOn,
"isUp": addr.IsUp,
})
}
// 分组
var groupMap maps.Map = nil
if node.NodeGroup != nil {
groupMap = maps.Map{
"id": node.NodeGroup.Id,
"name": node.NodeGroup.Name,
}
}
// 区域
var regionMap maps.Map = nil
if node.NodeRegion != nil {
regionMap = maps.Map{
"id": node.NodeRegion.Id,
"name": node.NodeRegion.Name,
}
}
// DNS
dnsRouteNames := []string{}
for _, route := range node.DnsRoutes {
dnsRouteNames = append(dnsRouteNames, route.Name)
}
// 从集群
var secondaryClusterMaps []maps.Map
for _, secondaryCluster := range node.SecondaryNodeClusters {
secondaryClusterMaps = append(secondaryClusterMaps, maps.Map{
"id": secondaryCluster.Id,
"name": secondaryCluster.Name,
"isOn": secondaryCluster.IsOn,
})
}
nodeMaps = append(nodeMaps, maps.Map{
"id": node.Id,
"name": node.Name,
"isInstalled": node.IsInstalled,
"isOn": node.IsOn,
"isUp": node.IsUp,
"installStatus": maps.Map{
"isRunning": node.InstallStatus.IsRunning,
"isFinished": node.InstallStatus.IsFinished,
"isOk": node.InstallStatus.IsOk,
"error": node.InstallStatus.Error,
},
"status": maps.Map{
"isActive": status.IsActive,
"updatedAt": status.UpdatedAt,
"hostname": status.Hostname,
"cpuUsage": status.CPUUsage,
"cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
"memUsage": status.MemoryUsage,
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
},
"cluster": maps.Map{
"id": node.NodeCluster.Id,
"name": node.NodeCluster.Name,
},
"secondaryClusters": secondaryClusterMaps,
"isSynced": isSynced,
"ipAddresses": ipAddresses,
"group": groupMap,
"region": regionMap,
"dnsRouteNames": dnsRouteNames,
})
}
this.Data["nodes"] = nodeMaps
this.Data["clusters"] = []maps.Map{}
// 搜索集群
{
countResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClusters(this.AdminContext(), &pb.CountAllEnabledNodeClustersRequest{
Keyword: keyword,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["countClusters"] = countResp.Count
}
this.Show()
}

View File

@@ -12,16 +12,19 @@ func init() {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
Helper(clusterutils.NewClustersHelper()).
Data("teaMenu", "clusters").
Prefix("/clusters").
Get("", new(IndexAction)).
GetPost("/create", new(CreateAction)).
Post("/pin", new(PinAction)).
Get("/nodes", new(NodesAction)).
// 只要登录即可访问的Action
EndHelpers().
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeCommon)).
Post("/options", new(OptionsAction)).
Post("/nodeOptions", new(NodeOptionsAction)).
GetPost("/selectPopup", new(SelectPopupAction)).
EndAll()
})
}

View File

@@ -0,0 +1,33 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package logs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/types"
"strings"
)
type FixAction struct {
actionutils.ParentAction
}
func (this *FixAction) RunPost(params struct {
LogIds []int64
}) {
var logIdStrings = []string{}
for _, logId := range params.LogIds {
logIdStrings = append(logIdStrings, types.String(logId))
}
defer this.CreateLogInfo("设置日志 %s 为已修复", strings.Join(logIdStrings, ", "))
_, err := this.RPC().NodeLogRPC().FixNodeLogs(this.AdminContext(), &pb.FixNodeLogsRequest{NodeLogIds: params.LogIds})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,25 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package logs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type FixAllAction struct {
actionutils.ParentAction
}
func (this *FixAllAction) RunPost(params struct {
}) {
defer this.CreateLogInfo("设置所有日志为已修复")
_, err := this.RPC().NodeLogRPC().FixAllNodeLogs(this.AdminContext(), &pb.FixAllNodeLogsRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -1,8 +1,12 @@
package logs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils/nodelogutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
)
@@ -12,69 +16,151 @@ type IndexAction struct {
}
func (this *IndexAction) Init() {
var paramType = this.ParamString("type")
if paramType == "unread" {
this.FirstMenu("unread")
} else if paramType == "needFix" {
this.FirstMenu("needFix")
} else {
this.FirstMenu("index")
}
}
func (this *IndexAction) RunGet(params struct {
DayFrom string
DayTo string
Keyword string
Level string
DayFrom string
DayTo string
Keyword string
Level string
Type string // unread, needFix
Tag string
ClusterId int64
NodeId int64
}) {
this.Data["dayFrom"] = params.DayFrom
this.Data["dayTo"] = params.DayTo
this.Data["keyword"] = params.Keyword
this.Data["level"] = params.Level
this.Data["type"] = params.Type
this.Data["tag"] = params.Tag
this.Data["clusterId"] = params.ClusterId
this.Data["nodeId"] = params.NodeId
countResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
NodeId: 0,
Role: "node",
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
var fixedState configutils.BoolState = 0
var allServers = false
if params.Type == "needFix" {
fixedState = configutils.BoolStateNo
allServers = true
}
// 常见标签
this.Data["tags"] = nodelogutils.FindNodeCommonTags()
// 未读数量
countUnreadResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
Role: nodeconfigs.NodeRoleNode,
IsUnread: true,
})
if err != nil {
this.ErrorPage(err)
return
}
count := countResp.Count
page := this.NewPage(count)
this.Data["countUnreadLogs"] = countUnreadResp.Count
// 需要修复数量
countNeedFixResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
Role: nodeconfigs.NodeRoleNode,
AllServers: true,
FixedState: int32(configutils.BoolStateNo),
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["countNeedFixLogs"] = countNeedFixResp.Count
// 日志数量
countResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
NodeClusterId: params.ClusterId,
NodeId: params.NodeId,
Role: nodeconfigs.NodeRoleNode,
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
IsUnread: params.Type == "unread",
Tag: params.Tag,
FixedState: int32(fixedState),
AllServers: allServers,
})
if err != nil {
this.ErrorPage(err)
return
}
var count = countResp.Count
var page = this.NewPage(count)
this.Data["page"] = page.AsHTML()
logsResp, err := this.RPC().NodeLogRPC().ListNodeLogs(this.AdminContext(), &pb.ListNodeLogsRequest{
NodeId: 0,
Role: "node",
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
Offset: page.Offset,
Size: page.Size,
NodeClusterId: params.ClusterId,
NodeId: params.NodeId,
Role: nodeconfigs.NodeRoleNode,
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
IsUnread: params.Type == "unread",
Tag: params.Tag,
FixedState: int32(fixedState),
AllServers: allServers,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
logs := []maps.Map{}
var logs = []maps.Map{}
for _, log := range logsResp.NodeLogs {
// 节点信息
nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: log.NodeId})
if err != nil {
continue
}
node := nodeResp.Node
var node = nodeResp.Node
if node == nil || node.NodeCluster == nil {
continue
}
// 服务信息
var serverMap = maps.Map{"id": 0}
if log.ServerId > 0 {
serverResp, err := this.RPC().ServerRPC().FindEnabledUserServerBasic(this.AdminContext(), &pb.FindEnabledUserServerBasicRequest{ServerId: log.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
var server = serverResp.Server
if server != nil {
serverMap = maps.Map{"id": server.Id, "name": server.Name}
}
}
var isFixed = true
if !log.IsFixed && log.ServerId > 0 && lists.ContainsString([]string{"success", "warning", "error"}, log.Level) {
isFixed = false
}
logs = append(logs, maps.Map{
"id": log.Id,
"tag": log.Tag,
"description": log.Description,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", log.CreatedAt),
"level": log.Level,
"isToday": timeutil.FormatTime("Y-m-d", log.CreatedAt) == timeutil.Format("Y-m-d"),
"count": log.Count,
"isRead": log.IsRead,
"isFixed": isFixed,
"node": maps.Map{
"id": node.Id,
"cluster": maps.Map{
@@ -83,6 +169,7 @@ func (this *IndexAction) RunGet(params struct {
},
"name": node.Name,
},
"server": serverMap,
})
}
this.Data["logs"] = logs

View File

@@ -14,6 +14,10 @@ func init() {
Data("teaSubMenu", "log").
Prefix("/clusters/logs").
Get("", new(IndexAction)).
Post("/readLogs", new(ReadLogsAction)).
Post("/readAllLogs", new(ReadAllLogsAction)).
Post("/fix", new(FixAction)).
Post("/fixAll", new(FixAllAction)).
EndAll()
})
}

View File

@@ -1,20 +1,20 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package servers
package logs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type FixLogAction struct {
type ReadAllLogsAction struct {
actionutils.ParentAction
}
func (this *FixLogAction) RunPost(params struct {
LogId int64
func (this *ReadAllLogsAction) RunPost(params struct {
LogIds []int64
}) {
_, err := this.RPC().NodeLogRPC().FixNodeLog(this.AdminContext(), &pb.FixNodeLogRequest{NodeLogId: params.LogId})
_, err := this.RPC().NodeLogRPC().UpdateAllNodeLogsRead(this.AdminContext(), &pb.UpdateAllNodeLogsReadRequest{})
if err != nil {
this.ErrorPage(err)
return

View File

@@ -0,0 +1,26 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package logs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type ReadLogsAction struct {
actionutils.ParentAction
}
func (this *ReadLogsAction) RunPost(params struct {
LogIds []int64
}) {
_, err := this.RPC().NodeLogRPC().UpdateNodeLogsRead(this.AdminContext(), &pb.UpdateNodeLogsReadRequest{
NodeLogIds: params.LogIds,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,34 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package clusters
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type NodeOptionsAction struct {
actionutils.ParentAction
}
func (this *NodeOptionsAction) RunPost(params struct {
ClusterId int64
}) {
resp, err := this.RPC().NodeRPC().FindAllEnabledNodesWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledNodesWithNodeClusterIdRequest{NodeClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
var nodeMaps = []maps.Map{}
for _, node := range resp.Nodes {
nodeMaps = append(nodeMaps, maps.Map{
"id": node.Id,
"name": node.Name,
})
}
this.Data["nodes"] = nodeMaps
this.Success()
}

View File

@@ -0,0 +1,293 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package clusters
import (
"encoding/json"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"strconv"
"time"
)
type NodesAction struct {
actionutils.ParentAction
}
func (this *NodesAction) Init() {
this.Nav("", "", "node")
}
func (this *NodesAction) RunGet(params struct {
ClusterId int64
GroupId int64
RegionId int64
InstalledState int
ActiveState int
Keyword string
Level int32
CpuOrder string
MemoryOrder string
TrafficInOrder string
TrafficOutOrder string
LoadOrder string
}) {
this.Data["groupId"] = params.GroupId
this.Data["regionId"] = params.RegionId
this.Data["installState"] = params.InstalledState
this.Data["activeState"] = params.ActiveState
this.Data["keyword"] = params.Keyword
this.Data["level"] = params.Level
this.Data["clusterId"] = params.ClusterId
this.Data["hasOrder"] = len(params.CpuOrder) > 0 || len(params.MemoryOrder) > 0 || len(params.TrafficInOrder) > 0 || len(params.TrafficOutOrder) > 0 || len(params.LoadOrder) > 0
// 集群是否已经设置了线路
clusterDNSResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeClusterDNS(this.AdminContext(), &pb.FindEnabledNodeClusterDNSRequest{NodeClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["hasClusterDNS"] = clusterDNSResp.Domain != nil
// 数量
countAllResp, err := this.RPC().NodeRPC().CountAllEnabledNodesMatch(this.AdminContext(), &pb.CountAllEnabledNodesMatchRequest{
NodeClusterId: params.ClusterId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["countAll"] = countAllResp.Count
countResp, err := this.RPC().NodeRPC().CountAllEnabledNodesMatch(this.AdminContext(), &pb.CountAllEnabledNodesMatchRequest{
NodeClusterId: params.ClusterId,
NodeGroupId: params.GroupId,
NodeRegionId: params.RegionId,
Level: params.Level,
InstallState: types.Int32(params.InstalledState),
ActiveState: types.Int32(params.ActiveState),
Keyword: params.Keyword,
})
if err != nil {
this.ErrorPage(err)
return
}
var page = this.NewPage(countResp.Count)
this.Data["page"] = page.AsHTML()
var req = &pb.ListEnabledNodesMatchRequest{
Offset: page.Offset,
Size: page.Size,
NodeClusterId: params.ClusterId,
NodeGroupId: params.GroupId,
NodeRegionId: params.RegionId,
Level: params.Level,
InstallState: types.Int32(params.InstalledState),
ActiveState: types.Int32(params.ActiveState),
Keyword: params.Keyword,
}
if params.CpuOrder == "asc" {
req.CpuAsc = true
} else if params.CpuOrder == "desc" {
req.CpuDesc = true
} else if params.MemoryOrder == "asc" {
req.MemoryAsc = true
} else if params.MemoryOrder == "desc" {
req.MemoryDesc = true
} else if params.TrafficInOrder == "asc" {
req.TrafficInAsc = true
} else if params.TrafficInOrder == "desc" {
req.TrafficInDesc = true
} else if params.TrafficOutOrder == "asc" {
req.TrafficOutAsc = true
} else if params.TrafficOutOrder == "desc" {
req.TrafficOutDesc = true
} else if params.LoadOrder == "asc" {
req.LoadAsc = true
} else if params.LoadOrder == "desc" {
req.LoadDesc = true
}
nodesResp, err := this.RPC().NodeRPC().ListEnabledNodesMatch(this.AdminContext(), req)
if err != nil {
this.ErrorPage(err)
return
}
var nodeMaps = []maps.Map{}
for _, node := range nodesResp.Nodes {
// 状态
isSynced := false
status := &nodeconfigs.NodeStatus{}
if len(node.StatusJSON) > 0 {
err = json.Unmarshal(node.StatusJSON, &status)
if err != nil {
logs.Error(err)
continue
}
status.IsActive = status.IsActive && time.Now().Unix()-status.UpdatedAt <= 60 // N秒之内认为活跃
isSynced = status.ConfigVersion == node.Version
}
// IP
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledNodeIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledNodeIPAddressesWithNodeIdRequest{
NodeId: node.Id,
Role: nodeconfigs.NodeRoleNode,
})
if err != nil {
this.ErrorPage(err)
return
}
ipAddresses := []maps.Map{}
for _, addr := range ipAddressesResp.NodeIPAddresses {
ipAddresses = append(ipAddresses, maps.Map{
"id": addr.Id,
"name": addr.Name,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
"isUp": addr.IsUp,
"isOn": addr.IsOn,
})
}
// 分组
var groupMap maps.Map = nil
if node.NodeGroup != nil {
groupMap = maps.Map{
"id": node.NodeGroup.Id,
"name": node.NodeGroup.Name,
}
}
// 区域
var regionMap maps.Map = nil
if node.NodeRegion != nil {
regionMap = maps.Map{
"id": node.NodeRegion.Id,
"name": node.NodeRegion.Name,
}
}
// DNS
dnsRouteNames := []string{}
for _, route := range node.DnsRoutes {
dnsRouteNames = append(dnsRouteNames, route.Name)
}
// 从集群
var secondaryClusterMaps []maps.Map
for _, secondaryCluster := range node.SecondaryNodeClusters {
secondaryClusterMaps = append(secondaryClusterMaps, maps.Map{
"id": secondaryCluster.Id,
"name": secondaryCluster.Name,
"isOn": secondaryCluster.IsOn,
})
}
nodeMaps = append(nodeMaps, maps.Map{
"id": node.Id,
"name": node.Name,
"isInstalled": node.IsInstalled,
"isOn": node.IsOn,
"isUp": node.IsUp,
"installStatus": maps.Map{
"isRunning": node.InstallStatus.IsRunning,
"isFinished": node.InstallStatus.IsFinished,
"isOk": node.InstallStatus.IsOk,
"error": node.InstallStatus.Error,
},
"status": maps.Map{
"isActive": status.IsActive,
"updatedAt": status.UpdatedAt,
"hostname": status.Hostname,
"cpuUsage": status.CPUUsage,
"cpuUsageText": numberutils.FormatFloat2(status.CPUUsage * 100),
"memUsage": status.MemoryUsage,
"memUsageText": numberutils.FormatFloat2(status.MemoryUsage * 100),
"trafficInBytes": status.TrafficInBytes,
"trafficOutBytes": status.TrafficOutBytes,
"load1m": numberutils.FormatFloat2(status.Load1m),
},
"cluster": maps.Map{
"id": node.NodeCluster.Id,
"name": node.NodeCluster.Name,
},
"secondaryClusters": secondaryClusterMaps,
"isSynced": isSynced,
"ipAddresses": ipAddresses,
"group": groupMap,
"region": regionMap,
"dnsRouteNames": dnsRouteNames,
"level": node.Level,
})
}
this.Data["nodes"] = nodeMaps
// 所有分组
var groupMaps = []maps.Map{}
groupsResp, err := this.RPC().NodeGroupRPC().FindAllEnabledNodeGroupsWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledNodeGroupsWithNodeClusterIdRequest{
NodeClusterId: params.ClusterId,
})
if err != nil {
this.ErrorPage(err)
return
}
for _, group := range groupsResp.NodeGroups {
countNodesInGroupResp, err := this.RPC().NodeRPC().CountAllEnabledNodesWithNodeGroupId(this.AdminContext(), &pb.CountAllEnabledNodesWithNodeGroupIdRequest{NodeGroupId: group.Id})
if err != nil {
this.ErrorPage(err)
return
}
countNodes := countNodesInGroupResp.Count
groupName := group.Name
if countNodes > 0 {
groupName += "(" + strconv.FormatInt(countNodes, 10) + ")"
}
groupMaps = append(groupMaps, maps.Map{
"id": group.Id,
"name": groupName,
"countNodes": countNodes,
})
}
this.Data["groups"] = groupMaps
// 所有区域
regionsResp, err := this.RPC().NodeRegionRPC().FindAllEnabledAndOnNodeRegions(this.AdminContext(), &pb.FindAllEnabledAndOnNodeRegionsRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var regionMaps = []maps.Map{}
for _, region := range regionsResp.NodeRegions {
regionMaps = append(regionMaps, maps.Map{
"id": region.Id,
"name": region.Name,
})
}
this.Data["regions"] = regionMaps
// 级别
this.Data["levels"] = []maps.Map{}
if teaconst.IsPlus {
this.Data["levels"] = nodeconfigs.FindAllNodeLevels()
}
// 集群总数
totalClustersResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClusters(this.AdminContext(), &pb.CountAllEnabledNodeClustersRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["totalNodeClusters"] = totalClustersResp.Count
// 节点总数
this.Data["totalNodes"] = countResp.Count
this.Show()
}

View File

@@ -0,0 +1,34 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package clusters
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type PinAction struct {
actionutils.ParentAction
}
func (this *PinAction) RunPost(params struct {
ClusterId int64
IsPinned bool
}) {
if params.IsPinned {
defer this.CreateLogInfo("置顶集群 %d", params.ClusterId)
} else {
defer this.CreateLogInfo("取消置顶集群 %d", params.ClusterId)
}
_, err := this.RPC().NodeClusterRPC().UpdateNodeClusterPinned(this.AdminContext(), &pb.UpdateNodeClusterPinnedRequest{
NodeClusterId: params.ClusterId,
IsPinned: params.IsPinned,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -21,6 +21,7 @@ func (this *SelectPopupAction) Init() {
func (this *SelectPopupAction) RunGet(params struct {
SelectedClusterIds string
Keyword string
PageSize int64
}) {
this.Data["keyword"] = params.Keyword
@@ -34,8 +35,11 @@ func (this *SelectPopupAction) RunGet(params struct {
return
}
var count = countResp.Count
var page = this.NewPage(count)
page.Size = 6
var pageSize int64 = 6
if params.PageSize > 0 {
pageSize = params.PageSize
}
var page = this.NewPage(count, pageSize)
this.Data["page"] = page.AsHTML()
clustersResp, err := this.RPC().NodeClusterRPC().ListEnabledNodeClusters(this.AdminContext(), &pb.ListEnabledNodeClustersRequest{

View File

@@ -10,7 +10,9 @@ type CheckAction struct {
}
func (this *CheckAction) RunPost(params struct{}) {
resp, err := this.RPC().NodeTaskRPC().ExistsNodeTasks(this.AdminContext(), &pb.ExistsNodeTasksRequest{})
resp, err := this.RPC().NodeTaskRPC().ExistsNodeTasks(this.AdminContext(), &pb.ExistsNodeTasksRequest{
ExcludeTypes: []string{"ipItemChanged"},
})
if err != nil {
this.ErrorPage(err)
return

View File

@@ -40,6 +40,11 @@ func (this *IndexAction) RunGet(params struct{}) {
}
}
// 版本更新
this.Data["currentVersionCode"] = teaconst.Version
this.Data["newVersionCode"] = teaconst.NewVersionCode
this.Data["newVersionDownloadURL"] = teaconst.NewVersionDownloadURL
this.Show()
}
@@ -55,13 +60,16 @@ func (this *IndexAction) RunPost(params struct{}) {
this.Data["dashboard"] = maps.Map{
"defaultClusterId": resp.DefaultNodeClusterId,
"countServers": resp.CountServers,
"countNodeClusters": resp.CountNodeClusters,
"countNodes": resp.CountNodes,
"countUsers": resp.CountUsers,
"countAPINodes": resp.CountAPINodes,
"countDBNodes": resp.CountDBNodes,
"countUserNodes": resp.CountUserNodes,
"countServers": resp.CountServers,
"countNodeClusters": resp.CountNodeClusters,
"countNodes": resp.CountNodes,
"countOfflineNodes": resp.CountOfflineNodes,
"countUsers": resp.CountUsers,
"countAPINodes": resp.CountAPINodes,
"countOfflineAPINodes": resp.CountOfflineAPINodes,
"countDBNodes": resp.CountDBNodes,
"countUserNodes": resp.CountUserNodes,
"countOfflineUserNodes": resp.CountOfflineUserNodes,
"canGoServers": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeServer),
"canGoNodes": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeNode),

View File

@@ -6,7 +6,7 @@ import (
"github.com/iwind/TeaGo/maps"
)
// 域名列表选项
// DomainOptionsAction 域名列表选项
type DomainOptionsAction struct {
actionutils.ParentAction
}
@@ -23,8 +23,8 @@ func (this *DomainOptionsAction) RunPost(params struct {
}
domainMaps := []maps.Map{}
for _, domain := range domainsResp.DnsDomains {
// 未开启的先跳过
if !domain.IsOn {
// 未开启或者已删除的先跳过
if !domain.IsOn || domain.IsDeleted {
continue
}

View File

@@ -17,7 +17,7 @@ func ValidateDomainFormat(domain string) bool {
if piece == "-" ||
strings.HasPrefix(piece, "-") ||
strings.HasSuffix(piece, "-") ||
strings.Contains(piece, "--") ||
//strings.Contains(piece, "--") ||
len(piece) > 63 ||
!regexp.MustCompile(`^[a-z0-9-]+$`).MatchString(piece) {
return false
@@ -75,9 +75,9 @@ func ValidateRecordName(name string) bool {
if piece == "-" ||
strings.HasPrefix(piece, "-") ||
strings.HasSuffix(piece, "-") ||
strings.Contains(piece, "--") ||
//strings.Contains(piece, "--") ||
len(piece) > 63 ||
!regexp.MustCompile(`^[a-z0-9-]+$`).MatchString(piece) {
!regexp.MustCompile(`^[_a-z0-9-]+$`).MatchString(piece) {
return false
}
}

View File

@@ -0,0 +1,27 @@
package domains
import (
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type RecoverAction struct {
actionutils.ParentAction
}
func (this *RecoverAction) RunPost(params struct {
DomainId int64
}) {
// 记录日志
defer this.CreateLog(oplogs.LevelInfo, "从DNS服务商中恢复域名 %d", params.DomainId)
// 执行恢复
_, err := this.RPC().DNSDomainRPC().RecoverDNSDomain(this.AdminContext(), &pb.RecoverDNSDomainRequest{DnsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -52,7 +52,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
// TODO 检查DomainId
// 记录日志
defer this.CreateLog(oplogs.LevelInfo, "修改管理域名到DNS服务商 %d", params.DomainId)
defer this.CreateLog(oplogs.LevelInfo, "修改DNS服务商域名 %d", params.DomainId)
params.Must.
Field("name", params.Name).

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