Compare commits

...

317 Commits

Author SHA1 Message Date
刘祥超
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
刘祥超
e4e3591413 修改文字 2021-09-24 12:03:31 +08:00
刘祥超
3f847d7c7f 区域监控增加检测时间、结果页增加级别筛选 2021-09-23 10:02:32 +08:00
刘祥超
67a10994a4 提示已经有服务分组配置时加入分组链接 2021-09-22 19:46:22 +08:00
刘祥超
4016296e0b 可以在分组中设置一些全局配置选项 2021-09-22 19:39:38 +08:00
刘祥超
92777366ec 域名解析任务列表中增加集群解析页面链接/在域名解析--集群详情中显示正在执行的任务 2021-09-21 10:56:29 +08:00
刘祥超
beb34f8264 可以设置集群的DNS记录TTL 2021-09-20 20:01:15 +08:00
刘祥超
f6f8c5c858 在集群中可以设置自动加入DNS的CNAME记录 2021-09-20 16:37:41 +08:00
刘祥超
a19c4dffcd 反向代理源站实现使用域名分组 2021-09-20 11:54:28 +08:00
刘祥超
a5754971f6 开源版本也显示域名排行、缓存流量趋势、攻击流量趋势 2021-09-19 16:10:52 +08:00
刘祥超
dbe6435809 实现连通性变化发送通知功能 2021-09-18 14:22:01 +08:00
刘祥超
7e5b980600 优化消息弹窗交互 2021-09-18 11:02:23 +08:00
刘祥超
a6c3b70ee7 修改生成的YAML配置中可能含有tab的Bug 2021-09-16 15:06:37 +08:00
刘祥超
0a23e7951a 增加商业版激活功能 2021-09-16 10:32:58 +08:00
刘祥超
e0e3fc8fef 看板提醒商业版过期日期 2021-09-16 09:21:44 +08:00
刘祥超
a8b947f5ca 修复日期控件初始化格式可能错误的问题 2021-09-15 19:40:42 +08:00
刘祥超
0f28df51f1 优化界面 2021-09-15 19:22:54 +08:00
刘祥超
6e36528f35 IP地址详情中增加区域监控雷达图、数据列表 2021-09-15 17:54:03 +08:00
刘祥超
a885fdbea7 修复IP地址不能修改在线状态的Bug 2021-09-15 11:46:50 +08:00
刘祥超
713de74abb 调整部分命名 2021-09-14 19:39:41 +08:00
刘祥超
8db6a33e15 IP阈值动作增加WebHook 2021-09-14 15:27:54 +08:00
刘祥超
ed2d577be0 IP阈值增加节点分组和集群相关统计项目 2021-09-14 11:36:08 +08:00
刘祥超
084eddecbf 优化节点设置交互 2021-09-13 16:47:34 +08:00
刘祥超
5e09ba302a IP地址支持手动上线和从备用IP中恢复 2021-09-13 13:45:39 +08:00
刘祥超
fa37b40435 实现节点自动切换到备用IP 2021-09-13 10:51:16 +08:00
刘祥超
c95dde5187 实现基础的IP地址阈值 2021-09-12 20:21:32 +08:00
刘祥超
4d5a4d501a 修复当集群没有绑定DNS域名时无法修改节点DNS信息的Bug 2021-09-11 20:12:06 +08:00
刘祥超
ce97f20826 实现基本的监控终端管理 2021-09-08 19:34:51 +08:00
刘祥超
ab9cd13abc 版本修改为0.3.1 2021-09-08 19:34:47 +08:00
刘祥超
7af5e828bb 在选择线路弹窗可以从域名中获取线路 2021-09-06 09:16:19 +08:00
刘祥超
98d53c49a0 修复打开网站服务菜单时无法点击系统设置的Bug 2021-09-06 08:53:51 +08:00
刘祥超
bb32849e47 安装时延长API节点初始化等待时间 2021-09-06 08:53:26 +08:00
刘祥超
13221faee0 实现区域监控节点运行日志和运行结果 2021-09-06 08:53:08 +08:00
刘祥超
abda14f543 实现基本的区域监控终端管理功能 2021-09-05 11:10:07 +08:00
刘祥超
3f0e459181 删除不需要的文件 2021-09-04 10:27:45 +08:00
刘祥超
3df61381a4 删除部分不需要的文件/阶段性提交 2021-09-04 10:17:14 +08:00
刘祥超
daf8ae2192 修复IP地址保存后跳转错误的问题 2021-09-04 08:27:24 +08:00
刘祥超
3bf374ea5a 优化URL跳转交互 2021-09-01 08:49:59 +08:00
刘祥超
94b3559eee 增加独立的IP地址管理功能 2021-08-31 17:24:30 +08:00
刘祥超
1c93b42681 企业认证信息中增加节点数显示 2021-08-30 18:57:35 +08:00
刘祥超
1d9d44ecc3 数据看板和节点看板都改成异步加载 2021-08-30 15:23:31 +08:00
刘祥超
19178f963e 服务看板改成异步 2021-08-30 11:26:23 +08:00
刘祥超
4dc54f95b6 优化数据库节点管理 2021-08-30 10:56:03 +08:00
刘祥超
0790403923 优化交互 2021-08-30 09:52:09 +08:00
刘祥超
c55008d302 看板在健康检查失败离线时提供手动上线按钮 2021-08-29 16:56:37 +08:00
刘祥超
df9df94bb8 节点选择集群时可以根据关键词搜索 2021-08-29 16:41:59 +08:00
刘祥超
502ce4e414 健康检查连续下线次数默认值从1次改为3次 2021-08-29 15:26:56 +08:00
刘祥超
f5f07c0e96 可以在节点列表中直接修改节点所属线路 2021-08-29 14:00:59 +08:00
刘祥超
9549424835 缓存策略里的默认缓存条件增加、修改或者删除后自动保存 2021-08-29 09:40:21 +08:00
刘祥超
062b9baeab 修复缓存条件状态码无法修改的问题 2021-08-29 09:22:02 +08:00
刘祥超
08d1fcaf03 服务 -- 缓存设置中增加清理和预热功能 2021-08-29 09:21:44 +08:00
刘祥超
ad7523a500 修改accessKeys package因为大小写而无法编译的问题 2021-08-26 14:02:53 +08:00
刘祥超
28aa0f3b76 删除不需要的文件 2021-08-26 14:01:48 +08:00
刘祥超
8247843936 添加IP到IP名单时,可以选择批量输入 2021-08-26 10:28:52 +08:00
刘祥超
09ff5a37a9 DNS服务商支持搜索 2021-08-25 18:46:57 +08:00
刘祥超
663ec7710c 集群支持使用域名搜索 2021-08-25 18:39:35 +08:00
刘祥超
0dad0b10e6 选择DNS线路时增加搜索/节点如果没有设置DNS线路就使用默认线路 2021-08-25 17:16:14 +08:00
刘祥超
8c352813c4 优化HTTP Header管理界面交互 2021-08-25 14:09:37 +08:00
刘祥超
745f5d9759 优化集群选择DNS设置交互 2021-08-25 11:55:06 +08:00
刘祥超
6536ace4f7 默认集群没有节点时,在看板提示添加 2021-08-25 11:40:57 +08:00
刘祥超
67e6e06e72 新安装检查数据库权限后删除测试表 2021-08-25 11:27:09 +08:00
刘祥超
d0bf85de17 创建集群时自动创建缓存策略和WAF策略/优化界面 2021-08-25 11:17:24 +08:00
刘祥超
14cb3bdc67 增加忽略相似消息周期设置 2021-08-24 20:45:07 +08:00
刘祥超
e9dadd5571 消息接收人可以设置接收消息时间段 2021-08-24 17:46:00 +08:00
刘祥超
d5eceb05ce 消息发送任务队列可以删除单个任务 2021-08-24 16:36:47 +08:00
刘祥超
b510c88f84 通知媒介可以设置发送频率 2021-08-24 15:46:37 +08:00
刘祥超
16e409e5f5 通知媒介增加任务队列查看功能 2021-08-24 14:22:15 +08:00
刘祥超
ad257fcd0e 优化js 2021-08-24 09:31:14 +08:00
刘祥超
e84087d6ec 修复指标table类型图表的js错误 2021-08-24 09:31:07 +08:00
刘祥超
218c2385f0 在服务日志中增加WAF日志筛选 2021-08-22 16:49:15 +08:00
刘祥超
4ef8c2bc77 全局访问日志增加WAF日志/优化交互/数据看板--WAF--最新拦截记录增加更多链接 2021-08-22 16:34:20 +08:00
刘祥超
d9bc1bdfca Dashboard可以提示API节点升级 2021-08-21 19:44:03 +08:00
刘祥超
a5c287e8d6 自建DNS支持递归查询 2021-08-21 16:46:49 +08:00
刘祥超
1e9f5c9c56 DNS访问日志显示匹配的线路 2021-08-20 11:27:06 +08:00
刘祥超
fbcf439f89 集群界面数据改成异步加载 2021-08-20 10:51:01 +08:00
刘祥超
766c3666d8 添加DNS账号时自动读取DNS服务商下域名 2021-08-19 14:26:25 +08:00
刘祥超
f08289351d IP名单批量导入IP支持CIDR 2021-08-19 10:50:15 +08:00
刘祥超
e426bba8b5 优化界面 2021-08-18 17:09:09 +08:00
刘祥超
4b200762a5 节点IP地址可以设置阈值 2021-08-18 16:19:07 +08:00
刘祥超
fd203ed436 节点IP增加是否启用、是否在线状态 2021-08-18 09:24:03 +08:00
刘祥超
04cea4dbd6 优化界面 2021-08-17 10:48:38 +08:00
刘祥超
a8f1056c3c 调整版本为0.3.0 2021-08-17 09:44:14 +08:00
刘祥超
c2576f4c62 优化界面 2021-08-16 14:44:32 +08:00
刘祥超
1da5d1094f WAF设置处增加文字提示 2021-08-16 10:03:44 +08:00
刘祥超
34e6122407 修复可能edgeTest无法升级的问题 2021-08-16 10:03:14 +08:00
刘祥超
ee61feb581 优化IPBox交互 2021-08-15 15:44:24 +08:00
刘祥超
7f04f1ed62 IPBox增加地域、ISP、添加到黑名单等功能 2021-08-15 15:42:05 +08:00
刘祥超
82646c3576 改进细节 2021-08-15 10:39:22 +08:00
刘祥超
b3f62240c4 节点选择认证时增加推荐 2021-08-14 21:33:48 +08:00
刘祥超
86a5992e8a 优化节点创建流程 2021-08-14 18:06:24 +08:00
刘祥超
82a731ed06 修复在MySQL8下安装提示无法创建edgeTest的问题 2021-08-13 19:37:56 +08:00
刘祥超
e650529efb 可以远程停止和启动DNS节点 2021-08-12 11:47:27 +08:00
刘祥超
1de57e124f DNS节点可以修改SSH登录相关信息 2021-08-12 11:04:28 +08:00
刘祥超
9e72dcc390 实现DNS节点远程安装 2021-08-11 20:59:58 +08:00
刘祥超
80019e2071 修改文字 2021-08-11 17:16:36 +08:00
刘祥超
ab7cdcd1b1 增加全局访问日志 2021-08-11 15:38:49 +08:00
刘祥超
7afe1e0a30 安全设置检查IP时同时也检查直接连接管理平台的上游IP 2021-08-11 10:01:23 +08:00
刘祥超
a39eb80214 安全设置中增加允许记住登录选项 2021-08-11 09:36:16 +08:00
刘祥超
10e2a08cd2 访问日志显示节点信息 2021-08-10 11:14:41 +08:00
刘祥超
6da4949c98 优化自建DNS交互 2021-08-10 10:47:12 +08:00
刘祥超
4d092f329b 自建DNS增加解析测试 2021-08-09 18:42:00 +08:00
刘祥超
69c1d35406 EdgeDNS支持内置线路 2021-08-09 13:56:11 +08:00
刘祥超
a256a7328e DNS节点增加在线状态通知 2021-08-08 10:29:57 +08:00
刘祥超
75c8658366 访问日志搜索增加域名和IP搜索 2021-08-07 22:04:30 +08:00
刘祥超
c3e68915a3 优化代码/支持IP名单的更多格式的导入、导出 2021-08-07 18:26:50 +08:00
刘祥超
d97f4da7fa 修复节点无法修改线路的Bug 2021-08-07 16:11:04 +08:00
刘祥超
06e81dbe37 调整版本为0.2.9 2021-08-07 15:41:21 +08:00
刘祥超
4a46aaa880 自建DNS增加全局配置 2021-08-05 16:08:18 +08:00
刘祥超
e766372a81 域名解析支持华为云解析DNS 2021-08-04 22:14:35 +08:00
刘祥超
c5ee9f095a 调整版本号 2021-08-04 15:36:30 +08:00
刘祥超
9734ed1cf8 调整版本号 2021-08-03 10:40:24 +08:00
刘祥超
548d122c64 修复在自动安装过程中不能填写SSH认证用户名的Bug 2021-08-03 06:29:01 +08:00
刘祥超
c5205ef7a9 优化界面 2021-08-02 14:51:15 +08:00
刘祥超
f71a27960b 优化界面 2021-08-01 14:54:27 +08:00
刘祥超
d747e656dc 优化界面 2021-08-01 11:13:17 +08:00
刘祥超
867215e2af 初步实现多集群共享节点 2021-07-31 22:23:07 +08:00
刘祥超
3614b9f3b7 修复服务设置 -- HTTP/HTTPS页面可能为空的Bug 2021-07-30 13:43:36 +08:00
刘祥超
cbfa4c85c1 优化文字 2021-07-29 17:40:16 +08:00
刘祥超
b9bef5042a 优化界面 2021-07-29 17:33:42 +08:00
刘祥超
46e523d005 实现基本的访问日志策略 2021-07-29 16:51:22 +08:00
刘祥超
fa2930dfc5 调整样式 2021-07-27 15:24:32 +08:00
刘祥超
086ab15e8a 优化交互 2021-07-27 11:49:04 +08:00
刘祥超
c2e3e784a2 优化一处规则显示 2021-07-27 11:26:52 +08:00
刘祥超
9dd01bd36e 网站服务显示服务错误的时候增加节点信息和链接 2021-07-27 10:48:42 +08:00
刘祥超
02d08b0762 图表中攻击流量类型改为Line Area 2021-07-27 09:51:47 +08:00
刘祥超
cbd312e67d 指标图表可以设置忽略空值和其他对象值 2021-07-26 16:44:22 +08:00
刘祥超
5084d9f87e 各个线图改成圆滑曲线 2021-07-26 14:32:24 +08:00
刘祥超
3c3408624d URL跳转模式默认改成匹配前缀 2021-07-26 12:57:41 +08:00
刘祥超
6e8403390b 自动跳转到HTTPS可以设置允许和排除的域名 2021-07-26 11:21:47 +08:00
刘祥超
ef9e5d4bbc 修复一个跳转到详情的链接错误 2021-07-26 10:09:07 +08:00
刘祥超
c0b49948d7 版本改为0.2.6 2021-07-26 08:27:06 +08:00
刘祥超
48b066fb83 改进文字 2021-07-25 20:34:31 +08:00
刘祥超
7d551d59db 缓存目录默认为/opt/cache 2021-07-25 20:19:58 +08:00
刘祥超
5a057857dc 点击访问日志的IP弹出该IP最近访问日志 2021-07-25 19:58:30 +08:00
刘祥超
0a9cea722c 修改部分文字 2021-07-25 19:42:07 +08:00
刘祥超
f7eb7b1ec6 将访问日志保留天数从30天改成14天 2021-07-25 19:41:38 +08:00
刘祥超
0eb57a734e 更新编译脚本 2021-07-25 16:28:48 +08:00
刘祥超
82ff9a2b06 区分社区版和商业版 2021-07-25 15:44:14 +08:00
刘祥超
051a48bc73 DNS支持TSIG 2021-07-25 15:08:11 +08:00
刘祥超
6b9d5cf7a0 DNS服务支持密钥管理/“域名服务”改为“自建DNS” 2021-07-25 09:44:29 +08:00
刘祥超
2e7d64d97b 更新相关库 2021-07-22 18:44:55 +08:00
刘祥超
dd6050ee0b 修复在手机端无法正常浏览图表的Bug 2021-07-22 10:45:36 +08:00
刘祥超
c954910728 修复企业版身份认证可能会导致异常退出的问题 2021-07-22 10:45:23 +08:00
刘祥超
0fe2bc4a54 修复在使用内存缓存时导致节点看板页为空的Bug 2021-07-22 08:25:42 +08:00
刘祥超
9f39cd615f Demo模式下不允许调用Sync方法 2021-07-22 08:25:14 +08:00
刘祥超
07bf21dbb4 修改DEMO链接地址 2021-07-21 22:18:08 +08:00
刘祥超
43e8c65473 增加DEMO连接 2021-07-21 22:14:40 +08:00
刘祥超
8a926eabbe 增加DEMO模式、修复监控节点、用户节点、认证节点无法查看运行日志的Bug 2021-07-21 22:14:33 +08:00
刘祥超
fcbed5f63e 修复DNS看板中Y坐标轴不正确的问题 2021-07-21 09:02:28 +08:00
刘祥超
8d203a1d7c 优化界面 2021-07-21 08:07:46 +08:00
刘祥超
d6efb2dd12 域名记录可以停用/启用 2021-07-20 19:02:45 +08:00
刘祥超
6ea3938f74 自动替换API节点时增加对新节点的测试 2021-07-20 18:10:22 +08:00
刘祥超
63e773ce96 增加恢复模式 2021-07-20 17:15:17 +08:00
刘祥超
60b8e1041c 在各个地方支持IPv6 2021-07-20 10:55:25 +08:00
刘祥超
fb263c926b 创建网站服务后自动开启访问日志 2021-07-20 08:48:55 +08:00
刘祥超
dbbae30f5a 创建网站服务后自动开启Websocket 2021-07-20 08:29:50 +08:00
刘祥超
7b73c86fa4 增加内置的统计指标 2021-07-19 20:38:19 +08:00
刘祥超
c16c6ea6a6 优化指标图表 2021-07-19 18:19:33 +08:00
刘祥超
4ecba0040a Dashboard增加指标图表 2021-07-19 17:58:02 +08:00
刘祥超
f65aabe897 指标中列出使用当前指标的集群 2021-07-19 15:48:36 +08:00
刘祥超
de2337afe7 实现公用的统计指标 2021-07-19 15:23:20 +08:00
刘祥超
3360f2fc08 实现新的CC 2021-07-19 10:48:53 +08:00
刘祥超
7d8f3a9d9b IP名单中可以通过IP查找访问日志 2021-07-18 17:09:31 +08:00
刘祥超
63884bc836 优化WAF动作 2021-07-18 15:52:50 +08:00
刘祥超
9c231a2b8c WAF支持更多动作 2021-07-14 22:45:52 +08:00
刘祥超
6a5c979d2a 修复一个指标图表无法显示时间的Bug 2021-07-14 22:44:13 +08:00
刘祥超
094734ae1f 安装时不检查API地址是否可以绑定 2021-07-14 15:40:23 +08:00
刘祥超
97b4d457fd IP测试时同时也检查绑定的IP名单 2021-07-13 15:50:09 +08:00
刘祥超
9669c0ddda 路径规则改成路由规则 2021-07-13 14:28:06 +08:00
刘祥超
daf8257a1f 优化看板 2021-07-13 11:04:18 +08:00
刘祥超
40f32c7cf9 访问日志增加更容易可视化的时间显示 2021-07-12 18:06:43 +08:00
刘祥超
b8295e5cfc 改进界面 2021-07-12 17:37:10 +08:00
刘祥超
aba489217a 修复<keyword>组件的安全问题 2021-07-12 17:35:33 +08:00
刘祥超
7775689dfa 实现数据看板--WAF 2021-07-12 16:56:56 +08:00
刘祥超
90397b2b6f 修改一处文字 2021-07-12 10:27:35 +08:00
刘祥超
32683a2b59 管理界面可以切换风格 2021-07-12 10:21:17 +08:00
刘祥超
9e36847397 实现数据看板-DNS 2021-07-11 21:44:03 +08:00
刘祥超
4c82804622 实现数据看板--用户 2021-07-11 18:05:49 +08:00
刘祥超
cb911c474e 数据看板使用字节代替比特 2021-07-11 11:09:41 +08:00
刘祥超
ce2537d69d 节点看板磁盘缓存用量增加剩余磁盘空间 2021-07-08 19:47:49 +08:00
刘祥超
38746e83c5 节点看板增加缓存目录用量 2021-07-08 19:43:39 +08:00
刘祥超
be8988d309 优化关键词显示 2021-07-08 16:23:51 +08:00
刘祥超
ed59994db8 实现服务看板(企业版) 2021-07-07 19:55:32 +08:00
刘祥超
11a8d3ba70 优化交互 2021-07-07 19:02:39 +08:00
刘祥超
adf91bf3d0 节点列表增加下行流量,节点列表可以按CPU、内存、下行流量排序 2021-07-07 15:17:41 +08:00
刘祥超
7729b88a00 取消节点的监控图表,使用节点看板代替 2021-07-06 20:09:47 +08:00
刘祥超
e93aecabb4 实现节点看板(仅对企业版开放) 2021-07-06 20:06:20 +08:00
刘祥超
3cf43b2cc1 优化API节点详情页 2021-07-06 15:31:09 +08:00
刘祥超
3cba8cf98d 优化ACME任务修改页面 2021-07-06 15:30:06 +08:00
刘祥超
e2ca94c93b 实现集群看板(暂时只对企业版开放) 2021-07-05 11:38:07 +08:00
刘祥超
1c3e25ba1a 指标增加一些易读的名称等信息 2021-07-03 18:05:55 +08:00
刘祥超
f297f4ec52 实现基本的图表管理 2021-07-03 15:44:49 +08:00
刘祥超
534f10b8a7 指标数据增加占比数据 2021-07-01 10:39:07 +08:00
刘祥超
a7aed3d49e 实现基础的统计指标 2021-06-30 19:59:59 +08:00
刘祥超
9a915a48b5 SSH认证--私钥认证方式增加用户名选项 2021-06-30 14:55:21 +08:00
刘祥超
81a3b299f0 优化交互 2021-06-28 21:09:22 +08:00
刘祥超
a4edae6692 修改版本为0.2.5 2021-06-28 10:31:56 +08:00
刘祥超
047f6c409f API节点相关操作增加一些提示 2021-06-28 09:38:26 +08:00
刘祥超
963be2bc63 阶段性提交 2021-06-27 21:59:06 +08:00
刘祥超
bd34a9bd37 服务列表可以搜索端口号 2021-06-25 11:04:45 +08:00
刘祥超
32b93b2bd9 安装时默认设置访问日志保留30天 2021-06-24 21:06:43 +08:00
刘祥超
e7c4507b0b 集群设置左侧菜单显示TOA设置状态 2021-06-24 18:03:33 +08:00
刘祥超
17ae34c44f ACME申请证书时可以设置回调URL 2021-06-24 09:58:44 +08:00
刘祥超
b466ea2fd1 ACME申请证书时可以设置回调URL 2021-06-24 09:27:11 +08:00
刘祥超
8e4ee54f03 实现公用的IP名单 2021-06-23 13:12:33 +08:00
刘祥超
c99547d9e3 变更版本 2021-06-21 14:44:13 +08:00
1188 changed files with 40934 additions and 8453 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
*_plus.go
*-plus.sh
*_plus.html
*_plus.js
*@plus.js

View File

@@ -1,13 +1,16 @@
# GoEdge目标
做一款人人用得起的CDN & WAF系统。
![架构](doc/screenshot.png)
![截图](doc/screenshot.png)
## 特性
* `免费` - 开源、免费、自由、开放
* `简单` - 架构简单清晰,安装简单,使用简单,运维简单
* `高扩展性` - 可以自由扩展新的节点,支持亿级数据
## 在线演示
* [http://demo.goedge.cn](http://demo.goedge.cn)
## 文档
* [新手指南](https://edge.teaos.cn/docs/QuickStart/Index.md)
* [完整文档](https://edge.teaos.cn/docs)

View File

@@ -1,5 +1,17 @@
#!/usr/bin/env bash
ROOT=$(dirname $0)
# build all nodes
if [ -f $ROOT"/../../EdgeNode/build/build-all-plus.sh" ]; then
echo "=============================="
echo "build all edge-node"
echo "=============================="
cd $ROOT"/../../EdgeNode/build"
./build-all-plus.sh
cd -
fi
./build.sh linux amd64
./build.sh linux 386
./build.sh linux arm64

View File

@@ -1,91 +0,0 @@
#!/usr/bin/env bash
function build() {
ROOT=$(dirname $0)
NAME="edge-admin"
DIST=$ROOT/"../dist/${NAME}"
OS=${1}
ARCH=${2}
if [ -z $OS ]; then
echo "usage: build.sh OS ARCH"
exit
fi
if [ -z $ARCH ]; then
echo "usage: build.sh OS ARCH"
exit
fi
VERSION=$(lookup-version $ROOT/../internal/const/const.go)
ZIP="${NAME}-${OS}-${ARCH}-v${VERSION}.zip"
# check edge-api
APINodeVersion=$(lookup-version $ROOT"/../../EdgeAPI/internal/const/const.go")
echo "building edge-api v${APINodeVersion} ..."
EDGE_API_BUILD_SCRIPT=$ROOT"/../../EdgeAPI/build/build.sh"
if [ ! -f $EDGE_API_BUILD_SCRIPT ]; then
echo "unable to find edge-api build script 'EdgeAPI/build/build.sh'"
exit
fi
cd $ROOT"/../../EdgeAPI/build"
echo "=============================="
./build.sh $OS $ARCH
echo "=============================="
cd -
# create dir & copy files
echo "copying ..."
if [ ! -d $DIST ]; then
mkdir $DIST
mkdir $DIST/bin
mkdir $DIST/configs
mkdir $DIST/logs
fi
cp -R $ROOT/../web $DIST/
rm -f $DIST/web/tmp/*
cp $ROOT/configs/server.template.yaml $DIST/configs/
EDGE_API_ZIP_FILE=$ROOT"/../../EdgeAPI/dist/edge-api-${OS}-${ARCH}-v${APINodeVersion}.zip"
cp $EDGE_API_ZIP_FILE $DIST/
cd $DIST/
unzip -q $(basename $EDGE_API_ZIP_FILE)
rm -f $(basename $EDGE_API_ZIP_FILE)
cd -
# build
echo "building "${NAME}" ..."
env GOOS=$OS GOARCH=$GOARCH go build -ldflags="-s -w" -tags demo -o $DIST/bin/${NAME} $ROOT/../cmd/edge-admin/main.go
# delete hidden files
find $DIST -name ".DS_Store" -delete
find $DIST -name ".gitignore" -delete
# zip
echo "zip files ..."
cd "${DIST}/../" || exit
if [ -f "${ZIP}" ]; then
rm -f "${ZIP}"
fi
zip -r -X -q "${ZIP}" ${NAME}/
rm -rf ${NAME}
cd - || exit
echo "[done]"
}
function lookup-version() {
FILE=$1
VERSION_DATA=$(cat $FILE)
re="Version[ ]+=[ ]+\"([0-9.]+)\""
if [[ $VERSION_DATA =~ $re ]]; then
VERSION=${BASH_REMATCH[1]}
echo $VERSION
else
echo "could not match version"
exit
fi
}
build $1 $2

View File

@@ -6,6 +6,7 @@ function build() {
DIST=$ROOT/"../dist/${NAME}"
OS=${1}
ARCH=${2}
TAG=${3}
if [ -z $OS ]; then
echo "usage: build.sh OS ARCH"
@@ -15,11 +16,14 @@ function build() {
echo "usage: build.sh OS ARCH"
exit
fi
if [ -z $TAG ]; then
TAG="community"
fi
VERSION=$(lookup-version $ROOT/../internal/const/const.go)
ZIP="${NAME}-${OS}-${ARCH}-v${VERSION}.zip"
ZIP="${NAME}-${OS}-${ARCH}-${TAG}-v${VERSION}.zip"
# check edge-api
# build edge-api
APINodeVersion=$(lookup-version $ROOT"/../../EdgeAPI/internal/const/const.go")
echo "building edge-api v${APINodeVersion} ..."
EDGE_API_BUILD_SCRIPT=$ROOT"/../../EdgeAPI/build/build.sh"
@@ -30,7 +34,7 @@ function build() {
cd $ROOT"/../../EdgeAPI/build"
echo "=============================="
./build.sh $OS $ARCH
./build.sh $OS $ARCH $TAG
echo "=============================="
cd -
@@ -47,7 +51,7 @@ function build() {
rm -f $DIST/web/tmp/*
cp $ROOT/configs/server.template.yaml $DIST/configs/
EDGE_API_ZIP_FILE=$ROOT"/../../EdgeAPI/dist/edge-api-${OS}-${ARCH}-v${APINodeVersion}.zip"
EDGE_API_ZIP_FILE=$ROOT"/../../EdgeAPI/dist/edge-api-${OS}-${ARCH}-${TAG}-v${APINodeVersion}.zip"
cp $EDGE_API_ZIP_FILE $DIST/
cd $DIST/
unzip -q $(basename $EDGE_API_ZIP_FILE)
@@ -56,11 +60,14 @@ function build() {
# build
echo "building "${NAME}" ..."
env GOOS=$OS GOARCH=$GOARCH go build -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 ..."
@@ -88,4 +95,4 @@ function lookup-version() {
fi
}
build $1 $2
build $1 $2 $3

View File

@@ -1,4 +1,5 @@
api.yaml
server.yaml
api_db.yaml
*.pem
*.pem
*.cache.json

View File

@@ -8,20 +8,22 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/nodes"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/gosock/pkg/gosock"
)
func main() {
app := apps.NewAppCmd().
Version(teaconst.Version).
Product(teaconst.ProductName).
Usage(teaconst.ProcessName+" [-v|start|stop|restart|service|daemon|reset]").
Usage(teaconst.ProcessName+" [-v|start|stop|restart|service|daemon|reset|recover|demo]").
Option("-h", "show this help").
Option("-v", "show version").
Option("start", "start the service").
Option("stop", "stop the service").
Option("service", "register service into systemd").
Option("daemon", "start the service with daemon").
Option("reset", "reset configs")
Option("reset", "reset configs").
Option("recover", "enter recovery mode")
app.On("daemon", func() {
nodes.NewAdminNode().Daemon()
@@ -42,6 +44,32 @@ func main() {
}
fmt.Println("done")
})
app.On("recover", func() {
sock := gosock.NewTmpSock(teaconst.ProcessName)
if !sock.IsListening() {
fmt.Println("[ERROR]the service not started yet, you should start the service first")
return
}
_, err := sock.Send(&gosock.Command{Code: "recover"})
if err != nil {
fmt.Println("[ERROR]enter recovery mode failed: " + err.Error())
return
}
fmt.Println("enter recovery mode successfully")
})
app.On("demo", func() {
sock := gosock.NewTmpSock(teaconst.ProcessName)
if !sock.IsListening() {
fmt.Println("[ERROR]the service not started yet, you should start the service first")
return
}
_, 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")
})
app.Run(func() {
adminNode := nodes.NewAdminNode()
adminNode.Run()

8
go.mod
View File

@@ -1,6 +1,6 @@
module github.com/TeaOSLab/EdgeAdmin
go 1.15
go 1.16
replace github.com/TeaOSLab/EdgeCommon => ../EdgeCommon
@@ -10,9 +10,11 @@ require (
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-20210411134150-ddf57e240c2f
github.com/iwind/TeaGo v0.0.0-20211026123858-7de7a21cad24
github.com/iwind/gosock v0.0.0-20210722083328-12b2d66abec3
github.com/json-iterator/go v1.1.12 // indirect
github.com/miekg/dns v1.1.35
github.com/shirou/gopsutil v3.21.5+incompatible // indirect
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

32
go.sum
View File

@@ -44,7 +44,6 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
@@ -55,7 +54,6 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
@@ -63,10 +61,17 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f h1:r2O8PONj/KiuZjJHVHn7KlCePUIjNtgAmvLfgRafQ8o=
github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/TeaGo v0.0.0-20210628135026-38575a4ab060/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/TeaGo v0.0.0-20210831140440-a2a442471b13 h1:HuEJ5xJfujW1Q6rNDhOu5LQXEBB2qLPah3jYslT8Gz4=
github.com/iwind/TeaGo v0.0.0-20210831140440-a2a442471b13/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
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/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/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=
@@ -76,8 +81,12 @@ 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/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 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
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=
@@ -100,8 +109,6 @@ github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa h1:2cO3RojjYl3hVTbEvJVqrMaFmORhL6O06qdW42toftk=
github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa/go.mod h1:Yjr3bdWaVWyME1kha7X0jsz3k2DgXNa1Pj3XGyUAbx8=
github.com/shirou/gopsutil v3.21.5+incompatible h1:OloQyEerMi7JUrXiNzy8wQ5XN+baemxSl12QgIzt0jc=
github.com/shirou/gopsutil v3.21.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
@@ -141,16 +148,16 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
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/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
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-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
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=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -165,9 +172,8 @@ golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/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=
@@ -175,7 +181,6 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7s
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -193,15 +198,14 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapK
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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
google.golang.org/genproto v0.0.0-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=
@@ -210,7 +214,6 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0=
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
@@ -222,7 +225,6 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=

View File

@@ -2,17 +2,19 @@ package apps
import (
"fmt"
"github.com/iwind/TeaGo/Tea"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"github.com/iwind/gosock/pkg/gosock"
"os"
"os/exec"
"runtime"
"strconv"
"syscall"
"time"
)
// App命令帮助
// AppCmd App命令帮助
type AppCmd struct {
product string
version string
@@ -21,10 +23,14 @@ type AppCmd struct {
appendStrings []string
directives []*Directive
sock *gosock.Sock
}
func NewAppCmd() *AppCmd {
return &AppCmd{}
return &AppCmd{
sock: gosock.NewTmpSock(teaconst.ProcessName),
}
}
type CommandHelpOption struct {
@@ -32,25 +38,25 @@ type CommandHelpOption struct {
Description string
}
// 产品
// Product 产品
func (this *AppCmd) Product(product string) *AppCmd {
this.product = product
return this
}
// 版本
// Version 版本
func (this *AppCmd) Version(version string) *AppCmd {
this.version = version
return this
}
// 使用方法
// Usage 使用方法
func (this *AppCmd) Usage(usage string) *AppCmd {
this.usage = usage
return this
}
// 选项
// Option 选项
func (this *AppCmd) Option(code string, description string) *AppCmd {
this.options = append(this.options, &CommandHelpOption{
Code: code,
@@ -59,13 +65,13 @@ func (this *AppCmd) Option(code string, description string) *AppCmd {
return this
}
// 附加内容
// Append 附加内容
func (this *AppCmd) Append(appendString string) *AppCmd {
this.appendStrings = append(this.appendStrings, appendString)
return this
}
// 打印
// Print 打印
func (this *AppCmd) Print() {
fmt.Println(this.product + " v" + this.version)
@@ -104,7 +110,7 @@ func (this *AppCmd) Print() {
}
}
// 添加指令
// On 添加指令
func (this *AppCmd) On(arg string, callback func()) {
this.directives = append(this.directives, &Directive{
Arg: arg,
@@ -112,7 +118,7 @@ func (this *AppCmd) On(arg string, callback func()) {
})
}
// 运行
// Run 运行
func (this *AppCmd) Run(main func()) {
// 获取参数
args := os.Args[1:]
@@ -151,9 +157,6 @@ func (this *AppCmd) Run(main func()) {
return
}
// 记录PID
_ = this.writePid()
// 日志
writer := new(LogWriter)
writer.Init()
@@ -165,7 +168,7 @@ func (this *AppCmd) Run(main func()) {
// 版本号
func (this *AppCmd) runVersion() {
fmt.Println(this.product+" v"+this.version, "(build: "+runtime.Version(), runtime.GOOS, runtime.GOARCH+")")
fmt.Println(this.product+" v"+this.version, "(build: "+runtime.Version(), runtime.GOOS, runtime.GOARCH, teaconst.Tag+")")
}
// 帮助
@@ -175,9 +178,9 @@ func (this *AppCmd) runHelp() {
// 启动
func (this *AppCmd) runStart() {
proc := this.checkPid()
if proc != nil {
fmt.Println(this.product+" already started, pid:", proc.Pid)
var pid = this.getPID()
if pid > 0 {
fmt.Println(this.product+" already started, pid:", pid)
return
}
@@ -193,18 +196,15 @@ func (this *AppCmd) runStart() {
// 停止
func (this *AppCmd) runStop() {
proc := this.checkPid()
if proc == nil {
var pid = this.getPID()
if pid == 0 {
fmt.Println(this.product + " not started yet")
return
}
// 停止进程
_ = proc.Signal(syscall.SIGQUIT)
_, _ = this.sock.Send(&gosock.Command{Code: "stop"})
// 在Windows上经常不能及时释放资源
_ = DeletePid(Tea.Root + "/bin/pid")
fmt.Println(this.product+" stopped ok, pid:", proc.Pid)
fmt.Println(this.product+" stopped ok, pid:", types.String(pid))
}
// 重启
@@ -216,20 +216,24 @@ func (this *AppCmd) runRestart() {
// 状态
func (this *AppCmd) runStatus() {
proc := this.checkPid()
if proc == nil {
var pid = this.getPID()
if pid == 0 {
fmt.Println(this.product + " not started yet")
} else {
fmt.Println(this.product + " is running, pid: " + fmt.Sprintf("%d", proc.Pid))
return
}
fmt.Println(this.product + " is running, pid: " + types.String(pid))
}
// 检查PID
func (this *AppCmd) checkPid() *os.Process {
return CheckPid(Tea.Root + "/bin/pid")
}
// 获取当前的PID
func (this *AppCmd) getPID() int {
if !this.sock.IsListening() {
return 0
}
// 写入PID
func (this *AppCmd) writePid() error {
return WritePid(Tea.Root + "/bin/pid")
reply, err := this.sock.Send(&gosock.Command{Code: "pid"})
if err != nil {
return 0
}
return maps.NewMap(reply.Params).GetInt("pid")
}

View File

@@ -1,17 +0,0 @@
// +build !windows
package apps
import (
"os"
"syscall"
)
// lock file
func LockFile(fp *os.File) error {
return syscall.Flock(int(fp.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)
}
func UnlockFile(fp *os.File) error {
return syscall.Flock(int(fp.Fd()), syscall.LOCK_UN)
}

View File

@@ -1,17 +0,0 @@
// +build windows
package apps
import (
"errors"
"os"
)
// lock file
func LockFile(fp *os.File) error {
return errors.New("not implemented on windows")
}
func UnlockFile(fp *os.File) error {
return errors.New("not implemented on windows")
}

View File

@@ -1,113 +0,0 @@
package apps
import (
"fmt"
"github.com/iwind/TeaGo/types"
"io/ioutil"
"os"
"runtime"
)
var pidFileList = []*os.File{}
// 检查Pid
func CheckPid(path string) *os.Process {
// windows上打开的文件是不能删除的
if runtime.GOOS == "windows" {
if os.Remove(path) == nil {
return nil
}
}
file, err := os.Open(path)
if err != nil {
return nil
}
defer func() {
_ = file.Close()
}()
// 是否能取得Lock
err = LockFile(file)
if err == nil {
_ = UnlockFile(file)
return nil
}
pidBytes, err := ioutil.ReadAll(file)
if err != nil {
return nil
}
pid := types.Int(string(pidBytes))
if pid <= 0 {
return nil
}
proc, _ := os.FindProcess(pid)
return proc
}
// 写入Pid
func WritePid(path string) error {
fp, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_RDONLY, 0666)
if err != nil {
return err
}
if runtime.GOOS != "windows" {
err = LockFile(fp)
if err != nil {
return err
}
}
pidFileList = append(pidFileList, fp) // hold the file pointers
_, err = fp.WriteString(fmt.Sprintf("%d", os.Getpid()))
if err != nil {
return err
}
return nil
}
// 写入Ppid
func WritePpid(path string) error {
fp, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_RDONLY, 0666)
if err != nil {
return err
}
if runtime.GOOS != "windows" {
err = LockFile(fp)
if err != nil {
return err
}
}
pidFileList = append(pidFileList, fp) // hold the file pointers
_, err = fp.WriteString(fmt.Sprintf("%d", os.Getppid()))
if err != nil {
return err
}
return nil
}
// 删除Pid
func DeletePid(path string) error {
_, err := os.Stat(path)
if err != nil {
if !os.IsNotExist(err) {
return nil
}
return err
}
for _, fp := range pidFileList {
_ = UnlockFile(fp)
_ = fp.Close()
}
return os.Remove(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" // 只要登录就可以访问的模块
@@ -44,6 +45,7 @@ func loadAdminModuleMapping() (map[int64]*AdminModuleList, error) {
list := &AdminModuleList{
IsSuper: m.IsSuper,
Fullname: m.Fullname,
Theme: m.Theme,
}
for _, pbModule := range m.Modules {
@@ -132,6 +134,29 @@ func FindAdminFullname(adminId int64) string {
return ""
}
// FindAdminTheme 查找某个管理员选择的风格
func FindAdminTheme(adminId int64) string {
locker.Lock()
defer locker.Unlock()
list, ok := sharedAdminModuleMapping[adminId]
if ok {
return list.Theme
}
return ""
}
// UpdateAdminTheme 设置某个管理员的风格
func UpdateAdminTheme(adminId int64, theme string) {
locker.Lock()
defer locker.Unlock()
list, ok := sharedAdminModuleMapping[adminId]
if ok {
list.Theme = theme
}
}
// AllModuleMaps 所有权限列表
func AllModuleMaps() []maps.Map {
m := []maps.Map{
@@ -158,7 +183,7 @@ func AllModuleMaps() []maps.Map {
}
if teaconst.IsPlus {
m = append(m, maps.Map{
"name": "域名服务",
"name": "智能DNS",
"code": AdminModuleCodeNS,
"url": "/ns",
})
@@ -179,6 +204,11 @@ func AllModuleMaps() []maps.Map {
"code": AdminModuleCodeFinance,
"url": "/finance",
},
{
"name": "套餐管理",
"code": AdminModuleCodePlan,
"url": "/plans",
},
{
"name": "日志审计",
"code": AdminModuleCodeLog,

View File

@@ -6,6 +6,7 @@ type AdminModuleList struct {
IsSuper bool
Modules []*systemconfigs.AdminModule
Fullname string
Theme string
}
func (this *AdminModuleList) Allow(module string) bool {

View File

@@ -108,7 +108,6 @@ func (this *APIConfig) WriteFile(path string) error {
if err != nil {
return err
}
err = ioutil.WriteFile(path, data, 0666)
// 写入 ~/ 和 /etc/ 目录,因为是备份需要,所以不需要提示错误信息
// 写入 ~/.edge-admin/
@@ -141,5 +140,10 @@ func (this *APIConfig) WriteFile(path string) error {
}
}
return err
err = ioutil.WriteFile(path, data, 0666)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,40 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package configs
import (
"encoding/json"
"github.com/iwind/TeaGo/Tea"
"io/ioutil"
)
var plusConfigFile = "plus.cache.json"
type PlusConfig struct {
IsPlus bool `json:"isPlus"`
}
func ReadPlusConfig() *PlusConfig {
data, err := ioutil.ReadFile(Tea.ConfigFile(plusConfigFile))
if err != nil {
return &PlusConfig{IsPlus: false}
}
var config = &PlusConfig{IsPlus: false}
err = json.Unmarshal(data, config)
if err != nil {
return config
}
return config
}
func WritePlusConfig(config *PlusConfig) error {
configJSON, err := json.Marshal(config)
if err != nil {
return err
}
err = ioutil.WriteFile(Tea.ConfigFile(plusConfigFile), configJSON, 0777)
if err != nil {
return err
}
return nil
}

8
internal/const/build.go Normal file
View File

@@ -0,0 +1,8 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
// +build community
package teaconst
const BuildCommunity = true
const BuildPlus = false
const Tag = "community"

View File

@@ -1,7 +1,9 @@
package teaconst
const (
Version = "0.2.3"
Version = "0.3.5"
APINodeVersion = "0.3.5"
ProductName = "Edge Admin"
ProcessName = "edge-admin"

View File

@@ -1,7 +0,0 @@
// +build demo
package teaconst
const (
IsDemo = true
)

View File

@@ -1,7 +0,0 @@
// +build !demo
package teaconst
const (
IsDemo = false
)

12
internal/const/vars.go Normal file
View File

@@ -0,0 +1,12 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package teaconst
var (
IsRecoverMode = false
)
var (
IsDemoMode = false
ErrorDemoOperation = "DEMO模式下无法进行创建、修改、删除等操作"
)

View File

@@ -10,11 +10,12 @@ import (
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/sessions"
"github.com/iwind/gosock/pkg/gosock"
"io/ioutil"
"log"
"net"
"os"
"os/exec"
"os/signal"
@@ -25,6 +26,7 @@ import (
var SharedAdminNode *AdminNode = nil
type AdminNode struct {
sock *gosock.Sock
subPIDs []int
}
@@ -42,7 +44,7 @@ func (this *AdminNode) Run() {
// 本地Sock
err := this.listenSock()
if err != nil {
logs.Println("NODE" + err.Error())
logs.Println("[NODE]", err.Error())
return
}
@@ -89,11 +91,11 @@ func (this *AdminNode) Run() {
// Daemon 实现守护进程
func (this *AdminNode) Daemon() {
path := os.TempDir() + "/edge-admin.sock"
var sock = gosock.NewTmpSock(teaconst.ProcessName)
isDebug := lists.ContainsString(os.Args, "debug")
isDebug = true
for {
conn, err := net.DialTimeout("unix", path, 1*time.Second)
conn, err := sock.Dial()
if err != nil {
if isDebug {
log.Println("[DAEMON]starting ...")
@@ -281,37 +283,72 @@ func (this *AdminNode) genSecret() string {
// 监听本地sock
func (this *AdminNode) listenSock() error {
path := os.TempDir() + "/edge-admin.sock"
this.sock = gosock.NewTmpSock(teaconst.ProcessName)
// 检查是否已经存
_, err := os.Stat(path)
if err == nil {
conn, err := net.Dial("unix", path)
if err != nil {
_ = os.Remove(path)
// 检查是否在运行
if this.sock.IsListening() {
reply, err := this.sock.Send(&gosock.Command{Code: "pid"})
if err == nil {
return errors.New("error: the process is already running, pid: " + maps.NewMap(reply.Params).GetString("pid"))
} else {
_ = conn.Close()
return errors.New("error: the process is already running")
}
}
// 新的监听任务
listener, err := net.Listen("unix", path)
if err != nil {
return err
}
events.On(events.EventQuit, func() {
logs.Println("NODE", "quit unix sock")
_ = listener.Close()
})
// 启动监听
go func() {
for {
_, err := listener.Accept()
if err != nil {
return
this.sock.OnCommand(func(cmd *gosock.Command) {
switch cmd.Code {
case "pid":
_ = cmd.Reply(&gosock.Command{
Code: "pid",
Params: map[string]interface{}{
"pid": os.Getpid(),
},
})
case "stop":
_ = cmd.ReplyOk()
// 关闭子进程
for _, pid := range this.subPIDs {
p, err := os.FindProcess(pid)
if err == nil && p != nil {
_ = p.Kill()
}
}
// 退出主进程
events.Notify(events.EventQuit)
os.Exit(0)
case "recover":
teaconst.IsRecoverMode = true
_ = cmd.ReplyOk()
case "demo":
teaconst.IsDemoMode = !teaconst.IsDemoMode
_ = cmd.ReplyOk()
case "info":
exePath, _ := os.Executable()
_ = cmd.Reply(&gosock.Command{
Code: "info",
Params: map[string]interface{}{
"pid": os.Getpid(),
"version": teaconst.Version,
"path": exePath,
},
})
}
})
err := this.sock.Listen()
if err != nil {
logs.Println("NODE", err.Error())
}
}()
events.On(events.EventQuit, func() {
logs.Println("NODE", "quit unix sock")
_ = this.sock.Close()
})
return nil
}

View File

@@ -1,7 +0,0 @@
package rpc
import "context"
type ContextInterface interface {
AdminContext() context.Context
}

View File

@@ -27,11 +27,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,11 +46,17 @@ func NewRPCClient(apiConfig *configs.APIConfig) (*RPCClient, error) {
}
// 设置RPC
dao.SetRPC(client)
if isPrimary {
dao.SetRPC(client)
}
return client, nil
}
func (this *RPCClient) APITokenRPC() pb.APITokenServiceClient {
return pb.NewAPITokenServiceClient(this.pickConn())
}
func (this *RPCClient) AdminRPC() pb.AdminServiceClient {
return pb.NewAdminServiceClient(this.pickConn())
}
@@ -67,6 +73,10 @@ func (this *RPCClient) NodeGrantRPC() pb.NodeGrantServiceClient {
return pb.NewNodeGrantServiceClient(this.pickConn())
}
func (this *RPCClient) NodeLoginRPC() pb.NodeLoginServiceClient {
return pb.NewNodeLoginServiceClient(this.pickConn())
}
func (this *RPCClient) NodeClusterRPC() pb.NodeClusterServiceClient {
return pb.NewNodeClusterServiceClient(this.pickConn())
}
@@ -91,6 +101,14 @@ func (this *RPCClient) NodeIPAddressRPC() pb.NodeIPAddressServiceClient {
return pb.NewNodeIPAddressServiceClient(this.pickConn())
}
func (this *RPCClient) NodeIPAddressLogRPC() pb.NodeIPAddressLogServiceClient {
return pb.NewNodeIPAddressLogServiceClient(this.pickConn())
}
func (this *RPCClient) NodeIPAddressThresholdRPC() pb.NodeIPAddressThresholdServiceClient {
return pb.NewNodeIPAddressThresholdServiceClient(this.pickConn())
}
func (this *RPCClient) NodeValueRPC() pb.NodeValueServiceClient {
return pb.NewNodeValueServiceClient(this.pickConn())
}
@@ -207,6 +225,10 @@ func (this *RPCClient) HTTPFirewallRuleSetRPC() pb.HTTPFirewallRuleSetServiceCli
return pb.NewHTTPFirewallRuleSetServiceClient(this.pickConn())
}
func (this *RPCClient) FirewallRPC() pb.FirewallServiceClient {
return pb.NewFirewallServiceClient(this.pickConn())
}
func (this *RPCClient) HTTPLocationRPC() pb.HTTPLocationServiceClient {
return pb.NewHTTPLocationServiceClient(this.pickConn())
}
@@ -284,6 +306,18 @@ func (this *RPCClient) IPListRPC() pb.IPListServiceClient {
return pb.NewIPListServiceClient(this.pickConn())
}
func (this *RPCClient) ReportNodeRPC() pb.ReportNodeServiceClient {
return pb.NewReportNodeServiceClient(this.pickConn())
}
func (this *RPCClient) ReportNodeGroupRPC() pb.ReportNodeGroupServiceClient {
return pb.NewReportNodeGroupServiceClient(this.pickConn())
}
func (this *RPCClient) ReportResultRPC() pb.ReportResultServiceClient {
return pb.NewReportResultServiceClient(this.pickConn())
}
func (this *RPCClient) IPItemRPC() pb.IPItemServiceClient {
return pb.NewIPItemServiceClient(this.pickConn())
}
@@ -332,6 +366,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())
}
@@ -340,6 +382,18 @@ func (this *RPCClient) UserBillRPC() pb.UserBillServiceClient {
return pb.NewUserBillServiceClient(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())
}
@@ -380,6 +434,10 @@ func (this *RPCClient) NSRecordRPC() pb.NSRecordServiceClient {
return pb.NewNSRecordServiceClient(this.pickConn())
}
func (this *RPCClient) NSKeyRPC() pb.NSKeyServiceClient {
return pb.NewNSKeyServiceClient(this.pickConn())
}
func (this *RPCClient) NSRouteRPC() pb.NSRouteServiceClient {
return pb.NewNSRouteServiceClient(this.pickConn())
}
@@ -388,6 +446,46 @@ func (this *RPCClient) NSAccessLogRPC() pb.NSAccessLogServiceClient {
return pb.NewNSAccessLogServiceClient(this.pickConn())
}
func (this *RPCClient) NSRPC() pb.NSServiceClient {
return pb.NewNSServiceClient(this.pickConn())
}
func (this *RPCClient) NSQuestionOptionRPC() pb.NSQuestionOptionServiceClient {
return pb.NewNSQuestionOptionServiceClient(this.pickConn())
}
func (this *RPCClient) MetricItemRPC() pb.MetricItemServiceClient {
return pb.NewMetricItemServiceClient(this.pickConn())
}
func (this *RPCClient) MetricStatRPC() pb.MetricStatServiceClient {
return pb.NewMetricStatServiceClient(this.pickConn())
}
func (this *RPCClient) MetricChartRPC() pb.MetricChartServiceClient {
return pb.NewMetricChartServiceClient(this.pickConn())
}
func (this *RPCClient) NodeClusterMetricItemRPC() pb.NodeClusterMetricItemServiceClient {
return pb.NewNodeClusterMetricItemServiceClient(this.pickConn())
}
func (this *RPCClient) ServerStatBoardRPC() pb.ServerStatBoardServiceClient {
return pb.NewServerStatBoardServiceClient(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())
}
// Context 构造Admin上下文
func (this *RPCClient) Context(adminId int64) context.Context {
ctx := context.Background()
@@ -437,7 +535,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
}
// 初始化
@@ -515,3 +617,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

@@ -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

@@ -1,74 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package tasks
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/events"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeAdmin/internal/setup"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/logs"
"time"
)
func init() {
events.On(events.EventStart, func() {
task := NewAuthorityTask()
go task.Start()
})
}
type AuthorityTask struct {
}
func NewAuthorityTask() *AuthorityTask {
return &AuthorityTask{}
}
func (this *AuthorityTask) Start() {
ticker := time.NewTicker(10 * time.Minute)
if Tea.IsTesting() {
// 快速测试
ticker = time.NewTicker(1 * time.Minute)
}
// 初始化的时候先获取一次
timeout := time.NewTimer(3 * time.Second)
<-timeout.C
err := this.Loop()
if err != nil {
logs.Println("[TASK][AuthorityTask]" + err.Error())
}
// 定时获取
for range ticker.C {
err := this.Loop()
if err != nil {
logs.Println("[TASK][AuthorityTask]" + err.Error())
}
}
}
func (this *AuthorityTask) Loop() error {
// 如果还没有安装直接返回
if !setup.IsConfigured() {
return nil
}
rpcClient, err := rpc.SharedRPC()
if err != nil {
return err
}
resp, err := rpcClient.AuthorityKeyRPC().ReadAuthorityKey(rpcClient.Context(0), &pb.ReadAuthorityKeyRequest{})
if err != nil {
return err
}
if resp.AuthorityKey != nil {
teaconst.IsPlus = true
} else {
teaconst.IsPlus = false
}
return nil
}

View File

@@ -1,15 +1,22 @@
package tasks
import (
"context"
"crypto/tls"
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/events"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeAdmin/internal/setup"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/logs"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"net/url"
"sort"
"strings"
"sync"
"time"
)
@@ -20,7 +27,7 @@ func init() {
})
}
// API节点同步任务
// SyncAPINodesTask API节点同步任务
type SyncAPINodesTask struct {
}
@@ -44,7 +51,7 @@ func (this *SyncAPINodesTask) Start() {
func (this *SyncAPINodesTask) Loop() error {
// 如果还没有安装直接返回
if !setup.IsConfigured() {
if !setup.IsConfigured() || teaconst.IsRecoverMode {
return nil
}
@@ -59,7 +66,7 @@ func (this *SyncAPINodesTask) Loop() error {
}
newEndpoints := []string{}
for _, node := range resp.Nodes {
for _, node := range resp.ApiNodes {
if !node.IsOn {
continue
}
@@ -75,6 +82,12 @@ func (this *SyncAPINodesTask) Loop() error {
return nil
}
// 测试是否有API节点可用
hasOk := this.testEndpoints(newEndpoints)
if !hasOk {
return nil
}
// 修改RPC对象配置
config.RPC.Endpoints = newEndpoints
err = rpcClient.UpdateConfig(config)
@@ -96,3 +109,47 @@ func (this *SyncAPINodesTask) isSame(endpoints1 []string, endpoints2 []string) b
sort.Strings(endpoints2)
return strings.Join(endpoints1, "&") == strings.Join(endpoints2, "&")
}
func (this *SyncAPINodesTask) testEndpoints(endpoints []string) bool {
if len(endpoints) == 0 {
return false
}
var wg = sync.WaitGroup{}
wg.Add(len(endpoints))
var ok = false
for _, endpoint := range endpoints {
go func(endpoint string) {
defer wg.Done()
u, err := url.Parse(endpoint)
if err != nil {
return
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer func() {
cancel()
}()
var conn *grpc.ClientConn
if u.Scheme == "http" {
conn, err = grpc.DialContext(ctx, u.Host, grpc.WithInsecure(), grpc.WithBlock())
} else if u.Scheme == "https" {
conn, err = grpc.DialContext(ctx, u.Host, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
InsecureSkipVerify: true,
})), grpc.WithBlock())
}
if err != nil {
return
}
_ = conn.Close()
ok = true
}(endpoint)
}
wg.Wait()
return ok
}

View File

@@ -1,6 +1,7 @@
package tasks
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/events"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeAdmin/internal/setup"
@@ -40,7 +41,7 @@ func (this *SyncClusterTask) Start() {
func (this *SyncClusterTask) loop() error {
// 如果还没有安装直接返回
if !setup.IsConfigured() {
if !setup.IsConfigured() || teaconst.IsRecoverMode {
return nil
}

View File

@@ -1,10 +1,11 @@
package utils
import (
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/miekg/dns"
)
// 获取CNAME
// LookupCNAME 获取CNAME
func LookupCNAME(host string) (string, error) {
config, err := dns.ClientConfigFromFile("/etc/resolv.conf")
if err != nil {
@@ -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,15 @@
package actionutils
import (
"context"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/iwind/TeaGo/maps"
)
type ActionInterface interface {
RPC() *rpc.RPCClient
AdminContext() context.Context
ViewData() maps.Map
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
"net/http"
"strconv"
)
@@ -18,6 +19,8 @@ type ParentAction struct {
actions.ActionObject
rpcClient *rpc.RPCClient
ctx context.Context
}
// Parent 可以调用自身的一个简便方法
@@ -116,6 +119,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 {
@@ -124,5 +131,11 @@ 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 视图里可以使用的数据
func (this *ParentAction) ViewData() maps.Map {
return this.Data
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"regexp"
)
type CreatePopupAction struct {
@@ -29,6 +30,14 @@ func (this *CreatePopupAction) RunPost(params struct {
GroupIds string
Description string
TimeFromHour string
TimeFromMinute string
TimeFromSecond string
TimeToHour string
TimeToMinute string
TimeToSecond string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
@@ -40,12 +49,26 @@ func (this *CreatePopupAction) RunPost(params struct {
groupIds := utils.SplitNumbers(params.GroupIds)
var digitReg = regexp.MustCompile(`^\d+$`)
var timeFrom = ""
if digitReg.MatchString(params.TimeFromHour) && digitReg.MatchString(params.TimeFromMinute) && digitReg.MatchString(params.TimeFromSecond) {
timeFrom = params.TimeFromHour + ":" + params.TimeFromMinute + ":" + params.TimeFromSecond
}
var timeTo = ""
if digitReg.MatchString(params.TimeToHour) && digitReg.MatchString(params.TimeToMinute) && digitReg.MatchString(params.TimeToSecond) {
timeTo = params.TimeToHour + ":" + params.TimeToMinute + ":" + params.TimeToSecond
}
resp, err := this.RPC().MessageRecipientRPC().CreateMessageRecipient(this.AdminContext(), &pb.CreateMessageRecipientRequest{
AdminId: params.AdminId,
MessageMediaInstanceId: params.InstanceId,
User: params.User,
MessageRecipientGroupIds: groupIds,
Description: params.Description,
TimeFrom: timeFrom,
TimeTo: timeTo,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -39,6 +39,10 @@ func (this *IndexAction) RunGet(params struct {
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
recipientMaps := []maps.Map{}
for _, recipient := range recipientsResp.MessageRecipients {

View File

@@ -3,6 +3,7 @@ package instances
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/monitorconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
@@ -65,6 +66,11 @@ func (this *CreatePopupAction) RunPost(params struct {
TelegramToken string
RateMinutes int32
RateCount int32
HashLife int32
Description string
Must *actions.Must
@@ -231,11 +237,23 @@ func (this *CreatePopupAction) RunPost(params struct {
return
}
var rateConfig = &monitorconfigs.RateConfig{
Minutes: params.RateMinutes,
Count: params.RateCount,
}
rateJSON, err := json.Marshal(rateConfig)
if err != nil {
this.ErrorPage(err)
return
}
resp, err := this.RPC().MessageMediaInstanceRPC().CreateMessageMediaInstance(this.AdminContext(), &pb.CreateMessageMediaInstanceRequest{
Name: params.Name,
MediaType: params.MediaType,
ParamsJSON: optionsJSON,
Description: params.Description,
RateJSON: rateJSON,
HashLife: params.HashLife,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -35,6 +35,10 @@ func (this *IndexAction) RunGet(params struct {
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
instanceMaps := []maps.Map{}
for _, instance := range instancesResp.MessageMediaInstances {

View File

@@ -3,6 +3,7 @@ package instances
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/monitorconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
@@ -38,6 +39,16 @@ func (this *InstanceAction) RunGet(params struct {
}
}
// 频率
var rateConfig = &monitorconfigs.RateConfig{}
if len(instance.RateJSON) > 0 {
err = json.Unmarshal(instance.RateJSON, rateConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["instance"] = maps.Map{
"id": instance.Id,
"name": instance.Name,
@@ -48,6 +59,8 @@ func (this *InstanceAction) RunGet(params struct {
},
"description": instance.Description,
"params": mediaParams,
"rate": rateConfig,
"hashLife": instance.HashLife,
}
this.Show()

View File

@@ -3,6 +3,7 @@ package instances
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/monitorconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
@@ -39,6 +40,15 @@ func (this *UpdateAction) RunGet(params struct {
}
}
var rateConfig = &monitorconfigs.RateConfig{}
if len(instance.RateJSON) > 0 {
err = json.Unmarshal(instance.RateJSON, rateConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["instance"] = maps.Map{
"id": instance.Id,
"name": instance.Name,
@@ -49,6 +59,8 @@ func (this *UpdateAction) RunGet(params struct {
},
"description": instance.Description,
"params": mediaParams,
"rate": rateConfig,
"hashLife": instance.HashLife,
}
this.Show()
@@ -104,6 +116,11 @@ func (this *UpdateAction) RunPost(params struct {
Description string
IsOn bool
RateMinutes int32
RateCount int32
HashLife int32
Must *actions.Must
CSRF *actionutils.CSRF
}) {
@@ -270,12 +287,24 @@ func (this *UpdateAction) RunPost(params struct {
return
}
var rateConfig = &monitorconfigs.RateConfig{
Minutes: params.RateMinutes,
Count: params.RateCount,
}
rateJSON, err := json.Marshal(rateConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().MessageMediaInstanceRPC().UpdateMessageMediaInstance(this.AdminContext(), &pb.UpdateMessageMediaInstanceRequest{
MessageMediaInstanceId: params.InstanceId,
Name: params.Name,
MediaType: params.MediaType,
ParamsJSON: optionsJSON,
Description: params.Description,
RateJSON: rateJSON,
HashLife: params.HashLife,
IsOn: params.IsOn,
})
if err != nil {

View File

@@ -44,6 +44,8 @@ func (this *RecipientAction) RunGet(params struct {
},
"user": recipient.User,
"description": recipient.Description,
"timeFrom": recipient.TimeFrom,
"timeTo": recipient.TimeTo,
}
this.Show()

View File

@@ -0,0 +1,26 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package tasks
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
TaskId int64
}) {
defer this.CreateLogInfo("删除消息发送任务 %d", params.TaskId)
_, err := this.RPC().MessageTaskRPC().DeleteMessageTask(this.AdminContext(), &pb.DeleteMessageTaskRequest{MessageTaskId: params.TaskId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,103 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package tasks
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"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "task")
}
func (this *IndexAction) RunGet(params struct {
Status int32
}) {
this.Data["status"] = params.Status
if params.Status > 3 {
params.Status = 0
}
countWaitingResp, err := this.RPC().MessageTaskRPC().CountMessageTasksWithStatus(this.AdminContext(), &pb.CountMessageTasksWithStatusRequest{Status: pb.CountMessageTasksWithStatusRequest_MessageTaskStatusNone})
if err != nil {
this.ErrorPage(err)
return
}
var countWaiting = countWaitingResp.Count
this.Data["countWaiting"] = countWaiting
countFailedResp, err := this.RPC().MessageTaskRPC().CountMessageTasksWithStatus(this.AdminContext(), &pb.CountMessageTasksWithStatusRequest{Status: pb.CountMessageTasksWithStatusRequest_MessageTaskStatusFailed})
if err != nil {
this.ErrorPage(err)
return
}
var countFailed = countFailedResp.Count
this.Data["countFailed"] = countFailed
// 列表
var total = int64(0)
switch params.Status {
case 0:
total = countWaiting
case 3:
total = countFailed
}
page := this.NewPage(total)
this.Data["page"] = page.AsHTML()
var taskMaps = []maps.Map{}
tasksResp, err := this.RPC().MessageTaskRPC().ListMessageTasksWithStatus(this.AdminContext(), &pb.ListMessageTasksWithStatusRequest{
Status: pb.ListMessageTasksWithStatusRequest_Status(params.Status),
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
for _, task := range tasksResp.MessageTasks {
var resultMap = maps.Map{}
var result = task.Result
if result != nil {
resultMap = maps.Map{
"isOk": result.IsOk,
"error": result.Error,
"response": result.Response,
}
}
//var recipients = []string{}
var user = ""
var instanceMap maps.Map
if task.MessageRecipient != nil {
user = task.MessageRecipient.User
if task.MessageRecipient.MessageMediaInstance != nil {
instanceMap = maps.Map{
"id": task.MessageRecipient.MessageMediaInstance.Id,
"name": task.MessageRecipient.MessageMediaInstance.Name,
}
}
}
taskMaps = append(taskMaps, maps.Map{
"id": task.Id,
"subject": task.Subject,
"body": task.Body,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", task.CreatedAt),
"result": resultMap,
"status": task.Status,
"user": user,
"instance": instanceMap,
})
}
this.Data["tasks"] = taskMaps
this.Show()
}

View File

@@ -13,7 +13,9 @@ func init() {
Data("teaMenu", "admins").
Data("teaSubMenu", "recipients").
Prefix("/admins/recipients/tasks").
Get("", new(IndexAction)).
Post("/taskInfo", new(TaskInfoAction)).
Post("/delete", new(DeleteAction)).
EndAll()
})
}

View File

@@ -6,6 +6,8 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"regexp"
"strings"
)
type UpdateAction struct {
@@ -30,6 +32,27 @@ func (this *UpdateAction) RunGet(params struct {
return
}
var timeFromHour = ""
var timeFromMinute = ""
var timeFromSecond = ""
if len(recipient.TimeFrom) > 0 {
var pieces = strings.Split(recipient.TimeFrom, ":")
timeFromHour = pieces[0]
timeFromMinute = pieces[1]
timeFromSecond = pieces[2]
}
var timeToHour = ""
var timeToMinute = ""
var timeToSecond = ""
if len(recipient.TimeTo) > 0 {
var pieces = strings.Split(recipient.TimeTo, ":")
timeToHour = pieces[0]
timeToMinute = pieces[1]
timeToSecond = pieces[2]
}
this.Data["recipient"] = maps.Map{
"id": recipient.Id,
"admin": maps.Map{
@@ -43,8 +66,14 @@ func (this *UpdateAction) RunGet(params struct {
"id": recipient.MessageMediaInstance.Id,
"name": recipient.MessageMediaInstance.Name,
},
"user": recipient.User,
"description": recipient.Description,
"user": recipient.User,
"description": recipient.Description,
"timeFromHour": timeFromHour,
"timeFromMinute": timeFromMinute,
"timeFromSecond": timeFromSecond,
"timeToHour": timeToHour,
"timeToMinute": timeToMinute,
"timeToSecond": timeToSecond,
}
this.Show()
@@ -61,6 +90,14 @@ func (this *UpdateAction) RunPost(params struct {
Description string
IsOn bool
TimeFromHour string
TimeFromMinute string
TimeFromSecond string
TimeToHour string
TimeToMinute string
TimeToSecond string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
@@ -74,6 +111,18 @@ func (this *UpdateAction) RunPost(params struct {
groupIds := utils.SplitNumbers(params.GroupIds)
var digitReg = regexp.MustCompile(`^\d+$`)
var timeFrom = ""
if digitReg.MatchString(params.TimeFromHour) && digitReg.MatchString(params.TimeFromMinute) && digitReg.MatchString(params.TimeFromSecond) {
timeFrom = params.TimeFromHour + ":" + params.TimeFromMinute + ":" + params.TimeFromSecond
}
var timeTo = ""
if digitReg.MatchString(params.TimeToHour) && digitReg.MatchString(params.TimeToMinute) && digitReg.MatchString(params.TimeToSecond) {
timeTo = params.TimeToHour + ":" + params.TimeToMinute + ":" + params.TimeToSecond
}
_, err := this.RPC().MessageRecipientRPC().UpdateMessageRecipient(this.AdminContext(), &pb.UpdateMessageRecipientRequest{
MessageRecipientId: params.RecipientId,
AdminId: params.AdminId,
@@ -82,6 +131,8 @@ func (this *UpdateAction) RunPost(params struct {
MessageRecipientGroupIds: groupIds,
Description: params.Description,
IsOn: params.IsOn,
TimeFrom: timeFrom,
TimeTo: timeTo,
})
if err != nil {
this.ErrorPage(err)

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

@@ -41,7 +41,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 {

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,18 +20,19 @@ 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
}
// 监听地址
var hasHTTPS = false
httpConfig := &serverconfigs.HTTPProtocolConfig{}
if len(node.HttpJSON) > 0 {
err = json.Unmarshal(node.HttpJSON, httpConfig)
@@ -47,6 +48,7 @@ func (this *IndexAction) RunGet(params struct {
this.ErrorPage(err)
return
}
hasHTTPS = len(httpsConfig.Listen) > 0
}
// 监听地址
@@ -56,7 +58,6 @@ func (this *IndexAction) RunGet(params struct {
// 证书信息
certs := []*sslconfigs.SSLCertConfig{}
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 {
@@ -65,8 +66,6 @@ func (this *IndexAction) RunGet(params struct {
}
sslPolicyConfigJSON := sslPolicyConfigResp.SslPolicyJSON
if len(sslPolicyConfigJSON) > 0 {
sslPolicyId = httpsConfig.SSLPolicyRef.SSLPolicyId
sslPolicy := &sslconfigs.SSLPolicy{}
err = json.Unmarshal(sslPolicyConfigJSON, sslPolicy)
if err != nil {
@@ -112,6 +111,10 @@ func (this *IndexAction) RunGet(params struct {
if httpsConfig.IsOn && len(httpsConfig.Listen) > 0 {
restAccessAddrs = append(restAccessAddrs, httpsConfig.Listen...)
}
if !hasHTTPS {
hasHTTPS = len(httpsConfig.Listen) > 0
}
}
}
@@ -124,7 +127,7 @@ func (this *IndexAction) RunGet(params struct {
"accessAddrs": accessAddrs,
"restIsOn": node.RestIsOn,
"restAccessAddrs": restAccessAddrs,
"hasHTTPS": sslPolicyId > 0,
"hasHTTPS": hasHTTPS,
"certs": certs,
}

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,13 +23,13 @@ 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
node := nodeResp.ApiNode
if node == nil {
this.WriteString("要操作的节点不存在")
return
@@ -127,7 +127,7 @@ func (this *UpdateAction) RunGet(params struct {
this.Show()
}
// 保存基础设置
// RunPost 保存基础设置
func (this *UpdateAction) RunPost(params struct {
NodeId int64
Name string
@@ -281,7 +281,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,

View File

@@ -95,6 +95,7 @@ func (this *CreateBatchAction) RunPost(params struct {
Name: "IP地址",
Ip: ip,
CanAccess: true,
IsUp: true,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -4,14 +4,16 @@ import (
"encoding/json"
"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"
"strconv"
"strings"
)
// 创建节点
// CreateNodeAction 创建节点
type CreateNodeAction struct {
actionutils.ParentAction
}
@@ -57,14 +59,32 @@ func (this *CreateNodeAction) RunGet(params struct {
}
for _, route := range routesResp.Routes {
dnsRouteMaps = append(dnsRouteMaps, maps.Map{
"name": route.Name,
"code": route.Code,
"domainId": domainId,
"domainName": clusterDNSResp.Domain.Name,
"name": route.Name,
"code": route.Code,
})
}
}
}
this.Data["dnsRoutes"] = dnsRouteMaps
// API节点列表
apiNodesResp, err := this.RPC().APINodeRPC().FindAllEnabledAPINodes(this.AdminContext(), &pb.FindAllEnabledAPINodesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
apiNodes := apiNodesResp.ApiNodes
apiEndpoints := []string{}
for _, apiNode := range apiNodes {
if !apiNode.IsOn {
continue
}
apiEndpoints = append(apiEndpoints, apiNode.AccessAddrs...)
}
this.Data["apiEndpoints"] = "\"" + strings.Join(apiEndpoints, "\", \"") + "\""
this.Show()
}
@@ -147,30 +167,106 @@ func (this *CreateNodeAction) RunPost(params struct {
nodeId := createResp.NodeId
// IP地址
for _, address := range ipAddresses {
addressId := address.GetInt64("id")
if addressId > 0 {
for _, addr := range ipAddresses {
addrId := addr.GetInt64("id")
if addrId > 0 {
_, err = this.RPC().NodeIPAddressRPC().UpdateNodeIPAddressNodeId(this.AdminContext(), &pb.UpdateNodeIPAddressNodeIdRequest{
AddressId: addressId,
NodeId: nodeId,
NodeIPAddressId: addrId,
NodeId: nodeId,
})
if err != nil {
this.ErrorPage(err)
return
}
} else {
_, err = this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{
createResp, err := this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{
NodeId: nodeId,
Role: nodeconfigs.NodeRoleNode,
Name: address.GetString("name"),
Ip: address.GetString("ip"),
CanAccess: address.GetBool("canAccess"),
Name: addr.GetString("name"),
Ip: addr.GetString("ip"),
CanAccess: addr.GetBool("canAccess"),
IsUp: addr.GetBool("isUp"),
})
if err != nil {
this.ErrorPage(err)
return
}
addrId = createResp.NodeIPAddressId
}
if err != nil {
this.ErrorPage(err)
return
// 阈值
var thresholds = addr.GetSlice("thresholds")
if len(thresholds) > 0 {
thresholdsJSON, err := json.Marshal(thresholds)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().NodeIPAddressThresholdRPC().UpdateAllNodeIPAddressThresholds(this.AdminContext(), &pb.UpdateAllNodeIPAddressThresholdsRequest{
NodeIPAddressId: addrId,
NodeIPAddressThresholdsJSON: thresholdsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
}
}
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "创建节点 %d", nodeId)
// 响应数据
this.Data["nodeId"] = nodeId
nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: nodeId})
if err != nil {
this.ErrorPage(err)
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 {
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: grantId})
if err != nil {
this.ErrorPage(err)
return
}
if grantResp.NodeGrant != nil && grantResp.NodeGrant.Id > 0 {
grantMap = maps.Map{
"id": grantResp.NodeGrant.Id,
"name": grantResp.NodeGrant.Name,
"method": grantResp.NodeGrant.Method,
"methodName": grantutils.FindGrantMethodName(grantResp.NodeGrant.Method),
"username": grantResp.NodeGrant.Username,
}
}
}
this.Data["node"] = maps.Map{
"id": nodeResp.Node.Id,
"name": nodeResp.Node.Name,
"uniqueId": nodeResp.Node.UniqueId,
"secret": nodeResp.Node.Secret,
"addresses": addresses,
"login": maps.Map{
"id": 0,
"name": "SSH",
"type": "ssh",
"params": maps.Map{
"grantId": params.GrantId,
"host": params.SshHost,
"port": params.SshPort,
},
},
"grant": grantMap,
}
}
this.Success()
}

View File

@@ -0,0 +1,76 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package cluster
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type CreateNodeInstallAction struct {
actionutils.ParentAction
}
func (this *CreateNodeInstallAction) RunPost(params struct {
NodeId int64
SshHost string
SshPort int
GrantId int64
Must *actions.Must
}) {
defer this.CreateLogInfo("安装节点 %d", params.NodeId)
params.Must.
Field("sshHost2", params.SshHost).
Require("请填写SSH主机地址").
Field("sshPort2", params.SshPort).
Gt(0, "请填写SSH主机端口").
Lt(65535, "SSH主机端口需要小于65535").
Field("grantId", params.GrantId).
Gt(0, "请选择SSH登录认证")
// 查询login
nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
var node = nodeResp.Node
if node == nil {
this.Fail("找不到要修改的节点")
}
var loginId int64
if node.NodeLogin != nil {
loginId = node.NodeLogin.Id
}
// 修改节点信息
_, err = this.RPC().NodeRPC().UpdateNodeLogin(this.AdminContext(), &pb.UpdateNodeLoginRequest{
NodeId: params.NodeId,
NodeLogin: &pb.NodeLogin{
Id: loginId,
Name: "SSH",
Type: "ssh",
Params: maps.Map{
"grantId": params.GrantId,
"host": params.SshHost,
"port": params.SshPort,
}.AsJSON(),
},
})
if err != nil {
this.ErrorPage(err)
return
}
// 开始安装
_, err = this.RPC().NodeRPC().InstallNode(this.AdminContext(), &pb.InstallNodeRequest{NodeId: params.NodeId})
if err != nil {
this.Fail("安装失败:" + err.Error())
}
this.Success()
}

View File

@@ -1,16 +1,11 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package cluster
import (
"encoding/json"
"fmt"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"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 IndexAction struct {
@@ -18,207 +13,15 @@ type IndexAction struct {
}
func (this *IndexAction) Init() {
this.Nav("", "node", "index")
this.SecondMenu("nodes")
this.Nav("", "", "")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
GroupId int64
RegionId int64
InstalledState int
ActiveState int
Keyword string
ClusterId int64
}) {
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
countAllResp, err := this.RPC().NodeRPC().CountAllEnabledNodesMatch(this.AdminContext(), &pb.CountAllEnabledNodesMatchRequest{
NodeClusterId: params.ClusterId,
})
if err != nil {
this.ErrorPage(err)
return
if teaconst.IsPlus {
this.RedirectURL("/clusters/cluster/boards?clusterId=" + strconv.FormatInt(params.ClusterId, 10))
} else {
this.RedirectURL("/clusters/cluster/nodes?clusterId=" + strconv.FormatInt(params.ClusterId, 10))
}
this.Data["countAll"] = countAllResp.Count
countResp, err := this.RPC().NodeRPC().CountAllEnabledNodesMatch(this.AdminContext(), &pb.CountAllEnabledNodesMatchRequest{
NodeClusterId: params.ClusterId,
NodeGroupId: params.GroupId,
NodeRegionId: params.RegionId,
InstallState: types.Int32(params.InstalledState),
ActiveState: types.Int32(params.ActiveState),
Keyword: params.Keyword,
})
if err != nil {
this.ErrorPage(err)
return
}
page := this.NewPage(countResp.Count)
this.Data["page"] = page.AsHTML()
nodesResp, err := this.RPC().NodeRPC().ListEnabledNodesMatch(this.AdminContext(), &pb.ListEnabledNodesMatchRequest{
Offset: page.Offset,
Size: page.Size,
NodeClusterId: params.ClusterId,
NodeGroupId: params.GroupId,
NodeRegionId: params.RegionId,
InstallState: types.Int32(params.InstalledState),
ActiveState: types.Int32(params.ActiveState),
Keyword: params.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 {
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().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{
NodeId: node.Id,
Role: nodeconfigs.NodeRoleNode,
})
if err != nil {
this.ErrorPage(err)
return
}
ipAddresses := []maps.Map{}
for _, addr := range ipAddressesResp.Addresses {
ipAddresses = append(ipAddresses, maps.Map{
"id": addr.Id,
"name": addr.Name,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
})
}
// 分组
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)
}
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,
},
"isSynced": isSynced,
"ipAddresses": ipAddresses,
"group": groupMap,
"region": regionMap,
"dnsRouteNames": dnsRouteNames,
})
}
this.Data["nodes"] = nodeMaps
// 所有分组
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 {
countResp, err := this.RPC().NodeRPC().CountAllEnabledNodesWithNodeGroupId(this.AdminContext(), &pb.CountAllEnabledNodesWithNodeGroupIdRequest{NodeGroupId: group.Id})
if err != nil {
this.ErrorPage(err)
return
}
countNodes := countResp.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
}
regionMaps := []maps.Map{}
for _, region := range regionsResp.NodeRegions {
regionMaps = append(regionMaps, maps.Map{
"id": region.Id,
"name": region.Name,
})
}
this.Data["regions"] = regionMaps
// 记录最近访问
_, err = this.RPC().LatestItemRPC().IncreaseLatestItem(this.AdminContext(), &pb.IncreaseLatestItemRequest{
ItemType: "cluster",
ItemId: params.ClusterId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Show()
}

View File

@@ -4,8 +4,11 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/groups"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/monitor"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/thresholds"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/settings/cache"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/settings/dns"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/settings/ssh"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/settings/system"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/settings/thresholds"
clusters "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/clusterutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
@@ -16,8 +19,10 @@ func init() {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
Helper(clusters.NewClusterHelper()).
Data("teaMenu", "clusters").
Prefix("/clusters/cluster").
Get("", new(IndexAction)).
Get("/nodes", new(NodesAction)).
GetPost("/installNodes", new(InstallNodesAction)).
GetPost("/installRemote", new(InstallRemoteAction)).
Post("/installStatus", new(InstallStatusAction)).
@@ -25,37 +30,40 @@ func init() {
Post("/upgradeStatus", new(UpgradeStatusAction)).
GetPost("/delete", new(DeleteAction)).
GetPost("/createNode", new(CreateNodeAction)).
Post("/createNodeInstall", new(CreateNodeInstallAction)).
GetPost("/createBatch", new(CreateBatchAction)).
GetPost("/updateNodeSSH", new(UpdateNodeSSHAction)).
GetPost("/installManual", new(InstallManualAction)).
Post("/suggestLoginPorts", new(SuggestLoginPortsAction)).
// 节点相关
Get("/node", new(node.IndexAction)).
GetPost("/node/update", new(node.UpdateAction)).
GetPost("/node/install", new(node.InstallAction)).
Post("/node/updateInstallStatus", new(node.UpdateInstallStatusAction)).
Post("/node/status", new(node.StatusAction)).
Get("/node/logs", new(node.LogsAction)).
Post("/node/start", new(node.StartAction)).
Post("/node/stop", new(node.StopAction)).
Post("/node/up", new(node.UpAction)).
Get("/node/monitor", new(monitor.IndexAction)).
Post("/node/monitor/cpu", new(monitor.CpuAction)).
Post("/node/monitor/memory", new(monitor.MemoryAction)).
Post("/node/monitor/load", new(monitor.LoadAction)).
Post("/node/monitor/trafficIn", new(monitor.TrafficInAction)).
Post("/node/monitor/trafficOut", new(monitor.TrafficOutAction)).
Post("/node/monitor/connections", new(monitor.ConnectionsAction)).
Get("/node/thresholds", new(thresholds.IndexAction)).
Prefix("/clusters/cluster/node").
Get("", new(node.IndexAction)).
GetPost("/update", new(node.UpdateAction)).
GetPost("/install", new(node.InstallAction)).
Post("/updateInstallStatus", new(node.UpdateInstallStatusAction)).
Post("/status", new(node.StatusAction)).
Get("/logs", new(node.LogsAction)).
Post("/start", new(node.StartAction)).
Post("/stop", new(node.StopAction)).
Post("/up", new(node.UpAction)).
Get("/detail", new(node.DetailAction)).
GetPost("/updateDNSPopup", new(node.UpdateDNSPopupAction)).
Post("/syncDomain", new(node.SyncDomainAction)).
GetPost("/settings/cache", new(cache.IndexAction)).
GetPost("/settings/dns", new(dns.IndexAction)).
GetPost("/settings/system", new(system.IndexAction)).
GetPost("/settings/ssh", new(ssh.IndexAction)).
GetPost("/settings/thresholds", new(thresholds.IndexAction)).
// 分组相关
Get("/groups", new(groups.IndexAction)).
GetPost("/groups/createPopup", new(groups.CreatePopupAction)).
GetPost("/groups/updatePopup", new(groups.UpdatePopupAction)).
Post("/groups/delete", new(groups.DeleteAction)).
Post("/groups/sort", new(groups.SortAction)).
GetPost("/groups/selectPopup", new(groups.SelectPopupAction)).
Prefix("/clusters/cluster/groups").
Get("", new(groups.IndexAction)).
GetPost("/createPopup", new(groups.CreatePopupAction)).
GetPost("/updatePopup", new(groups.UpdatePopupAction)).
Post("/delete", new(groups.DeleteAction)).
Post("/sort", new(groups.SortAction)).
GetPost("/selectPopup", new(groups.SelectPopupAction)).
EndAll()
})
}

View File

@@ -30,8 +30,8 @@ func (this *InstallManualAction) RunGet(params struct {
nodeMaps := []maps.Map{}
for _, node := range nodesResp.Nodes {
loginParams := maps.Map{}
if node.Login != nil && len(node.Login.Params) > 0 {
err := json.Unmarshal(node.Login.Params, &loginParams)
if node.NodeLogin != nil && len(node.NodeLogin.Params) > 0 {
err := json.Unmarshal(node.NodeLogin.Params, &loginParams)
if err != nil {
this.ErrorPage(err)
return
@@ -56,7 +56,7 @@ func (this *InstallManualAction) RunGet(params struct {
"isOn": node.IsOn,
"name": node.Name,
"addresses": node.IpAddresses,
"login": node.Login,
"login": node.NodeLogin,
"loginParams": loginParams,
"installStatus": installStatus,
})

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

@@ -32,8 +32,8 @@ func (this *InstallRemoteAction) RunGet(params struct {
nodeMaps := []maps.Map{}
for _, node := range nodesResp.Nodes {
loginParams := maps.Map{}
if node.Login != nil && len(node.Login.Params) > 0 {
err := json.Unmarshal(node.Login.Params, &loginParams)
if node.NodeLogin != nil && len(node.NodeLogin.Params) > 0 {
err := json.Unmarshal(node.NodeLogin.Params, &loginParams)
if err != nil {
this.ErrorPage(err)
return
@@ -58,7 +58,7 @@ func (this *InstallRemoteAction) RunGet(params struct {
"isOn": node.IsOn,
"name": node.Name,
"addresses": node.IpAddresses,
"login": node.Login,
"login": node.NodeLogin,
"loginParams": loginParams,
"installStatus": installStatus,
})

View File

@@ -0,0 +1,309 @@
package node
import (
"encoding/json"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/ipAddresses/ipaddressutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
"time"
)
type DetailAction struct {
actionutils.ParentAction
}
func (this *DetailAction) Init() {
this.Nav("", "node", "node")
this.SecondMenu("nodes")
}
func (this *DetailAction) RunGet(params struct {
NodeId int64
}) {
this.Data["nodeId"] = params.NodeId
nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
node := nodeResp.Node
if node == nil {
this.WriteString("找不到要操作的节点")
return
}
// 主集群
var clusterMap maps.Map = nil
if node.NodeCluster != nil {
clusterId := node.NodeCluster.Id
clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: clusterId})
if err != nil {
this.ErrorPage(err)
return
}
cluster := clusterResp.NodeCluster
if cluster != nil {
clusterMap = maps.Map{
"id": cluster.Id,
"name": cluster.Name,
"installDir": cluster.InstallDir,
}
}
}
// 从集群
var secondaryClustersMaps = []maps.Map{}
for _, cluster := range node.SecondaryNodeClusters {
secondaryClustersMaps = append(secondaryClustersMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
"isOn": cluster.IsOn,
})
}
// IP地址
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledNodeIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledNodeIPAddressesWithNodeIdRequest{
NodeId: params.NodeId,
Role: nodeconfigs.NodeRoleNode,
})
if err != nil {
this.ErrorPage(err)
return
}
var ipAddresses = ipAddressesResp.NodeIPAddresses
ipAddressMaps := []maps.Map{}
for _, addr := range ipAddressesResp.NodeIPAddresses {
thresholds, err := ipaddressutils.InitNodeIPAddressThresholds(this.Parent(), addr.Id)
if err != nil {
this.ErrorPage(err)
return
}
ipAddressMaps = append(ipAddressMaps, maps.Map{
"id": addr.Id,
"name": addr.Name,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
"isOn": addr.IsOn,
"isUp": addr.IsUp,
"thresholds": thresholds,
})
}
// DNS相关
var clusters = []*pb.NodeCluster{node.NodeCluster}
clusters = append(clusters, node.SecondaryNodeClusters...)
var recordMaps = []maps.Map{}
var routeMaps = []maps.Map{}
for _, cluster := range clusters {
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{
NodeId: params.NodeId,
NodeClusterId: cluster.Id,
})
if err != nil {
this.ErrorPage(err)
return
}
var dnsInfo = dnsInfoResp.Node
if len(dnsInfo.DnsDomainName) == 0 || len(dnsInfo.NodeClusterDNSName) == 0 {
continue
}
var domainName = dnsInfo.DnsDomainName
// 默认线路
if len(dnsInfo.Routes) == 0 {
dnsInfo.Routes = append(dnsInfo.Routes, &pb.DNSRoute{})
} else {
for _, route := range dnsInfo.Routes {
routeMaps = append(routeMaps, maps.Map{
"domainName": domainName,
"code": route.Code,
"name": route.Name,
})
}
}
for _, addr := range ipAddresses {
if !addr.CanAccess || !addr.IsUp || !addr.IsOn {
continue
}
for _, route := range dnsInfo.Routes {
var recordType = "A"
if utils.IsIPv6(addr.Ip) {
recordType = "AAAA"
}
recordMaps = append(recordMaps, maps.Map{
"name": dnsInfo.NodeClusterDNSName + "." + domainName,
"type": recordType,
"route": route.Name,
"value": addr.Ip,
})
}
}
}
// 登录信息
var loginMap maps.Map = nil
if node.NodeLogin != nil {
loginParams := maps.Map{}
if len(node.NodeLogin.Params) > 0 {
err = json.Unmarshal(node.NodeLogin.Params, &loginParams)
if err != nil {
this.ErrorPage(err)
return
}
}
grantMap := maps.Map{}
grantId := loginParams.GetInt64("grantId")
if grantId > 0 {
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: grantId})
if err != nil {
this.ErrorPage(err)
return
}
if grantResp.NodeGrant != nil {
grantMap = maps.Map{
"id": grantResp.NodeGrant.Id,
"name": grantResp.NodeGrant.Name,
"method": grantResp.NodeGrant.Method,
"methodName": grantutils.FindGrantMethodName(grantResp.NodeGrant.Method),
"username": grantResp.NodeGrant.Username,
}
}
}
loginMap = maps.Map{
"id": node.NodeLogin.Id,
"name": node.NodeLogin.Name,
"type": node.NodeLogin.Type,
"params": loginParams,
"grant": grantMap,
}
}
// 运行状态
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秒之内认为活跃
}
// 检查是否有新版本
if len(status.OS) > 0 {
checkVersionResp, err := this.RPC().NodeRPC().CheckNodeLatestVersion(this.AdminContext(), &pb.CheckNodeLatestVersionRequest{
Os: status.OS,
Arch: status.Arch,
CurrentVersion: status.BuildVersion,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["shouldUpgrade"] = checkVersionResp.HasNewVersion
this.Data["newVersion"] = checkVersionResp.NewVersion
} else {
this.Data["shouldUpgrade"] = false
this.Data["newVersion"] = ""
}
// 分组
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,
}
}
// 缓存硬盘 & 内存容量
var maxCacheDiskCapacity maps.Map = nil
if node.MaxCacheDiskCapacity != nil {
maxCacheDiskCapacity = maps.Map{
"count": node.MaxCacheDiskCapacity.Count,
"unit": node.MaxCacheDiskCapacity.Unit,
}
} else {
maxCacheDiskCapacity = maps.Map{
"count": 0,
"unit": "gb",
}
}
var maxCacheMemoryCapacity maps.Map = nil
if node.MaxCacheMemoryCapacity != nil {
maxCacheMemoryCapacity = maps.Map{
"count": node.MaxCacheMemoryCapacity.Count,
"unit": node.MaxCacheMemoryCapacity.Unit,
}
} else {
maxCacheMemoryCapacity = maps.Map{
"count": 0,
"unit": "gb",
}
}
this.Data["node"] = maps.Map{
"id": node.Id,
"name": node.Name,
"ipAddresses": ipAddressMaps,
"cluster": clusterMap,
"secondaryClusters": secondaryClustersMaps,
"login": loginMap,
"installDir": node.InstallDir,
"isInstalled": node.IsInstalled,
"uniqueId": node.UniqueId,
"secret": node.Secret,
"maxCPU": node.MaxCPU,
"isOn": node.IsOn,
"records": recordMaps,
"routes": routeMaps,
"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),
"connectionCount": status.ConnectionCount,
"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),
"cacheTotalDiskSize": numberutils.FormatBytes(status.CacheTotalDiskSize),
"cacheTotalMemorySize": numberutils.FormatBytes(status.CacheTotalMemorySize),
},
"group": groupMap,
"region": regionMap,
"maxCacheDiskCapacity": maxCacheDiskCapacity,
"maxCacheMemoryCapacity": maxCacheMemoryCapacity,
}
this.Show()
}

View File

@@ -1,15 +1,11 @@
package node
import (
"encoding/json"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"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/maps"
"time"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
"strconv"
)
type IndexAction struct {
@@ -24,236 +20,15 @@ func (this *IndexAction) Init() {
func (this *IndexAction) RunGet(params struct {
NodeId int64
}) {
this.Data["nodeId"] = params.NodeId
nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId})
_, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
node := nodeResp.Node
if node == nil {
this.WriteString("找不到要操作的节点")
return
}
var clusterMap maps.Map = nil
if node.NodeCluster != nil {
clusterId := node.NodeCluster.Id
clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: clusterId})
if err != nil {
this.ErrorPage(err)
return
}
cluster := clusterResp.NodeCluster
if cluster != nil {
clusterMap = maps.Map{
"id": cluster.Id,
"name": cluster.Name,
"installDir": cluster.InstallDir,
}
}
}
// IP地址
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{
NodeId: params.NodeId,
Role: nodeconfigs.NodeRoleNode,
})
if err != nil {
this.ErrorPage(err)
return
}
ipAddressMaps := []maps.Map{}
for _, addr := range ipAddressesResp.Addresses {
ipAddressMaps = append(ipAddressMaps, maps.Map{
"id": addr.Id,
"name": addr.Name,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
})
}
// DNS相关
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
dnsRouteMaps := []maps.Map{}
recordName := ""
recordValue := ""
if dnsInfoResp.Node != nil {
recordName = dnsInfoResp.Node.NodeClusterDNSName + "." + dnsInfoResp.Node.DnsDomainName
recordValue = dnsInfoResp.Node.IpAddr
for _, dnsInfo := range dnsInfoResp.Node.Routes {
dnsRouteMaps = append(dnsRouteMaps, maps.Map{
"name": dnsInfo.Name,
"code": dnsInfo.Code,
})
}
}
if len(dnsRouteMaps) == 0 {
dnsRouteMaps = append(dnsRouteMaps, maps.Map{
"name": "",
"code": "",
})
}
this.Data["dnsRoutes"] = dnsRouteMaps
this.Data["dnsRecordName"] = recordName
this.Data["dnsRecordValue"] = recordValue
// 登录信息
var loginMap maps.Map = nil
if node.Login != nil {
loginParams := maps.Map{}
if len(node.Login.Params) > 0 {
err = json.Unmarshal(node.Login.Params, &loginParams)
if err != nil {
this.ErrorPage(err)
return
}
}
grantMap := maps.Map{}
grantId := loginParams.GetInt64("grantId")
if grantId > 0 {
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: grantId})
if err != nil {
this.ErrorPage(err)
return
}
if grantResp.NodeGrant != nil {
grantMap = maps.Map{
"id": grantResp.NodeGrant.Id,
"name": grantResp.NodeGrant.Name,
"method": grantResp.NodeGrant.Method,
"methodName": grantutils.FindGrantMethodName(grantResp.NodeGrant.Method),
}
}
}
loginMap = maps.Map{
"id": node.Login.Id,
"name": node.Login.Name,
"type": node.Login.Type,
"params": loginParams,
"grant": grantMap,
}
}
// 运行状态
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秒之内认为活跃
}
// 检查是否有新版本
if len(status.OS) > 0 {
checkVersionResp, err := this.RPC().NodeRPC().CheckNodeLatestVersion(this.AdminContext(), &pb.CheckNodeLatestVersionRequest{
Os: status.OS,
Arch: status.Arch,
CurrentVersion: status.BuildVersion,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["shouldUpgrade"] = checkVersionResp.HasNewVersion
this.Data["newVersion"] = checkVersionResp.NewVersion
if teaconst.IsPlus {
this.RedirectURL("/clusters/cluster/node/boards?clusterId=" + fmt.Sprintf("%d", this.Data["clusterId"]) + "&nodeId=" + strconv.FormatInt(params.NodeId, 10))
} else {
this.Data["shouldUpgrade"] = false
this.Data["newVersion"] = ""
this.RedirectURL("/clusters/cluster/node/detail?clusterId=" + fmt.Sprintf("%d", this.Data["clusterId"]) + "&nodeId=" + strconv.FormatInt(params.NodeId, 10))
}
// 分组
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,
}
}
// 缓存硬盘 & 内存容量
var maxCacheDiskCapacity maps.Map = nil
if node.MaxCacheDiskCapacity != nil {
maxCacheDiskCapacity = maps.Map{
"count": node.MaxCacheDiskCapacity.Count,
"unit": node.MaxCacheDiskCapacity.Unit,
}
} else {
maxCacheDiskCapacity = maps.Map{
"count": 0,
"unit": "gb",
}
}
var maxCacheMemoryCapacity maps.Map = nil
if node.MaxCacheMemoryCapacity != nil {
maxCacheMemoryCapacity = maps.Map{
"count": node.MaxCacheMemoryCapacity.Count,
"unit": node.MaxCacheMemoryCapacity.Unit,
}
} else {
maxCacheMemoryCapacity = maps.Map{
"count": 0,
"unit": "gb",
}
}
this.Data["node"] = maps.Map{
"id": node.Id,
"name": node.Name,
"ipAddresses": ipAddressMaps,
"cluster": clusterMap,
"login": loginMap,
"installDir": node.InstallDir,
"isInstalled": node.IsInstalled,
"uniqueId": node.UniqueId,
"secret": node.Secret,
"maxCPU": node.MaxCPU,
"isOn": node.IsOn,
"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),
"connectionCount": status.ConnectionCount,
"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),
"cacheTotalDiskSize": numberutils.FormatBytes(status.CacheTotalDiskSize),
"cacheTotalMemorySize": numberutils.FormatBytes(status.CacheTotalMemorySize),
},
"group": groupMap,
"region": regionMap,
"maxCacheDiskCapacity": maxCacheDiskCapacity,
"maxCacheMemoryCapacity": maxCacheMemoryCapacity,
}
this.Show()
}

View File

@@ -3,13 +3,14 @@ package node
import (
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"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/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"strings"
)
// 安装节点
// InstallAction 安装节点
type InstallAction struct {
actionutils.ParentAction
}
@@ -25,16 +26,11 @@ func (this *InstallAction) RunGet(params struct {
this.Data["nodeId"] = params.NodeId
// 节点
nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId})
node, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
node := nodeResp.Node
if node == nil {
this.WriteString("找不到要操作的节点")
return
}
// 安装信息
if node.InstallStatus != nil {
@@ -74,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 {
@@ -84,20 +80,17 @@ func (this *InstallAction) RunGet(params struct {
}
this.Data["apiEndpoints"] = "\"" + strings.Join(apiEndpoints, "\", \"") + "\""
this.Data["node"] = maps.Map{
"id": node.Id,
"name": node.Name,
"installDir": node.InstallDir,
"isInstalled": node.IsInstalled,
"uniqueId": node.UniqueId,
"secret": node.Secret,
"cluster": clusterMap,
}
var nodeMap = this.Data["node"].(maps.Map)
nodeMap["installDir"] = node.InstallDir
nodeMap["isInstalled"] = node.IsInstalled
nodeMap["uniqueId"] = node.UniqueId
nodeMap["secret"] = node.Secret
nodeMap["cluster"] = clusterMap
this.Show()
}
// 开始安装
// RunPost 开始安装
func (this *InstallAction) RunPost(params struct {
NodeId int64

View File

@@ -2,6 +2,7 @@ package node
import (
"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/rpc/pb"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
@@ -23,6 +24,13 @@ func (this *LogsAction) RunGet(params struct {
Keyword string
Level string
}) {
// 初始化节点信息(用于菜单)
_, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["nodeId"] = params.NodeId
this.Data["dayFrom"] = params.DayFrom
this.Data["dayTo"] = params.DayTo
@@ -54,9 +62,27 @@ func (this *LogsAction) RunGet(params struct {
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
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,
@@ -64,6 +90,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

@@ -1,73 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package monitor
import (
"encoding/json"
"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/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type ConnectionsAction struct {
actionutils.ParentAction
}
func (this *ConnectionsAction) RunPost(params struct {
NodeId int64
}) {
resp, err := this.RPC().NodeValueRPC().ListNodeValues(this.AdminContext(), &pb.ListNodeValuesRequest{
Role: "node",
NodeId: params.NodeId,
Item: nodeconfigs.NodeValueItemConnections,
Range: nodeconfigs.NodeValueRangeMinute,
})
if err != nil {
this.ErrorPage(err)
return
}
valuesMap := map[string]int64{} // YmdHi => count
for _, v := range resp.NodeValues {
if len(v.ValueJSON) == 0 {
continue
}
valueMap := maps.Map{}
err = json.Unmarshal(v.ValueJSON, &valueMap)
if err != nil {
this.ErrorPage(err)
return
}
valuesMap[timeutil.FormatTime("YmdHi", v.CreatedAt)] = valueMap.GetInt64("total")
}
// 过去一个小时
result := []maps.Map{}
for i := 60; i >= 1; i-- {
timestamp := time.Now().Unix() - int64(i)*60
minute := timeutil.FormatTime("YmdHi", timestamp)
total, ok := valuesMap[minute]
if ok {
result = append(result, maps.Map{
"label": timeutil.FormatTime("H:i", timestamp),
"value": total,
"text": numberutils.FormatInt64(total),
})
} else {
result = append(result, maps.Map{
"label": timeutil.FormatTime("H:i", timestamp),
"value": 0,
"text": "0",
})
}
}
this.Data["values"] = result
this.Success()
}

View File

@@ -1,73 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package monitor
import (
"encoding/json"
"fmt"
"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/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type CpuAction struct {
actionutils.ParentAction
}
func (this *CpuAction) RunPost(params struct {
NodeId int64
}) {
resp, err := this.RPC().NodeValueRPC().ListNodeValues(this.AdminContext(), &pb.ListNodeValuesRequest{
Role: "node",
NodeId: params.NodeId,
Item: nodeconfigs.NodeValueItemCPU,
Range: nodeconfigs.NodeValueRangeMinute,
})
if err != nil {
this.ErrorPage(err)
return
}
valuesMap := map[string]float32{} // YmdHi => usage
for _, v := range resp.NodeValues {
if len(v.ValueJSON) == 0 {
continue
}
valueMap := maps.Map{}
err = json.Unmarshal(v.ValueJSON, &valueMap)
if err != nil {
this.ErrorPage(err)
return
}
valuesMap[timeutil.FormatTime("YmdHi", v.CreatedAt)] = valueMap.GetFloat32("usage") * 100
}
// 过去一个小时
result := []maps.Map{}
for i := 60; i >= 1; i-- {
timestamp := time.Now().Unix() - int64(i)*60
minute := timeutil.FormatTime("YmdHi", timestamp)
total, ok := valuesMap[minute]
if ok {
result = append(result, maps.Map{
"label": timeutil.FormatTime("H:i", timestamp),
"value": total,
"text": fmt.Sprintf("%.2f%%", total),
})
} else {
result = append(result, maps.Map{
"label": timeutil.FormatTime("H:i", timestamp),
"value": 0,
"text": "0.0%",
})
}
}
this.Data["values"] = result
this.Success()
}

View File

@@ -1,73 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package monitor
import (
"encoding/json"
"fmt"
"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/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type LoadAction struct {
actionutils.ParentAction
}
func (this *LoadAction) RunPost(params struct {
NodeId int64
}) {
resp, err := this.RPC().NodeValueRPC().ListNodeValues(this.AdminContext(), &pb.ListNodeValuesRequest{
Role: "node",
NodeId: params.NodeId,
Item: nodeconfigs.NodeValueItemLoad,
Range: nodeconfigs.NodeValueRangeMinute,
})
if err != nil {
this.ErrorPage(err)
return
}
valuesMap := map[string]float32{} // YmdHi => load5m
for _, v := range resp.NodeValues {
if len(v.ValueJSON) == 0 {
continue
}
valueMap := maps.Map{}
err = json.Unmarshal(v.ValueJSON, &valueMap)
if err != nil {
this.ErrorPage(err)
return
}
valuesMap[timeutil.FormatTime("YmdHi", v.CreatedAt)] = valueMap.GetFloat32("load5m")
}
// 过去一个小时
result := []maps.Map{}
for i := 60; i >= 1; i-- {
timestamp := time.Now().Unix() - int64(i)*60
minute := timeutil.FormatTime("YmdHi", timestamp)
total, ok := valuesMap[minute]
if ok {
result = append(result, maps.Map{
"label": timeutil.FormatTime("H:i", timestamp),
"value": total,
"text": fmt.Sprintf("5分钟: %.2f", total),
})
} else {
result = append(result, maps.Map{
"label": timeutil.FormatTime("H:i", timestamp),
"value": 0,
"text": "5分钟: 0.0",
})
}
}
this.Data["values"] = result
this.Success()
}

View File

@@ -1,73 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package monitor
import (
"encoding/json"
"fmt"
"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/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type MemoryAction struct {
actionutils.ParentAction
}
func (this *MemoryAction) RunPost(params struct {
NodeId int64
}) {
resp, err := this.RPC().NodeValueRPC().ListNodeValues(this.AdminContext(), &pb.ListNodeValuesRequest{
Role: "node",
NodeId: params.NodeId,
Item: nodeconfigs.NodeValueItemMemory,
Range: nodeconfigs.NodeValueRangeMinute,
})
if err != nil {
this.ErrorPage(err)
return
}
valuesMap := map[string]float32{} // YmdHi => usage
for _, v := range resp.NodeValues {
if len(v.ValueJSON) == 0 {
continue
}
valueMap := maps.Map{}
err = json.Unmarshal(v.ValueJSON, &valueMap)
if err != nil {
this.ErrorPage(err)
return
}
valuesMap[timeutil.FormatTime("YmdHi", v.CreatedAt)] = valueMap.GetFloat32("usage") * 100
}
// 过去一个小时
result := []maps.Map{}
for i := 60; i >= 1; i-- {
timestamp := time.Now().Unix() - int64(i)*60
minute := timeutil.FormatTime("YmdHi", timestamp)
total, ok := valuesMap[minute]
if ok {
result = append(result, maps.Map{
"label": timeutil.FormatTime("H:i", timestamp),
"value": total,
"text": fmt.Sprintf("%.2f%%", total),
})
} else {
result = append(result, maps.Map{
"label": timeutil.FormatTime("H:i", timestamp),
"value": 0,
"text": "0.0%",
})
}
}
this.Data["values"] = result
this.Success()
}

View File

@@ -1,73 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package monitor
import (
"encoding/json"
"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/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type TrafficInAction struct {
actionutils.ParentAction
}
func (this *TrafficInAction) RunPost(params struct {
NodeId int64
}) {
resp, err := this.RPC().NodeValueRPC().ListNodeValues(this.AdminContext(), &pb.ListNodeValuesRequest{
Role: "node",
NodeId: params.NodeId,
Item: nodeconfigs.NodeValueItemTrafficIn,
Range: nodeconfigs.NodeValueRangeMinute,
})
if err != nil {
this.ErrorPage(err)
return
}
valuesMap := map[string]int64{} // YmdHi => bytes
for _, v := range resp.NodeValues {
if len(v.ValueJSON) == 0 {
continue
}
valueMap := maps.Map{}
err = json.Unmarshal(v.ValueJSON, &valueMap)
if err != nil {
this.ErrorPage(err)
return
}
valuesMap[timeutil.FormatTime("YmdHi", v.CreatedAt)] = valueMap.GetInt64("total")
}
// 过去一个小时
result := []maps.Map{}
for i := 60; i >= 1; i-- {
timestamp := time.Now().Unix() - int64(i)*60
minute := timeutil.FormatTime("YmdHi", timestamp)
total, ok := valuesMap[minute]
if ok {
result = append(result, maps.Map{
"label": timeutil.FormatTime("H:i", timestamp),
"value": total / 60,
"text": numberutils.FormatBytes(total / 60),
})
} else {
result = append(result, maps.Map{
"label": timeutil.FormatTime("H:i", timestamp),
"value": 0,
"text": numberutils.FormatBytes(0),
})
}
}
this.Data["values"] = result
this.Success()
}

View File

@@ -1,73 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package monitor
import (
"encoding/json"
"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/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type TrafficOutAction struct {
actionutils.ParentAction
}
func (this *TrafficOutAction) RunPost(params struct {
NodeId int64
}) {
resp, err := this.RPC().NodeValueRPC().ListNodeValues(this.AdminContext(), &pb.ListNodeValuesRequest{
Role: "node",
NodeId: params.NodeId,
Item: nodeconfigs.NodeValueItemTrafficOut,
Range: nodeconfigs.NodeValueRangeMinute,
})
if err != nil {
this.ErrorPage(err)
return
}
valuesMap := map[string]int64{} // YmdHi => bytes
for _, v := range resp.NodeValues {
if len(v.ValueJSON) == 0 {
continue
}
valueMap := maps.Map{}
err = json.Unmarshal(v.ValueJSON, &valueMap)
if err != nil {
this.ErrorPage(err)
return
}
valuesMap[timeutil.FormatTime("YmdHi", v.CreatedAt)] = valueMap.GetInt64("total")
}
// 过去一个小时
result := []maps.Map{}
for i := 60; i >= 1; i-- {
timestamp := time.Now().Unix() - int64(i)*60
minute := timeutil.FormatTime("YmdHi", timestamp)
total, ok := valuesMap[minute]
if ok {
result = append(result, maps.Map{
"label": timeutil.FormatTime("H:i", timestamp),
"value": total / 60,
"text": numberutils.FormatBytes(total / 60),
})
} else {
result = append(result, maps.Map{
"label": timeutil.FormatTime("H:i", timestamp),
"value": 0,
"text": numberutils.FormatBytes(0),
})
}
}
this.Data["values"] = result
this.Success()
}

View File

@@ -0,0 +1,94 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
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"
"github.com/iwind/TeaGo/types"
"strconv"
)
// InitNodeInfo 初始化节点信息
func InitNodeInfo(parentAction *actionutils.ParentAction, nodeId int64) (*pb.Node, error) {
// 节点信息(用于菜单)
nodeResp, err := parentAction.RPC().NodeRPC().FindEnabledNode(parentAction.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: nodeId})
if err != nil {
return nil, err
}
if nodeResp.Node == nil {
return nil, errors.New("node '" + strconv.FormatInt(nodeId, 10) + "' not found")
}
var node = nodeResp.Node
var groupMap maps.Map
if node.NodeGroup != nil {
groupMap = maps.Map{
"id": node.NodeGroup.Id,
"name": node.NodeGroup.Name,
}
}
parentAction.Data["node"] = maps.Map{
"id": node.Id,
"name": node.Name,
"isOn": node.IsOn,
"isUp": node.IsUp,
"group": groupMap,
}
var clusterId int64 = 0
if node.NodeCluster != nil {
parentAction.Data["clusterId"] = node.NodeCluster.Id
clusterId = node.NodeCluster.Id
}
// 左侧菜单
var prefix = "/clusters/cluster/node"
var query = "clusterId=" + types.String(clusterId) + "&nodeId=" + types.String(nodeId)
var menuItem = parentAction.Data.GetString("secondMenuItem")
var menuItems = []maps.Map{
{
"name": "基础设置",
"url": prefix + "/update?" + query,
"isActive": menuItem == "basic",
},
{
"name": "DNS设置",
"url": prefix + "/settings/dns?" + query,
"isActive": menuItem == "dns",
},
{
"name": "缓存设置",
"url": prefix + "/settings/cache?" + query,
"isActive": menuItem == "cache",
},
}
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",
},
{
"name": "系统设置",
"url": prefix + "/settings/system?" + query,
"isActive": menuItem == "system",
},
}...)
parentAction.Data["leftMenuItems"] = menuItems
return nodeResp.Node, nil
}

View File

@@ -0,0 +1,117 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package cache
import (
"encoding/json"
"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/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "update")
this.SecondMenu("cache")
}
func (this *IndexAction) RunGet(params struct {
NodeId int64
}) {
node, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
// 缓存硬盘 & 内存容量
var maxCacheDiskCapacity maps.Map = nil
if node.MaxCacheDiskCapacity != nil {
maxCacheDiskCapacity = maps.Map{
"count": node.MaxCacheDiskCapacity.Count,
"unit": node.MaxCacheDiskCapacity.Unit,
}
} else {
maxCacheDiskCapacity = maps.Map{
"count": 0,
"unit": "gb",
}
}
var maxCacheMemoryCapacity maps.Map = nil
if node.MaxCacheMemoryCapacity != nil {
maxCacheMemoryCapacity = maps.Map{
"count": node.MaxCacheMemoryCapacity.Count,
"unit": node.MaxCacheMemoryCapacity.Unit,
}
} else {
maxCacheMemoryCapacity = maps.Map{
"count": 0,
"unit": "gb",
}
}
var nodeMap = this.Data["node"].(maps.Map)
nodeMap["maxCacheDiskCapacity"] = maxCacheDiskCapacity
nodeMap["maxCacheMemoryCapacity"] = maxCacheMemoryCapacity
this.Show()
}
func (this *IndexAction) RunPost(params struct {
NodeId int64
MaxCacheDiskCapacityJSON []byte
MaxCacheMemoryCapacityJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo("修改节点 %d 缓存设置", params.NodeId)
// 缓存硬盘 & 内存容量
var pbMaxCacheDiskCapacity *pb.SizeCapacity
if len(params.MaxCacheDiskCapacityJSON) > 0 {
var sizeCapacity = &shared.SizeCapacity{}
err := json.Unmarshal(params.MaxCacheDiskCapacityJSON, sizeCapacity)
if err != nil {
this.ErrorPage(err)
return
}
pbMaxCacheDiskCapacity = &pb.SizeCapacity{
Count: sizeCapacity.Count,
Unit: sizeCapacity.Unit,
}
}
var pbMaxCacheMemoryCapacity *pb.SizeCapacity
if len(params.MaxCacheMemoryCapacityJSON) > 0 {
var sizeCapacity = &shared.SizeCapacity{}
err := json.Unmarshal(params.MaxCacheMemoryCapacityJSON, sizeCapacity)
if err != nil {
this.ErrorPage(err)
return
}
pbMaxCacheMemoryCapacity = &pb.SizeCapacity{
Count: sizeCapacity.Count,
Unit: sizeCapacity.Unit,
}
}
_, err := this.RPC().NodeRPC().UpdateNodeCache(this.AdminContext(), &pb.UpdateNodeCacheRequest{
NodeId: params.NodeId,
MaxCacheDiskCapacity: pbMaxCacheDiskCapacity,
MaxCacheMemoryCapacity: pbMaxCacheMemoryCapacity,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,125 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package dns
import (
"encoding/json"
"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/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "update")
this.SecondMenu("dns")
}
func (this *IndexAction) RunGet(params struct {
NodeId int64
}) {
node, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
// DNS相关
var clusters = []*pb.NodeCluster{node.NodeCluster}
clusters = append(clusters, node.SecondaryNodeClusters...)
var allDNSRouteMaps = map[int64][]maps.Map{} // domain id => routes
var routeMaps = map[int64][]maps.Map{} // domain id => routes
for _, cluster := range clusters {
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{
NodeId: params.NodeId,
NodeClusterId: cluster.Id,
})
if err != nil {
this.ErrorPage(err)
return
}
var dnsInfo = dnsInfoResp.Node
if dnsInfo.DnsDomainId <= 0 || len(dnsInfo.DnsDomainName) == 0 {
continue
}
var domainId = dnsInfo.DnsDomainId
var domainName = dnsInfo.DnsDomainName
if len(dnsInfo.Routes) > 0 {
for _, route := range dnsInfo.Routes {
routeMaps[domainId] = append(routeMaps[domainId], maps.Map{
"domainId": domainId,
"domainName": domainName,
"code": route.Code,
"name": route.Name,
})
}
}
// 所有线路选项
routesResp, err := this.RPC().DNSDomainRPC().FindAllDNSDomainRoutes(this.AdminContext(), &pb.FindAllDNSDomainRoutesRequest{DnsDomainId: dnsInfoResp.Node.DnsDomainId})
if err != nil {
this.ErrorPage(err)
return
}
for _, route := range routesResp.Routes {
allDNSRouteMaps[domainId] = append(allDNSRouteMaps[domainId], maps.Map{
"domainId": domainId,
"domainName": domainName,
"name": route.Name,
"code": route.Code,
})
}
}
var domainRoutes = []maps.Map{}
for _, m := range routeMaps {
domainRoutes = append(domainRoutes, m...)
}
this.Data["dnsRoutes"] = domainRoutes
var allDomainRoutes = []maps.Map{}
for _, m := range allDNSRouteMaps {
allDomainRoutes = append(allDomainRoutes, m...)
}
this.Data["allDNSRoutes"] = allDomainRoutes
this.Show()
}
func (this *IndexAction) RunPost(params struct {
NodeId int64
DnsDomainId int64
DnsRoutesJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo("修改节点 %d DNS设置", params.NodeId)
dnsRouteCodes := []string{}
if len(params.DnsRoutesJSON) > 0 {
err := json.Unmarshal(params.DnsRoutesJSON, &dnsRouteCodes)
if err != nil {
this.ErrorPage(err)
return
}
}
_, err := this.RPC().NodeRPC().UpdateNodeDNS(this.AdminContext(), &pb.UpdateNodeDNSRequest{
NodeId: params.NodeId,
IpAddr: "",
DnsDomainId: 0,
Routes: dnsRouteCodes,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,114 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package ssh
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "update")
this.SecondMenu("ssh")
}
func (this *IndexAction) RunGet(params struct {
NodeId int64
}) {
node, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
// 登录信息
var loginMap maps.Map = nil
if node.NodeLogin != nil {
loginParams := maps.Map{}
if len(node.NodeLogin.Params) > 0 {
err = json.Unmarshal(node.NodeLogin.Params, &loginParams)
if err != nil {
this.ErrorPage(err)
return
}
}
grantMap := maps.Map{}
grantId := loginParams.GetInt64("grantId")
if grantId > 0 {
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: grantId})
if err != nil {
this.ErrorPage(err)
return
}
if grantResp.NodeGrant != nil {
grantMap = maps.Map{
"id": grantResp.NodeGrant.Id,
"name": grantResp.NodeGrant.Name,
"method": grantResp.NodeGrant.Method,
"methodName": grantutils.FindGrantMethodName(grantResp.NodeGrant.Method),
"username": grantResp.NodeGrant.Username,
}
}
}
loginMap = maps.Map{
"id": node.NodeLogin.Id,
"name": node.NodeLogin.Name,
"type": node.NodeLogin.Type,
"params": loginParams,
"grant": grantMap,
}
}
var nodeMap = this.Data["node"].(maps.Map)
nodeMap["login"] = loginMap
this.Show()
}
func (this *IndexAction) RunPost(params struct {
NodeId int64
LoginId int64
GrantId int64
SshHost string
SshPort int
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo("修改节点 %d SSH登录信息", params.NodeId)
// TODO 检查登录授权
loginInfo := &pb.NodeLogin{
Id: params.LoginId,
Name: "SSH",
Type: "ssh",
Params: maps.Map{
"grantId": params.GrantId,
"host": params.SshHost,
"port": params.SshPort,
}.AsJSON(),
}
_, err := this.RPC().NodeRPC().UpdateNodeLogin(this.AdminContext(), &pb.UpdateNodeLoginRequest{
NodeId: params.NodeId,
NodeLogin: loginInfo,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,61 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package system
import (
"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/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "update")
this.SecondMenu("system")
}
func (this *IndexAction) RunGet(params struct {
NodeId int64
}) {
node, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
// 获取节点信息
var nodeMap = this.Data["node"].(maps.Map)
nodeMap["maxCPU"] = node.MaxCPU
this.Show()
}
func (this *IndexAction) RunPost(params struct {
NodeId int64
MaxCPU int32
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo("修改节点 %d 系统信息", params.NodeId)
if params.MaxCPU < 0 {
this.Fail("CPU线程数不能小于0")
}
_, err := this.RPC().NodeRPC().UpdateNodeSystem(this.AdminContext(), &pb.UpdateNodeSystemRequest{
NodeId: params.NodeId,
MaxCPU: params.MaxCPU,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -4,6 +4,7 @@ package thresholds
import (
"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"
@@ -14,13 +15,20 @@ type IndexAction struct {
}
func (this *IndexAction) Init() {
this.Nav("", "node", "threshold")
this.Nav("", "", "update")
this.SecondMenu("threshold")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
NodeId int64
}) {
_, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["nodeId"] = params.NodeId
// 列出所有阈值

View File

@@ -6,7 +6,7 @@ import (
"github.com/iwind/TeaGo/maps"
)
// 节点状态
// StatusAction 节点状态
type StatusAction struct {
actionutils.ParentAction
}

View File

@@ -0,0 +1,33 @@
package node
import (
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type SyncDomainAction struct {
actionutils.ParentAction
}
func (this *SyncDomainAction) RunPost(params struct {
DomainId int64
}) {
// 记录日志
defer this.CreateLog(oplogs.LevelInfo, "同步DNS域名数据 %d", params.DomainId)
// 执行同步
resp, err := this.RPC().DNSDomainRPC().SyncDNSDomainData(this.AdminContext(), &pb.SyncDNSDomainDataRequest{DnsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
if resp.IsOk {
this.Success()
} else {
this.Data["shouldFix"] = resp.ShouldFix
this.Fail(resp.Error)
}
this.Success()
}

View File

@@ -4,11 +4,10 @@ import (
"encoding/json"
"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/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/ipAddresses/ipaddressutils"
"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"
"github.com/iwind/TeaGo/maps"
)
@@ -19,12 +18,18 @@ type UpdateAction struct {
func (this *UpdateAction) Init() {
this.Nav("", "node", "update")
this.SecondMenu("nodes")
this.SecondMenu("basic")
}
func (this *UpdateAction) RunGet(params struct {
NodeId int64
}) {
_, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["nodeId"] = params.NodeId
nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId})
@@ -47,7 +52,7 @@ func (this *UpdateAction) RunGet(params struct {
}
// IP地址
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledNodeIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledNodeIPAddressesWithNodeIdRequest{
NodeId: params.NodeId,
Role: nodeconfigs.NodeRoleNode,
})
@@ -56,91 +61,22 @@ func (this *UpdateAction) RunGet(params struct {
return
}
ipAddressMaps := []maps.Map{}
for _, addr := range ipAddressesResp.Addresses {
ipAddressMaps = append(ipAddressMaps, maps.Map{
"id": addr.Id,
"name": addr.Name,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
})
}
// DNS相关
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
nodeDNS := dnsInfoResp.Node
dnsRouteMaps := []maps.Map{}
if nodeDNS != nil {
for _, dnsInfo := range nodeDNS.Routes {
dnsRouteMaps = append(dnsRouteMaps, maps.Map{
"name": dnsInfo.Name,
"code": dnsInfo.Code,
})
}
}
this.Data["dnsRoutes"] = dnsRouteMaps
this.Data["allDNSRoutes"] = []maps.Map{}
if nodeDNS != nil {
this.Data["dnsDomainId"] = nodeDNS.DnsDomainId
} else {
this.Data["dnsDomainId"] = 0
}
if nodeDNS != nil && nodeDNS.DnsDomainId > 0 {
routesMaps := []maps.Map{}
routesResp, err := this.RPC().DNSDomainRPC().FindAllDNSDomainRoutes(this.AdminContext(), &pb.FindAllDNSDomainRoutesRequest{DnsDomainId: dnsInfoResp.Node.DnsDomainId})
for _, addr := range ipAddressesResp.NodeIPAddresses {
thresholds, err := ipaddressutils.InitNodeIPAddressThresholds(this.Parent(), addr.Id)
if err != nil {
this.ErrorPage(err)
return
}
for _, route := range routesResp.Routes {
routesMaps = append(routesMaps, maps.Map{
"name": route.Name,
"code": route.Code,
})
}
this.Data["allDNSRoutes"] = routesMaps
}
// 登录信息
var loginMap maps.Map = nil
if node.Login != nil {
loginParams := maps.Map{}
if len(node.Login.Params) > 0 {
err = json.Unmarshal(node.Login.Params, &loginParams)
if err != nil {
this.ErrorPage(err)
return
}
}
grantMap := maps.Map{}
grantId := loginParams.GetInt64("grantId")
if grantId > 0 {
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: grantId})
if err != nil {
this.ErrorPage(err)
return
}
if grantResp.NodeGrant != nil {
grantMap = maps.Map{
"id": grantResp.NodeGrant.Id,
"name": grantResp.NodeGrant.Name,
"method": grantResp.NodeGrant.Method,
"methodName": grantutils.FindGrantMethodName(grantResp.NodeGrant.Method),
}
}
}
loginMap = maps.Map{
"id": node.Login.Id,
"name": node.Login.Name,
"type": node.Login.Type,
"params": loginParams,
"grant": grantMap,
}
ipAddressMaps = append(ipAddressMaps, maps.Map{
"id": addr.Id,
"name": addr.Name,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
"isOn": addr.IsOn,
"isUp": addr.IsUp,
"thresholds": thresholds,
})
}
// 分组
@@ -161,86 +97,53 @@ func (this *UpdateAction) RunGet(params struct {
}
}
// 缓存硬盘 & 内存容量
var maxCacheDiskCapacity maps.Map = nil
if node.MaxCacheDiskCapacity != nil {
maxCacheDiskCapacity = maps.Map{
"count": node.MaxCacheDiskCapacity.Count,
"unit": node.MaxCacheDiskCapacity.Unit,
var m = maps.Map{
"id": node.Id,
"name": node.Name,
"ipAddresses": ipAddressMaps,
"cluster": clusterMap,
"isOn": node.IsOn,
"group": groupMap,
"region": regionMap,
}
if node.NodeCluster != nil {
m["primaryCluster"] = maps.Map{
"id": node.NodeCluster.Id,
"name": node.NodeCluster.Name,
}
} else {
maxCacheDiskCapacity = maps.Map{
"count": 0,
"unit": "gb",
}
m["primaryCluster"] = nil
}
var maxCacheMemoryCapacity maps.Map = nil
if node.MaxCacheMemoryCapacity != nil {
maxCacheMemoryCapacity = maps.Map{
"count": node.MaxCacheMemoryCapacity.Count,
"unit": node.MaxCacheMemoryCapacity.Unit,
if len(node.SecondaryNodeClusters) > 0 {
var secondaryClusterMaps = []maps.Map{}
for _, cluster := range node.SecondaryNodeClusters {
secondaryClusterMaps = append(secondaryClusterMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
})
}
m["secondaryClusters"] = secondaryClusterMaps
} else {
maxCacheMemoryCapacity = maps.Map{
"count": 0,
"unit": "gb",
}
m["secondaryClusters"] = []interface{}{}
}
this.Data["node"] = maps.Map{
"id": node.Id,
"name": node.Name,
"ipAddresses": ipAddressMaps,
"cluster": clusterMap,
"login": loginMap,
"maxCPU": node.MaxCPU,
"isOn": node.IsOn,
"group": groupMap,
"region": regionMap,
"maxCacheDiskCapacity": maxCacheDiskCapacity,
"maxCacheMemoryCapacity": maxCacheMemoryCapacity,
}
// 所有集群
resp, err := this.RPC().NodeClusterRPC().FindAllEnabledNodeClusters(this.AdminContext(), &pb.FindAllEnabledNodeClustersRequest{})
if err != nil {
this.ErrorPage(err)
}
if err != nil {
this.ErrorPage(err)
return
}
clusterMaps := []maps.Map{}
for _, cluster := range resp.NodeClusters {
clusterMaps = append(clusterMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
})
}
this.Data["clusters"] = clusterMaps
this.Data["node"] = m
this.Show()
}
func (this *UpdateAction) RunPost(params struct {
LoginId int64
NodeId int64
GroupId int64
RegionId int64
Name string
IPAddressesJSON []byte `alias:"ipAddressesJSON"`
ClusterId int64
GrantId int64
SshHost string
SshPort int
MaxCPU int32
IsOn bool
MaxCacheDiskCapacityJSON []byte
MaxCacheMemoryCapacityJSON []byte
DnsDomainId int64
DnsRoutesJSON []byte
LoginId int64
NodeId int64
GroupId int64
RegionId int64
Name string
IPAddressesJSON []byte `alias:"ipAddressesJSON"`
PrimaryClusterId int64
SecondaryClusterIds []byte
IsOn bool
Must *actions.Must
}) {
@@ -256,8 +159,17 @@ func (this *UpdateAction) RunPost(params struct {
Require("请输入节点名称")
// TODO 检查cluster
if params.ClusterId <= 0 {
this.Fail("请选择所在集群")
if params.PrimaryClusterId <= 0 {
this.Fail("请选择节点所在集群")
}
var secondaryClusterIds = []int64{}
if len(params.SecondaryClusterIds) > 0 {
err := json.Unmarshal(params.SecondaryClusterIds, &secondaryClusterIds)
if err != nil {
this.ErrorPage(err)
return
}
}
// IP地址
@@ -273,70 +185,15 @@ func (this *UpdateAction) RunPost(params struct {
this.Fail("请至少输入一个IP地址")
}
dnsRouteCodes := []string{}
if len(params.DnsRoutesJSON) > 0 {
err := json.Unmarshal(params.DnsRoutesJSON, &dnsRouteCodes)
if err != nil {
this.ErrorPage(err)
return
}
}
// TODO 检查登录授权
loginInfo := &pb.NodeLogin{
Id: params.LoginId,
Name: "SSH",
Type: "ssh",
Params: maps.Map{
"grantId": params.GrantId,
"host": params.SshHost,
"port": params.SshPort,
}.AsJSON(),
}
// 缓存硬盘 & 内存容量
var pbMaxCacheDiskCapacity *pb.SizeCapacity
if len(params.MaxCacheDiskCapacityJSON) > 0 {
var sizeCapacity = &shared.SizeCapacity{}
err := json.Unmarshal(params.MaxCacheDiskCapacityJSON, sizeCapacity)
if err != nil {
this.ErrorPage(err)
return
}
pbMaxCacheDiskCapacity = &pb.SizeCapacity{
Count: sizeCapacity.Count,
Unit: sizeCapacity.Unit,
}
}
var pbMaxCacheMemoryCapacity *pb.SizeCapacity
if len(params.MaxCacheMemoryCapacityJSON) > 0 {
var sizeCapacity = &shared.SizeCapacity{}
err := json.Unmarshal(params.MaxCacheMemoryCapacityJSON, sizeCapacity)
if err != nil {
this.ErrorPage(err)
return
}
pbMaxCacheMemoryCapacity = &pb.SizeCapacity{
Count: sizeCapacity.Count,
Unit: sizeCapacity.Unit,
}
}
// 保存
_, err := this.RPC().NodeRPC().UpdateNode(this.AdminContext(), &pb.UpdateNodeRequest{
NodeId: params.NodeId,
NodeGroupId: params.GroupId,
NodeRegionId: params.RegionId,
Name: params.Name,
NodeClusterId: params.ClusterId,
NodeLogin: loginInfo,
MaxCPU: params.MaxCPU,
IsOn: params.IsOn,
DnsDomainId: params.DnsDomainId,
DnsRoutes: dnsRouteCodes,
MaxCacheDiskCapacity: pbMaxCacheDiskCapacity,
MaxCacheMemoryCapacity: pbMaxCacheMemoryCapacity,
NodeId: params.NodeId,
NodeGroupId: params.GroupId,
NodeRegionId: params.RegionId,
Name: params.Name,
NodeClusterId: params.PrimaryClusterId,
SecondaryNodeClusterIds: secondaryClusterIds,
IsOn: params.IsOn,
})
if err != nil {
this.ErrorPage(err)
@@ -344,7 +201,7 @@ func (this *UpdateAction) RunPost(params struct {
}
// 禁用老的IP地址
_, err = this.RPC().NodeIPAddressRPC().DisableAllIPAddressesWithNodeId(this.AdminContext(), &pb.DisableAllIPAddressesWithNodeIdRequest{
_, err = this.RPC().NodeIPAddressRPC().DisableAllNodeIPAddressesWithNodeId(this.AdminContext(), &pb.DisableAllNodeIPAddressesWithNodeIdRequest{
NodeId: params.NodeId,
Role: nodeconfigs.NodeRoleNode,
})

View File

@@ -0,0 +1,117 @@
package node
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"net"
)
type UpdateDNSPopupAction struct {
actionutils.ParentAction
}
func (this *UpdateDNSPopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdateDNSPopupAction) RunGet(params struct {
ClusterId int64
NodeId int64
}) {
this.Data["nodeId"] = params.NodeId
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{
NodeId: params.NodeId,
NodeClusterId: params.ClusterId,
})
if err != nil {
this.ErrorPage(err)
return
}
dnsInfo := dnsInfoResp.Node
if dnsInfo == nil {
this.NotFound("node", params.NodeId)
return
}
this.Data["ipAddr"] = dnsInfo.IpAddr
this.Data["routes"] = domainutils.ConvertRoutesToMaps(dnsInfo)
this.Data["domainId"] = dnsInfo.DnsDomainId
this.Data["domainName"] = dnsInfo.DnsDomainName
// 读取所有线路
allRouteMaps := []maps.Map{}
if dnsInfo.DnsDomainId > 0 {
routesResp, err := this.RPC().DNSDomainRPC().FindAllDNSDomainRoutes(this.AdminContext(), &pb.FindAllDNSDomainRoutesRequest{DnsDomainId: dnsInfo.DnsDomainId})
if err != nil {
this.ErrorPage(err)
return
}
if len(routesResp.Routes) > 0 {
for _, route := range routesResp.Routes {
allRouteMaps = append(allRouteMaps, maps.Map{
"name": route.Name,
"code": route.Code,
"domainName": dnsInfo.DnsDomainName,
"domainId": dnsInfo.DnsDomainId,
})
}
// 筛选
var routes = domainutils.FilterRoutes(dnsInfo.Routes, routesResp.Routes)
dnsInfo.Routes = routes
this.Data["routes"] = domainutils.ConvertRoutesToMaps(dnsInfo)
}
}
this.Data["allRoutes"] = allRouteMaps
this.Show()
}
func (this *UpdateDNSPopupAction) RunPost(params struct {
NodeId int64
IpAddr string
DomainId int64
DnsRoutesJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
// 操作日志
defer this.CreateLog(oplogs.LevelInfo, "修改节点 %d 的DNS设置", params.NodeId)
routes := []string{}
if len(params.DnsRoutesJSON) > 0 {
err := json.Unmarshal(params.DnsRoutesJSON, &routes)
if err != nil {
this.ErrorPage(err)
return
}
}
params.Must.
Field("ipAddr", params.IpAddr).
Require("请输入IP地址")
if net.ParseIP(params.IpAddr) == nil {
this.FailField("ipAddr", "请输入正确的IP地址")
}
// 执行修改
_, err := this.RPC().NodeRPC().UpdateNodeDNS(this.AdminContext(), &pb.UpdateNodeDNSRequest{
NodeId: params.NodeId,
IpAddr: params.IpAddr,
DnsDomainId: params.DomainId,
Routes: routes,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,271 @@
package cluster
import (
"encoding/json"
"fmt"
"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", "index")
this.SecondMenu("nodes")
}
func (this *NodesAction) RunGet(params struct {
ClusterId int64
GroupId int64
RegionId int64
InstalledState int
ActiveState int
Keyword string
CpuOrder string
MemoryOrder string
TrafficInOrder string
TrafficOutOrder 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
// 集群是否已经设置了线路
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,
InstallState: types.Int32(params.InstalledState),
ActiveState: types.Int32(params.ActiveState),
Keyword: params.Keyword,
})
if err != nil {
this.ErrorPage(err)
return
}
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,
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
}
nodesResp, err := this.RPC().NodeRPC().ListEnabledNodesMatch(this.AdminContext(), req)
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 {
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": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
"memUsage": status.MemoryUsage,
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
"trafficInBytes": status.TrafficInBytes,
"trafficOutBytes": status.TrafficOutBytes,
},
"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
// 所有分组
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 {
countResp, err := this.RPC().NodeRPC().CountAllEnabledNodesWithNodeGroupId(this.AdminContext(), &pb.CountAllEnabledNodesWithNodeGroupIdRequest{NodeGroupId: group.Id})
if err != nil {
this.ErrorPage(err)
return
}
countNodes := countResp.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
}
regionMaps := []maps.Map{}
for _, region := range regionsResp.NodeRegions {
regionMaps = append(regionMaps, maps.Map{
"id": region.Id,
"name": region.Name,
})
}
this.Data["regions"] = regionMaps
// 记录最近访问
_, err = this.RPC().LatestItemRPC().IncreaseLatestItem(this.AdminContext(), &pb.IncreaseLatestItemRequest{
ItemType: "cluster",
ItemId: params.ClusterId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Show()
}

View File

@@ -46,6 +46,13 @@ func (this *IndexAction) RunGet(params struct {
this.Data["domainName"] = dnsInfoResp.Domain.Name
}
if len(dnsInfoResp.CnameRecords) == 0 {
this.Data["cnameRecords"] = []string{}
} else {
this.Data["cnameRecords"] = dnsInfoResp.CnameRecords
}
this.Data["ttl"] = dnsInfoResp.Ttl
this.Show()
}
@@ -56,6 +63,8 @@ func (this *IndexAction) RunPost(params struct {
DnsName string
NodesAutoSync bool
ServersAutoSync bool
CnameRecords []string
Ttl int32
Must *actions.Must
CSRF *actionutils.CSRF
@@ -63,6 +72,14 @@ func (this *IndexAction) RunPost(params struct {
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "修改集群 %d DNS设置", params.ClusterId)
if params.DnsDomainId <= 0 {
this.Fail("请选择集群的主域名")
}
params.Must.
Field("dnsName", params.DnsName).
Require("请输入DNS子域名")
// 检查DNS名称
if len(params.DnsName) > 0 {
if !domainutils.ValidateDomainFormat(params.DnsName) {
@@ -89,6 +106,8 @@ func (this *IndexAction) RunPost(params struct {
DnsDomainId: params.DnsDomainId,
NodesAutoSync: params.NodesAutoSync,
ServersAutoSync: params.ServersAutoSync,
CnameRecords: params.CnameRecords,
Ttl: params.Ttl,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -32,6 +32,9 @@ func (this *RunPopupAction) RunPost(params struct {
this.Fail(err.Error())
}
if resp.Results == nil {
resp.Results = []*pb.ExecuteNodeClusterHealthCheckResponse_Result{}
}
this.Data["results"] = resp.Results
this.Success()
}

View File

@@ -4,6 +4,7 @@ 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"
@@ -21,6 +22,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,10 +55,20 @@ func (this *IndexAction) RunGet(params struct {
}
this.Data["grant"] = grantMap
// 时区
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,
}
this.Show()
@@ -68,6 +80,7 @@ func (this *IndexAction) RunPost(params struct {
Name string
GrantId int64
InstallDir string
TimeZone string
Must *actions.Must
}) {
@@ -83,6 +96,7 @@ func (this *IndexAction) RunPost(params struct {
Name: params.Name,
NodeGrantId: params.GrantId,
InstallDir: params.InstallDir,
TimeZone: params.TimeZone,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -7,6 +7,7 @@ import (
firewallActions "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/firewall-actions"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/health"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/message"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/metrics"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/services"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/thresholds"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/toa"
@@ -67,6 +68,12 @@ func init() {
GetPost("/updatePopup", new(thresholds.UpdatePopupAction)).
Post("/delete", new(thresholds.DeleteAction)).
// 指标
Prefix("/clusters/cluster/settings/metrics").
Get("", new(metrics.IndexAction)).
GetPost("/createPopup", new(metrics.CreatePopupAction)).
Post("/delete", new(metrics.DeleteAction)).
EndAll()
})
}

View File

@@ -0,0 +1,100 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package metrics
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct {
ClusterId int64
Category string
}) {
if len(params.Category) == 0 {
params.Category = "http"
}
this.Data["category"] = params.Category
this.Data["clusterId"] = params.ClusterId
countResp, err := this.RPC().MetricItemRPC().CountAllEnabledMetricItems(this.AdminContext(), &pb.CountAllEnabledMetricItemsRequest{Category: params.Category})
if err != nil {
this.ErrorPage(err)
return
}
var count = countResp.Count
page := this.NewPage(count)
this.Data["page"] = page.AsHTML()
itemsResp, err := this.RPC().MetricItemRPC().ListEnabledMetricItems(this.AdminContext(), &pb.ListEnabledMetricItemsRequest{
Category: params.Category,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var itemMaps = []maps.Map{}
for _, item := range itemsResp.MetricItems {
// 是否已添加
existsResp, err := this.RPC().NodeClusterMetricItemRPC().ExistsNodeClusterMetricItem(this.AdminContext(), &pb.ExistsNodeClusterMetricItemRequest{
NodeClusterId: params.ClusterId,
MetricItemId: item.Id,
})
if err != nil {
this.ErrorPage(err)
return
}
var exists = existsResp.Exists
itemMaps = append(itemMaps, maps.Map{
"id": item.Id,
"name": item.Name,
"code": item.Code,
"isOn": item.IsOn,
"period": item.Period,
"periodUnit": item.PeriodUnit,
"periodUnitName": serverconfigs.FindMetricPeriodUnitName(item.PeriodUnit),
"keys": item.Keys,
"value": item.Value,
"valueName": serverconfigs.FindMetricValueName(item.Category, item.Value),
"category": item.Category,
"isPublic": item.IsPublic,
"isChecked": exists,
})
}
this.Data["items"] = itemMaps
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
ClusterId int64
ItemId int64
Must *actions.Must
}) {
defer this.CreateLogInfo("添加指标 %d 到集群 %d", params.ItemId, params.ClusterId)
_, err := this.RPC().NodeClusterMetricItemRPC().EnableNodeClusterMetricItem(this.AdminContext(), &pb.EnableNodeClusterMetricItemRequest{
NodeClusterId: params.ClusterId,
MetricItemId: params.ItemId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,30 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package metrics
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
ClusterId int64
ItemId int64
}) {
defer this.CreateLogInfo("从集群 %d 中移除指标 %d", params.ClusterId, params.ItemId)
_, err := this.RPC().NodeClusterMetricItemRPC().DisableNodeClusterMetricItem(this.AdminContext(), &pb.DisableNodeClusterMetricItemRequest{
NodeClusterId: params.ClusterId,
MetricItemId: params.ItemId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,59 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package metrics
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "setting")
this.SecondMenu("metric")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
Category string
}) {
if len(params.Category) == 0 {
params.Category = "http"
}
this.Data["category"] = params.Category
itemsResp, err := this.RPC().NodeClusterMetricItemRPC().FindAllNodeClusterMetricItems(this.AdminContext(), &pb.FindAllNodeClusterMetricItemsRequest{
NodeClusterId: params.ClusterId,
Category: params.Category,
})
if err != nil {
this.ErrorPage(err)
return
}
var itemMaps = []maps.Map{}
for _, item := range itemsResp.MetricItems {
itemMaps = append(itemMaps, maps.Map{
"id": item.Id,
"name": item.Name,
"code": item.Code,
"isOn": item.IsOn,
"period": item.Period,
"periodUnit": item.PeriodUnit,
"periodUnitName": serverconfigs.FindMetricPeriodUnitName(item.PeriodUnit),
"keys": item.Keys,
"value": item.Value,
"valueName": serverconfigs.FindMetricValueName(item.Category, item.Value),
"category": item.Category,
"isPublic": item.IsPublic,
})
}
this.Data["items"] = itemMaps
this.Show()
}

View File

@@ -0,0 +1,36 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package cluster
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type SuggestLoginPortsAction struct {
actionutils.ParentAction
}
func (this *SuggestLoginPortsAction) RunPost(params struct {
Host string
}) {
portsResp, err := this.RPC().NodeLoginRPC().FindNodeLoginSuggestPorts(this.AdminContext(), &pb.FindNodeLoginSuggestPortsRequest{Host: params.Host})
if err != nil {
this.ErrorPage(err)
return
}
if len(portsResp.Ports) == 0 {
this.Data["ports"] = []int32{}
} else {
this.Data["ports"] = portsResp.Ports
}
if len(portsResp.AvailablePorts) == 0 {
this.Data["availablePorts"] = []int32{}
} else {
this.Data["availablePorts"] = portsResp.AvailablePorts
}
this.Success()
}

View File

@@ -36,6 +36,11 @@ func (this *UpdateNodeSSHAction) RunGet(params struct {
"id": node.Id,
"name": node.Name,
}
if nodeResp.Node.NodeCluster != nil {
this.Data["clusterId"] = nodeResp.Node.NodeCluster.Id
} else {
this.Data["clusterId"] = 0
}
// SSH
loginParams := maps.Map{
@@ -44,10 +49,10 @@ func (this *UpdateNodeSSHAction) RunGet(params struct {
"grantId": 0,
}
this.Data["loginId"] = 0
if node.Login != nil {
this.Data["loginId"] = node.Login.Id
if len(node.Login.Params) > 0 {
err = json.Unmarshal(node.Login.Params, &loginParams)
if node.NodeLogin != nil {
this.Data["loginId"] = node.NodeLogin.Id
if len(node.NodeLogin.Params) > 0 {
err = json.Unmarshal(node.NodeLogin.Params, &loginParams)
if err != nil {
this.ErrorPage(err)
return

View File

@@ -31,8 +31,8 @@ func (this *UpgradeRemoteAction) RunGet(params struct {
}
for _, node := range resp.Nodes {
loginParams := maps.Map{}
if node.Node.Login != nil && len(node.Node.Login.Params) > 0 {
err := json.Unmarshal(node.Node.Login.Params, &loginParams)
if node.Node.NodeLogin != nil && len(node.Node.NodeLogin.Params) > 0 {
err := json.Unmarshal(node.Node.NodeLogin.Params, &loginParams)
if err != nil {
this.ErrorPage(err)
return
@@ -46,7 +46,7 @@ func (this *UpgradeRemoteAction) RunGet(params struct {
"arch": node.Arch,
"oldVersion": node.OldVersion,
"newVersion": node.NewVersion,
"login": node.Node.Login,
"login": node.Node.NodeLogin,
"loginParams": loginParams,
"addresses": node.Node.IpAddresses,
"installStatus": node.Node.InstallStatus,

View File

@@ -1,14 +1,11 @@
package clusterutils
import (
"encoding/json"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
@@ -38,7 +35,8 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
action.Data["clusterId"] = clusterId
if clusterId > 0 {
cluster, err := dao.SharedNodeClusterDAO.FindEnabledNodeCluster(actionPtr.(rpc.ContextInterface).AdminContext(), clusterId)
var ctx = actionPtr.(actionutils.ActionInterface).AdminContext()
cluster, err := dao.SharedNodeClusterDAO.FindEnabledNodeCluster(ctx, clusterId)
if err != nil {
logs.Error(err)
return
@@ -47,10 +45,24 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
action.WriteString("can not find cluster")
return
}
action.Data["currentClusterName"] = cluster.Name
clusterInfo, err := dao.SharedNodeClusterDAO.FindEnabledNodeClusterConfigInfo(ctx, clusterId)
if err != nil {
logs.Error(err)
return
}
if clusterInfo == nil {
action.WriteString("can not find cluster info")
return
}
tabbar := actionutils.NewTabbar()
tabbar.Add("集群列表", "", "/clusters", "", false)
tabbar.Add("集群节点", "", "/clusters/cluster?clusterId="+clusterIdString, "server", selectedTabbar == "node")
if teaconst.IsPlus {
tabbar.Add("集群看板", "", "/clusters/cluster/boards?clusterId="+clusterIdString, "board", selectedTabbar == "board")
}
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")
@@ -64,7 +76,7 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
secondMenuItem := action.Data.GetString("secondMenuItem")
switch selectedTabbar {
case "setting":
action.Data["leftMenuItems"] = this.createSettingMenu(cluster, secondMenuItem)
action.Data["leftMenuItems"] = this.createSettingMenu(cluster, clusterInfo, secondMenuItem)
}
}
@@ -72,7 +84,7 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
}
// 设置菜单
func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, selectedItem string) (items []maps.Map) {
func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, info *pb.FindEnabledNodeClusterConfigInfoResponse, selectedItem string) (items []maps.Map) {
clusterId := numberutils.FormatInt64(cluster.Id)
items = append(items, maps.Map{
"name": "基础设置",
@@ -92,25 +104,19 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, selectedIt
"isOn": cluster.HttpFirewallPolicyId > 0,
})
{
hasActions, _ := this.checkFirewallActions(cluster.Id)
items = append(items, maps.Map{
"name": "WAF动作",
"url": "/clusters/cluster/settings/firewall-actions?clusterId=" + clusterId,
"isActive": selectedItem == "firewallAction",
"isOn": hasActions,
})
}
items = append(items, maps.Map{
"name": "WAF动作",
"url": "/clusters/cluster/settings/firewall-actions?clusterId=" + clusterId,
"isActive": selectedItem == "firewallAction",
"isOn": info != nil && info.HasFirewallActions,
})
{
healthCheckIsOn, _ := this.checkHealthCheckIsOn(cluster.Id)
items = append(items, maps.Map{
"name": "健康检查",
"url": "/clusters/cluster/settings/health?clusterId=" + clusterId,
"isActive": selectedItem == "health",
"isOn": healthCheckIsOn,
})
}
items = append(items, maps.Map{
"name": "健康检查",
"url": "/clusters/cluster/settings/health?clusterId=" + clusterId,
"isActive": selectedItem == "health",
"isOn": info != nil && info.HealthCheckIsOn,
})
items = append(items, maps.Map{
"name": "DNS设置",
@@ -118,22 +124,26 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, selectedIt
"isActive": selectedItem == "dns",
"isOn": cluster.DnsDomainId > 0 || len(cluster.DnsName) > 0,
})
items = append(items, maps.Map{
"name": "统计指标",
"url": "/clusters/cluster/settings/metrics?clusterId=" + clusterId,
"isActive": selectedItem == "metric",
"isOn": info != nil && info.HasMetricItems,
})
if teaconst.IsPlus {
hasThresholds, _ := this.checkThresholds(cluster.Id)
items = append(items, maps.Map{
"name": "阈值设置",
"url": "/clusters/cluster/settings/thresholds?clusterId=" + clusterId,
"isActive": selectedItem == "threshold",
"isOn": hasThresholds,
"isOn": info != nil && info.HasThresholds,
})
}
if teaconst.IsPlus {
hasMessageReceivers, _ := this.checkMessages(cluster.Id)
items = append(items, maps.Map{
"name": "消息通知",
"url": "/clusters/cluster/settings/message?clusterId=" + clusterId,
"isActive": selectedItem == "message",
"isOn": hasMessageReceivers,
"isOn": info != nil && info.HasMessageReceivers,
})
}
@@ -148,75 +158,13 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, selectedIt
"url": "/clusters/cluster/settings/services?clusterId=" + clusterId,
"isActive": selectedItem == "service",
})
items = append(items, maps.Map{
"name": "TOA设置",
"url": "/clusters/cluster/settings/toa?clusterId=" + clusterId,
"isActive": selectedItem == "toa",
})
{
items = append(items, maps.Map{
"name": "TOA设置",
"url": "/clusters/cluster/settings/toa?clusterId=" + clusterId,
"isActive": selectedItem == "toa",
"isOn": info != nil && info.IsTOAEnabled,
})
}
return
}
// 检查健康检查是否开启
func (this *ClusterHelper) checkHealthCheckIsOn(clusterId int64) (bool, error) {
rpcClient, err := rpc.SharedRPC()
if err != nil {
return false, err
}
resp, err := rpcClient.NodeClusterRPC().FindNodeClusterHealthCheckConfig(rpcClient.Context(0), &pb.FindNodeClusterHealthCheckConfigRequest{NodeClusterId: clusterId})
if err != nil {
return false, err
}
if len(resp.HealthCheckJSON) > 0 {
healthCheckConfig := &serverconfigs.HealthCheckConfig{}
err = json.Unmarshal(resp.HealthCheckJSON, healthCheckConfig)
if err != nil {
return false, err
}
return healthCheckConfig.IsOn, nil
}
return false, nil
}
// 检查是否有WAF动作
func (this *ClusterHelper) checkFirewallActions(clusterId int64) (bool, error) {
rpcClient, err := rpc.SharedRPC()
if err != nil {
return false, err
}
resp, err := rpcClient.NodeClusterFirewallActionRPC().CountAllEnabledNodeClusterFirewallActions(rpcClient.Context(0), &pb.CountAllEnabledNodeClusterFirewallActionsRequest{NodeClusterId: clusterId})
if err != nil {
return false, err
}
return resp.Count > 0, nil
}
// 检查阈值是否已经设置
func (this *ClusterHelper) checkThresholds(clusterId int64) (bool, error) {
rpcClient, err := rpc.SharedRPC()
if err != nil {
return false, err
}
resp, err := rpcClient.NodeThresholdRPC().CountAllEnabledNodeThresholds(rpcClient.Context(0), &pb.CountAllEnabledNodeThresholdsRequest{
Role: "node",
NodeClusterId: clusterId,
})
if err != nil {
return false, err
}
return resp.Count > 0, nil
}
// 检查消息通知是否已经设置
func (this *ClusterHelper) checkMessages(clusterId int64) (bool, error) {
rpcClient, err := rpc.SharedRPC()
if err != nil {
return false, err
}
resp, err := rpcClient.MessageReceiverRPC().CountAllEnabledMessageReceivers(rpcClient.Context(0), &pb.CountAllEnabledMessageReceiversRequest{
NodeClusterId: clusterId,
})
if err != nil {
return false, err
}
return resp.Count > 0, nil
}

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

@@ -19,6 +19,7 @@ func (this *CreateAction) Init() {
}
func (this *CreateAction) RunGet(params struct{}) {
// 是否有域名
hasDomainsResp, err := this.RPC().DNSDomainRPC().ExistAvailableDomains(this.AdminContext(), &pb.ExistAvailableDomainsRequest{})
if err != nil {
this.ErrorPage(err)
@@ -53,13 +54,6 @@ func (this *CreateAction) RunPost(params struct {
Field("name", params.Name).
Require("请输入集群名称")
if params.CachePolicyId <= 0 {
this.Fail("请选择或者创建缓存策略")
}
if params.HttpFirewallPolicyId <= 0 {
this.Fail("请选择或者创建WAF策略")
}
// 检查DNS名称
if len(params.DnsName) > 0 {
if !domainutils.ValidateDomainFormat(params.DnsName) {
@@ -113,5 +107,7 @@ func (this *CreateAction) RunPost(params struct {
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "创建节点集群:%d", createResp.NodeClusterId)
this.Data["clusterId"] = createResp.NodeClusterId
this.Success()
}

View File

@@ -28,6 +28,7 @@ func (this *CreateAction) RunPost(params struct {
Username string
Password string
PrivateKey string
Passphrase string
Description string
Must *actions.Must
@@ -42,6 +43,9 @@ func (this *CreateAction) RunPost(params struct {
this.FailField("username", "请输入SSH登录用户名")
}
case "privateKey":
if len(params.Username) == 0 {
this.FailField("username", "请输入SSH登录用户名")
}
if len(params.PrivateKey) == 0 {
this.FailField("privateKey", "请输入RSA私钥")
}
@@ -55,6 +59,7 @@ func (this *CreateAction) RunPost(params struct {
Username: params.Username,
Password: params.Password,
PrivateKey: params.PrivateKey,
Passphrase: params.Passphrase,
Description: params.Description,
NodeId: 0,
})

View File

@@ -29,6 +29,7 @@ func (this *CreatePopupAction) RunPost(params struct {
Username string
Password string
PrivateKey string
Passphrase string
Description string
Must *actions.Must
@@ -43,6 +44,9 @@ func (this *CreatePopupAction) RunPost(params struct {
this.FailField("username", "请输入SSH登录用户名")
}
case "privateKey":
if len(params.Username) == 0 {
this.FailField("username", "请输入SSH登录用户名")
}
if len(params.PrivateKey) == 0 {
this.FailField("privateKey", "请输入RSA私钥")
}
@@ -56,6 +60,7 @@ func (this *CreatePopupAction) RunPost(params struct {
Username: params.Username,
Password: params.Password,
PrivateKey: params.PrivateKey,
Passphrase: params.Passphrase,
Description: params.Description,
NodeId: 0,
})

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

@@ -16,7 +16,10 @@ func (this *SelectPopupAction) Init() {
this.Nav("", "", "")
}
func (this *SelectPopupAction) RunGet(params struct{}) {
func (this *SelectPopupAction) RunGet(params struct {
NodeClusterId int64
NsClusterId int64
}) {
// 所有的认证
grantsResp, err := this.RPC().NodeGrantRPC().FindAllEnabledNodeGrants(this.AdminContext(), &pb.FindAllEnabledNodeGrantsRequest{})
if err != nil {
@@ -37,6 +40,28 @@ func (this *SelectPopupAction) RunGet(params struct{}) {
}
this.Data["grants"] = grantMaps
// 推荐的认证
suggestGrantsResp, err := this.RPC().NodeGrantRPC().FindSuggestNodeGrants(this.AdminContext(), &pb.FindSuggestNodeGrantsRequest{
NodeClusterId: params.NodeClusterId,
NsClusterId: params.NsClusterId,
})
if err != nil {
this.ErrorPage(err)
return
}
var suggestGrantMaps = []maps.Map{}
for _, grant := range suggestGrantsResp.NodeGrants {
suggestGrantMaps = append(suggestGrantMaps, maps.Map{
"id": grant.Id,
"name": grant.Name,
"method": grant.Method,
"methodName": grantutils.FindGrantMethodName(grant.Method),
"username": grant.Username,
"description": grant.Description,
})
}
this.Data["suggestGrants"] = suggestGrantMaps
this.Show()
}

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,6 +58,7 @@ func (this *UpdateAction) RunPost(params struct {
Username string
Password string
PrivateKey string
Passphrase string
Description string
Must *actions.Must
@@ -74,6 +76,9 @@ func (this *UpdateAction) RunPost(params struct {
this.FailField("username", "请输入SSH登录用户名")
}
case "privateKey":
if len(params.Username) == 0 {
this.FailField("username", "请输入SSH登录用户名")
}
if len(params.PrivateKey) == 0 {
this.FailField("privateKey", "请输入RSA私钥")
}
@@ -90,6 +95,7 @@ func (this *UpdateAction) RunPost(params struct {
Username: params.Username,
Password: params.Password,
PrivateKey: params.PrivateKey,
Passphrase: params.Passphrase,
Description: params.Description,
NodeId: 0,
})

View File

@@ -43,6 +43,7 @@ func (this *UpdatePopupAction) RunGet(params struct {
"password": grant.Password,
"description": grant.Description,
"privateKey": grant.PrivateKey,
"passphrase": grant.Passphrase,
}
this.Show()
@@ -56,6 +57,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
Username string
Password string
PrivateKey string
Passphrase string
Description string
Must *actions.Must
@@ -73,6 +75,9 @@ func (this *UpdatePopupAction) RunPost(params struct {
this.FailField("username", "请输入SSH登录用户名")
}
case "privateKey":
if len(params.Username) == 0 {
this.FailField("username", "请输入SSH登录用户名")
}
if len(params.PrivateKey) == 0 {
this.FailField("privateKey", "请输入RSA私钥")
}
@@ -88,6 +93,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
Username: params.Username,
Password: params.Password,
PrivateKey: params.PrivateKey,
Passphrase: params.Passphrase,
Description: params.Description,
NodeId: params.NodeId,
})

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