Compare commits

..

235 Commits

Author SHA1 Message Date
刘祥超
ca647d44bb 版本号修改为1.0.1 2023-04-10 09:17:37 +08:00
刘祥超
838b7dab5b Update Dockerfile 2023-04-10 08:48:09 +08:00
刘祥超
4f7fe247b4 更新components.js 2023-04-09 22:32:30 +08:00
刘祥超
cee20bd7d2 上传证书私钥时可以选择pem文件 2023-04-09 21:29:44 +08:00
刘祥超
f99ed0c4d9 优化grpc参数 2023-04-09 21:05:49 +08:00
刘祥超
4ae6523164 修复一个单词拼写错误 2023-04-09 20:09:12 +08:00
刘祥超
2ece764dcb 优化<combo-box>组件上下键盘键移动速度 2023-04-09 20:08:38 +08:00
刘祥超
f3bdd98af5 优化API升级处理提示文字 2023-04-09 17:29:29 +08:00
刘祥超
3c3c511c5b 版本号更改为1.0.0 2023-04-09 17:24:04 +08:00
刘祥超
45167b87e5 提交components.js 2023-04-09 17:23:51 +08:00
刘祥超
f7d5755744 提升Cookie安全性 2023-04-09 17:10:53 +08:00
刘祥超
e2adafd16b 服务配置菜单项增加配置代号 2023-04-09 15:59:37 +08:00
刘祥超
9d178b238d 审计日志列表增加级别筛选 2023-04-06 10:06:22 +08:00
刘祥超
431054a1be 自动检查管理员弱密码并提醒 2023-04-04 17:26:28 +08:00
刘祥超
5bf6428253 调整表单中注释颜色 2023-04-04 15:22:57 +08:00
刘祥超
6a4a03267b 优化文字提示 2023-04-03 10:03:48 +08:00
刘祥超
6598e16974 优化MySQL系统服务参数 2023-04-03 10:01:35 +08:00
刘祥超
b1943a4cec 优化MySQL系统服务参数 2023-04-02 18:05:54 +08:00
刘祥超
5f70d5afd3 优化文字提示 2023-04-02 10:17:56 +08:00
刘祥超
7dead36212 更新components.js 2023-04-02 10:17:47 +08:00
刘祥超
1f68a7830b 集群服务设置增加“支持低版本HTTP”选项 2023-04-01 09:51:44 +08:00
刘祥超
42c5b7a181 修复在未初始化缓存设置时添加缓存条件产生的panic错误 2023-03-31 17:42:33 +08:00
刘祥超
6596b47e54 证书即将到期提示 2023-03-28 16:52:21 +08:00
刘祥超
02e4a3e244 安装MySQL时指定 innodb_buffer_pool_size 为系统内存的一半 2023-03-28 11:42:19 +08:00
刘祥超
d9af90c76b 优化代码 2023-03-27 17:37:48 +08:00
刘祥超
a54405f24f 自动安装mysql时调整innodb_sort_buffer_size参数值 2023-03-27 17:22:13 +08:00
刘祥超
2b39c5d517 优化代码 2023-03-27 16:31:52 +08:00
刘祥超
a09d295948 优化<combo-box>组件 2023-03-27 11:28:06 +08:00
刘祥超
2c0b0be8c4 优化<combo-box>组件 2023-03-27 11:01:28 +08:00
刘祥超
b289877273 README中增加企业版链接 2023-03-26 20:50:45 +08:00
刘祥超
869d54b9a8 如果管理系统同时设置了HTTP和HTTPS端口,那么访问HTTP登录页时自动跳转到HTTPS地址 2023-03-26 15:54:14 +08:00
刘祥超
df48ae8316 优化缓存策略相关文字提示 2023-03-26 12:23:12 +08:00
刘祥超
e42cf2a420 创建服务和修改服务HTTPS设置时也支持批量上传证书 2023-03-26 12:00:41 +08:00
刘祥超
bd899b649d 创建服务时如果选择了所属用户,则证书列表中默认显示该用户的证书 2023-03-26 10:36:47 +08:00
刘祥超
2bc43ee2a5 实现自动匹配证书和批量选择证书功能 2023-03-25 20:50:27 +08:00
刘祥超
48e2907426 开源版本默认每次只能上传20个证书文件 2023-03-24 15:29:51 +08:00
刘祥超
4e33cce128 批量上传时如果证书文件和私钥文件不一致,也会提示错误 2023-03-24 15:13:28 +08:00
刘祥超
6658028f90 增加批量上传证书功能 2023-03-24 15:05:31 +08:00
刘祥超
3fe67bb179 优化系统设置--Web服务文字提示。 2023-03-24 10:53:40 +08:00
刘祥超
ce9a2d0cc3 证书内容输入框支持拖动文件上传 2023-03-24 10:17:05 +08:00
刘祥超
45b7c7af15 优化界面显示 2023-03-23 15:47:11 +08:00
刘祥超
3116aaeccb 节点列表节点IP超出4个后可以自动隐藏和显示 2023-03-23 14:50:12 +08:00
刘祥超
bfd5517c6c 优化创建网站服务界面 2023-03-22 17:52:22 +08:00
刘祥超
c83598a4f9 优化创建网站服务界面 2023-03-21 16:56:45 +08:00
刘祥超
3dc983871f 优化CNAME查询程序 2023-03-21 11:41:40 +08:00
刘祥超
ac6fcefffd 修改<columns-grid>组件文件名 2023-03-21 11:00:35 +08:00
刘祥超
094080cc9f 优化界面文字 2023-03-20 21:32:18 +08:00
刘祥超
d2dff968fb 自动安装MySQL时自动生成所需的动态库软链接 2023-03-20 21:32:10 +08:00
刘祥超
496f82a01f 日志数据库节点详情中密码使用星号(*)代替 2023-03-20 17:00:54 +08:00
刘祥超
9293c3e861 优化界面 2023-03-20 12:10:08 +08:00
刘祥超
da7d42edd5 修复<file-textarea>组件在firefox上无法复制内容的Bug 2023-03-20 09:43:21 +08:00
刘祥超
87b96d6526 SSH认证添加私钥时可以从私钥文件中直接拖入 2023-03-19 17:49:07 +08:00
刘祥超
a371821ff8 创建SSH认证私钥时校验私钥内容 2023-03-19 17:48:43 +08:00
刘祥超
748cb6eb8f 更新相关库 2023-03-19 17:46:28 +08:00
刘祥超
05bee642e9 配置初始化时增加context参数 2023-03-18 22:12:22 +08:00
刘祥超
081be04592 优化文字提示 2023-03-18 16:08:25 +08:00
刘祥超
f77518d086 生成节点DNS解析时区分节点是否已安装 2023-03-18 16:05:46 +08:00
刘祥超
35e6202b3c 创建节点时自动从节点名称中提取节点IP 2023-03-18 11:36:10 +08:00
刘祥超
1d84ea6ab9 更新components.js 2023-03-18 11:35:42 +08:00
刘祥超
df461d81d8 版本号更改为0.6.5 2023-03-17 15:53:01 +08:00
刘祥超
821d5aa595 优化节点列表连接数显示 2023-03-17 15:25:07 +08:00
刘祥超
c25bd71592 节点列表带宽保存两位小数数字 2023-03-17 15:20:53 +08:00
刘祥超
7e9224680e 当HTTP和HTTPS端口冲突时提示用户 2023-03-17 11:11:54 +08:00
刘祥超
bc89116d3d 编译脚本中暂时不删除.css.map和.js.map文件 2023-03-17 10:20:11 +08:00
刘祥超
aabe75ee13 优化文字提示 2023-03-17 10:19:38 +08:00
刘祥超
8903adc523 优化文字提示 2023-03-16 10:09:17 +08:00
刘祥超
61d8c8cd39 优化节点列表统计项宽度 2023-03-16 09:48:45 +08:00
刘祥超
2e1d991e0c 在开源版本看板增加检查是否为Plus的钩子 2023-03-16 09:37:16 +08:00
刘祥超
5a57167832 更新Dockerfile中的版本号 2023-03-16 09:27:36 +08:00
刘祥超
c3df181dcc 版本号改为0.6.4.2 2023-03-16 08:55:12 +08:00
刘祥超
699ea47ac5 更新components.js 2023-03-16 08:55:01 +08:00
刘祥超
fba69f3109 节点列表增加连接数列 2023-03-15 17:57:32 +08:00
刘祥超
063583dda1 调整节点任务和DNS同步任务弹窗高度 2023-03-15 16:24:14 +08:00
刘祥超
a31ca5cfb6 提高消息弹窗高度 2023-03-15 15:48:35 +08:00
刘祥超
c0a13305ad 管理系统增加两种背景风格 2023-03-15 15:35:11 +08:00
刘祥超
9d8cbf87dc URL跳转之域名跳转增加跳转后域名校验 2023-03-15 15:07:55 +08:00
刘祥超
009f7da26b 优化看板中的统计图表 2023-03-15 14:39:45 +08:00
刘祥超
f1d0359dc4 优化代码 2023-03-13 16:24:37 +08:00
刘祥超
473baa8c5b 版本号改为0.6.5 2023-03-13 16:16:26 +08:00
刘祥超
1a5dda6c72 Update Dockerfile 2023-03-13 12:12:44 +08:00
刘祥超
8a78dcb06e 版本号改为0.6.4.1 2023-03-13 10:36:56 +08:00
刘祥超
185768a80f 更新Dockerfile中的版本号 2023-03-13 09:15:23 +08:00
刘祥超
619a2817ce 检查版本更新时增加当前版本参数 2023-03-12 21:24:08 +08:00
刘祥超
12a33ee9fc 自动安装的MySQL binlog过期时间改成7天 2023-03-12 20:58:13 +08:00
刘祥超
26302ca930 重启edge-admin时确保同目录下的edge-api也能重启 2023-03-11 22:02:29 +08:00
刘祥超
7e85555ba7 安装过程中可以选择自动在本机安装MySQL 2023-03-11 18:52:40 +08:00
刘祥超
2546676f6a 删除页脚捐助作者链接 2023-03-11 14:44:19 +08:00
刘祥超
93b7edf5c4 安装过程中节点主机地址允许填写域名 2023-03-10 17:00:53 +08:00
刘祥超
571e432263 WAF cc防护增加“检查请求来源指纹”选项 2023-03-10 10:34:50 +08:00
刘祥超
580b158567 优化tips弹窗 2023-03-09 17:40:49 +08:00
刘祥超
395aa665d7 可以在运行日志页面中删除使用关键词匹配的运行日志 2023-03-09 16:20:09 +08:00
刘祥超
a80e54e0b3 优化<url-patterns-box>组件 2023-03-09 12:09:28 +08:00
刘祥超
8ccd41b551 优化路由规则标签、菜单、删除UAM组件 2023-03-09 11:42:15 +08:00
刘祥超
9d6a3a8a0d 优化<url-patterns-box>组件 2023-03-07 17:18:13 +08:00
刘祥超
a9d1b4b863 集群服务设置增加“记录找不到网站日志”选项 2023-03-07 10:30:11 +08:00
刘祥超
459d664a60 更新go.mod 2023-03-06 21:51:10 +08:00
刘祥超
5f10b0156c 5秒盾增加例外URL和限制URL 2023-03-06 21:48:29 +08:00
刘祥超
348c07f847 优化网站服务WAF设置界面 2023-03-06 16:40:42 +08:00
刘祥超
934b1894c4 使用edge-admin upgrade升级时可以通过--url参数指定升级包URL 2023-03-05 19:52:48 +08:00
刘祥超
a660f4af93 上传components.js 2023-03-05 19:51:46 +08:00
刘祥超
12c0d39b13 优化缓存条件设置界面 2023-03-05 16:48:01 +08:00
刘祥超
bc6de68006 远程升级API节点限制版本号必须不小于0.6.4 2023-03-05 15:32:27 +08:00
刘祥超
52bb753594 实现API节点远程升级 2023-03-05 12:05:18 +08:00
刘祥超
ed6b763d06 远程升级API节点(部分实现) 2023-03-04 21:04:30 +08:00
刘祥超
07e421afea 更新components.js 2023-03-04 09:34:17 +08:00
刘祥超
71352841bf 修复无法启动安装的Bug 2023-03-04 09:34:10 +08:00
刘祥超
91e8fcbb24 使用edge-admin upgrade升级系统时URL请求增加User-Agent 2023-03-03 10:44:04 +08:00
刘祥超
5b67a85624 WAF阻止动作也增加最大封禁时长 2023-03-01 19:18:22 +08:00
刘祥超
b49efa0d5a WAF拦截动作可以设置最大封禁时间,从而实现封禁时间随机 2023-03-01 18:59:42 +08:00
刘祥超
abeb585a0d 优化代码 2023-03-01 16:58:38 +08:00
刘祥超
cf621f1cc9 WAF支持忽略全局WAF规则 2023-03-01 16:46:49 +08:00
刘祥超
389a494e00 优化界面 2023-03-01 15:30:32 +08:00
刘祥超
a9c55dc23b 在节点列表中IP地址中显示对应的专属集群 2023-03-01 15:23:36 +08:00
刘祥超
3d35b7e71b 节点IP地址可以设置专属集群 2023-03-01 11:38:21 +08:00
刘祥超
6f78146711 可以修改单个用户的带宽算法 2023-02-27 10:46:48 +08:00
刘祥超
8afa47c351 删除不必要的文件 2023-02-22 19:25:38 +08:00
刘祥超
20838cfc3e 增加<js-page>组件 2023-02-22 17:34:32 +08:00
刘祥超
d00acd6d2f 上传日期相关公共函数 2023-02-18 20:45:05 +08:00
刘祥超
0b58a36779 优化界面 2023-02-17 10:30:25 +08:00
刘祥超
54bc98e9c1 优化组件显示 2023-02-13 09:44:10 +08:00
刘祥超
836daf2ad9 Dockerfile版本号修改为0.6.3 2023-02-10 18:17:57 +08:00
刘祥超
9a8cd9bd87 优化缓存请求条件界面 2023-02-10 11:15:06 +08:00
刘祥超
c555d91503 文件扩展名相关变量使用${requestPathLowerExtension} 2023-02-10 10:44:29 +08:00
刘祥超
e0e7c1bcc4 修复表单能够上传的数据过小问题 2023-02-07 11:40:28 +08:00
刘祥超
4bf733beec 在管理员登录后才验证OTP 2023-02-04 16:44:33 +08:00
刘祥超
e93f23c943 使用数据库存储SESSION 2023-02-04 15:18:26 +08:00
刘祥超
5384f4d9f2 优化WAF规则大小写敏感显示 2023-02-02 16:14:07 +08:00
刘祥超
3c0a97c3cc 修复防盗链设置中域名无法修改的Bug 2023-02-01 18:16:41 +08:00
刘祥超
129db6cf4e 修复无法显示IPv6最近日志的Bug 2023-02-01 10:36:30 +08:00
刘祥超
c6bfa5652f 版本号修改为0.6.4 2023-01-14 17:19:03 +08:00
刘祥超
780472d83e 更新components.js 2023-01-14 17:18:34 +08:00
刘祥超
0d02e3f15a 优化WAF创建规则界面中参数选项显示 2023-01-13 18:51:14 +08:00
刘祥超
bf82f22d0f WAF规则中如果对比值为空,则显示空字样 2023-01-13 15:58:35 +08:00
刘祥超
e18f182ce6 版本号修改为0.6.3 2023-01-11 15:45:03 +08:00
刘祥超
761c26b587 Update Dockerfile 2023-01-10 21:32:20 +08:00
刘祥超
5e62769dcf 修改版本为0.6.2 2023-01-10 21:17:20 +08:00
刘祥超
86b8a718a0 在Dockerfile中增加local repository提示 2023-01-10 18:31:28 +08:00
刘祥超
a729cfc31d 更新Dockerfile文件 2023-01-10 10:53:48 +08:00
刘祥超
96cfda852a Dockerfile版本号更新到0.6.0 2023-01-09 20:45:04 +08:00
刘祥超
0423d9246c 更新components.js 2023-01-09 18:12:45 +08:00
刘祥超
985798757f 修复<values-box>组件内容中如果出现数字的显示问题 2023-01-09 18:08:24 +08:00
刘祥超
72876f6749 版本修改为0.6.1 2023-01-09 16:06:13 +08:00
刘祥超
03d6e223d8 更新components.js 2023-01-09 09:31:19 +08:00
刘祥超
62d9f2ed97 优化文字提示 2023-01-08 19:38:50 +08:00
刘祥超
a550a44a52 集群服务设置增加自动读超时选项 2023-01-07 20:04:42 +08:00
刘祥超
b19d586949 更新components.js 2023-01-07 19:34:48 +08:00
刘祥超
bbfa3ee57f 优化任务Badge检查频率 2023-01-07 19:34:40 +08:00
刘祥超
af409dd3b8 优化WAF规则显示样式 2023-01-06 20:06:30 +08:00
刘祥超
3db79ca149 更新components.js 2023-01-06 19:17:31 +08:00
刘祥超
e880420494 WAF规则使用中文显示运算符 2023-01-06 19:12:53 +08:00
刘祥超
28ec17b8fe 优化请求条件操作符描述 2023-01-06 16:05:06 +08:00
刘祥超
8026a40807 修改一处错别字 2023-01-05 10:27:59 +08:00
刘祥超
068c6d406a useragent改为userAgent 2023-01-02 18:20:42 +08:00
刘祥超
57470e4ef0 集群服务设置中增加性能设置 2023-01-01 19:26:59 +08:00
刘祥超
ca8e1537f5 修复文字错误 2023-01-01 18:33:05 +08:00
刘祥超
d67b818398 华为云可以设置终端节点(endpoint) 2023-01-01 18:28:37 +08:00
刘祥超
f5f46424bb 集群/节点阈值切换监控项时同时切换参数描述 2022-12-31 18:33:42 +08:00
刘祥超
1e259717ce 优化代码 2022-12-31 17:31:17 +08:00
刘祥超
91ece99a9c 优化证书加载速度 2022-12-31 17:21:48 +08:00
刘祥超
d30ebdb369 优化证书数量很多时的页面加载速度 2022-12-31 17:12:49 +08:00
刘祥超
ade8522b69 实现UA名单功能 2022-12-30 20:48:38 +08:00
刘祥超
159b308f31 内容压缩增加默认内容长度限制 2022-12-30 14:37:50 +08:00
刘祥超
837bf25f7b 内容压缩支持例外扩展名 2022-12-30 12:04:17 +08:00
刘祥超
8301d3669b 默认情况下内容压缩不支持Partial Content 2022-12-30 11:43:47 +08:00
刘祥超
cc752e8d80 优化文字提示 2022-12-29 17:19:53 +08:00
刘祥超
d20e6bd42f 增加CORS自适应跨域 2022-12-29 17:16:07 +08:00
刘祥超
bfee9fe233 优化文字提示 2022-12-27 18:55:18 +08:00
刘祥超
9c962b09f1 调整文字提示 2022-12-22 11:43:31 +08:00
刘祥超
46edefead7 服务没有设置所属用户时可以设置一个用户 2022-12-21 16:13:39 +08:00
刘祥超
725cfc8a2b 优化代码 2022-12-15 16:18:10 +08:00
刘祥超
1ce48b9ef4 安全设置中检查搜索引擎和爬虫时不区分大小写 2022-12-13 18:22:11 +08:00
刘祥超
37cc28f225 优化界面 2022-12-13 18:21:31 +08:00
刘祥超
5ebe3bb8e0 实时访问日志有弹窗打开时,暂时不更新数据 2022-12-10 19:01:57 +08:00
刘祥超
aa01512f89 优化<more-options-indicator>组件 2022-12-10 15:57:39 +08:00
刘祥超
37ff2b886a 初步完成用户电子邮箱绑定(激活) 2022-12-08 20:25:20 +08:00
刘祥超
ce18212756 自定义线路增加区域之间关系设置 2022-12-06 22:32:32 +08:00
刘祥超
08f50a274a 修复HTTPS之HSTS设置无法手动输入有效时间的Bug 2022-12-06 14:38:59 +08:00
刘祥超
892ee0013a 修复点击修改WAF规则时未保存时也会生效的Bug 2022-12-06 10:46:46 +08:00
刘祥超
e9a47041fd 刷新预热页面增加功能说明 2022-12-06 09:52:57 +08:00
刘祥超
d419fa06e8 优化文字提示 2022-12-03 14:29:30 +08:00
刘祥超
8b961a890c 优化文字提示 2022-12-01 14:40:17 +08:00
刘祥超
db32915114 更新components.js 2022-11-29 15:40:37 +08:00
刘祥超
2ffdb10cce 版本号更新为0.6.0 2022-11-29 15:40:23 +08:00
刘祥超
507fd7e5d4 更新Dockerfile中的版本号 2022-11-29 15:40:11 +08:00
刘祥超
7df599b5a9 DNS线路选择器增加代号搜索 2022-11-28 19:00:14 +08:00
刘祥超
9987334f55 版本号修改为0.5.10 2022-11-28 18:59:40 +08:00
刘祥超
d8c3365384 节点时间相差30秒钟以上才提示 2022-11-28 15:57:41 +08:00
刘祥超
2e284b5af9 版本号修改为0.5.9 2022-11-28 15:57:18 +08:00
刘祥超
89ddd4e6a3 更新依赖库 2022-11-28 11:39:38 +08:00
刘祥超
36524ea481 修复一处测试用例package引用错误 2022-11-28 11:39:08 +08:00
刘祥超
35cf693610 更新Dockerfile 2022-11-28 11:34:02 +08:00
刘祥超
42148a66bd 改进文字提示 2022-11-27 22:00:35 +08:00
刘祥超
96878715bf 优化界面 2022-11-26 19:04:55 +08:00
刘祥超
1a5f3342e7 优化文字提示 2022-11-26 15:28:01 +08:00
刘祥超
3613d13a2b 优化文字说明 2022-11-25 19:05:11 +08:00
刘祥超
7786140d85 优化代码 2022-11-24 17:20:08 +08:00
刘祥超
3a23b57f1b 优化智能DNS访问日志组件 2022-11-24 10:24:04 +08:00
刘祥超
5a6e6fba69 节点详情显示API连接状况 2022-11-22 11:27:48 +08:00
刘祥超
910b3a6162 在节点详情中显示API节点地址 2022-11-21 21:07:51 +08:00
刘祥超
0dc19bed45 节点可以单独设置所使用的API节点地址 2022-11-21 19:54:54 +08:00
刘祥超
a7bdb64301 记录名第一段允许通配符 2022-11-20 20:08:03 +08:00
刘祥超
4739072a85 Update go.mod 2022-11-18 17:37:25 +08:00
刘祥超
07bdae2488 安装时检测本地数据库时增加更多的候选密码 2022-11-18 17:07:12 +08:00
刘祥超
b84035d821 优化代码 2022-11-18 17:02:38 +08:00
刘祥超
dc0a7b9dae 优化文字提示 2022-11-18 15:45:13 +08:00
刘祥超
2937bd8de0 提交components.js 2022-11-17 17:34:21 +08:00
刘祥超
37315ef4d9 优化代码 2022-11-17 10:41:56 +08:00
刘祥超
1986fece07 优化代码 2022-11-17 10:40:57 +08:00
刘祥超
16b1657f35 优化WAF规则相关界面 2022-11-16 15:43:08 +08:00
刘祥超
c115c62cd9 去掉服务CNAME末尾的点符号,防止误解 2022-11-16 14:44:07 +08:00
刘祥超
d2df7f8d5b 节点列表页带宽使用bps显示 2022-11-16 12:02:56 +08:00
刘祥超
6cb79864e6 边缘节点支持设置多个缓存目录 2022-11-15 20:35:45 +08:00
刘祥超
982d28c7b4 docker build增加--no-cache参数 2022-11-14 15:01:13 +08:00
刘祥超
0f57516fdc Dockerfile: 使用alpine:latest代替alpine:edge 2022-11-14 14:49:19 +08:00
刘祥超
75a89defcb 构建Dockerfile时从官网下载最新版本安装包 2022-11-14 10:01:22 +08:00
刘祥超
22a6c52060 增加Docker镜像构建脚本 2022-11-13 18:46:30 +08:00
刘祥超
37607e4a41 安装过程显示更详细内容 2022-11-11 21:47:36 +08:00
刘祥超
5936155998 优化文字提示 2022-11-11 17:46:10 +08:00
刘祥超
8d76de935f 优化文字提示 2022-11-10 15:07:15 +08:00
刘祥超
9baa530064 优化文字提示 2022-11-09 18:20:18 +08:00
刘祥超
103414b338 优化<checkbox>组件 2022-11-09 17:50:22 +08:00
刘祥超
72fe68ebfe 访问日志搜索method:XXX和requestMethod:XXX方法 2022-11-09 11:58:39 +08:00
刘祥超
cfed31958b 优化文字提示 2022-11-08 09:22:09 +08:00
刘祥超
3d5fca2d36 调整默认压缩的mime types 2022-11-08 09:18:13 +08:00
刘祥超
a5710286ec 优化代码 2022-11-06 20:34:19 +08:00
刘祥超
d0ce0c6c58 优化界面 2022-11-06 15:16:21 +08:00
刘祥超
779e2cf0f2 域名跳转增加忽略跳转前端口选项 2022-11-04 20:59:20 +08:00
刘祥超
2108474777 删除不必要的文件 2022-11-04 15:19:30 +08:00
刘祥超
e25e0f1747 删除不必要的文件 2022-11-04 15:01:36 +08:00
刘祥超
e8e74b639c 优化datepicker组件 2022-11-04 14:35:32 +08:00
刘祥超
a14fcd1e50 添加和修改API节点时,检查HTTP API端口是否和GRPC端口冲突 2022-11-04 12:04:07 +08:00
刘祥超
485c0e0891 修复系统用户和平台用户access key无法禁用和删除的问题 2022-11-04 11:06:45 +08:00
刘祥超
00a19e9d43 时钟同步增加是否检查chrony选项 2022-11-03 14:58:32 +08:00
刘祥超
67d0dc0783 版本修改为0.5.8 2022-11-02 15:10:47 +08:00
刘祥超
3718c35842 提交components.js 2022-11-02 15:10:39 +08:00
374 changed files with 10120 additions and 2288 deletions

View File

@@ -56,5 +56,8 @@
## 联系我们
有什么问题和建议都可以加入QQ群 `659832182` 或者 [Telegram群](https://t.me/+5kVCMGxQhZxiODY9) 。
## 企业版
* [GoEdge企业版](https://goedge.cn/commercial) - 功能更强大的CDN系统
## 感谢
* 感谢 [Gitee](https://gitee.com/) 提供国内源代码托管平台

View File

@@ -105,8 +105,8 @@ function build() {
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
#find "$DIST" -name "*.css.map" -delete
#find "$DIST" -name "*.js.map" -delete
# zip
echo "zip files ..."

View File

@@ -2,6 +2,7 @@ package main
import (
"bytes"
"flag"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/apps"
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
@@ -24,7 +25,7 @@ func main() {
var app = apps.NewAppCmd().
Version(teaconst.Version).
Product(teaconst.ProductName).
Usage(teaconst.ProcessName+" [-v|start|stop|restart|service|daemon|reset|recover|demo|upgrade]").
Usage(teaconst.ProcessName+" [-h|-v|start|stop|restart|service|daemon|reset|recover|demo|upgrade]").
Usage(teaconst.ProcessName+" [dev|prod]").
Option("-h", "show this help").
Option("-v", "show version").
@@ -38,7 +39,7 @@ func main() {
Option("demo", "switch to demo mode").
Option("dev", "switch to 'dev' mode").
Option("prod", "switch to 'prod' mode").
Option("upgrade", "upgrade from official site")
Option("upgrade [--url=URL]", "upgrade from official site or an url")
app.On("daemon", func() {
nodes.NewAdminNode().Daemon()
@@ -138,7 +139,12 @@ func main() {
}
})
app.On("upgrade", func() {
var manager = utils.NewUpgradeManager("admin")
var downloadURL = ""
var flagSet = flag.NewFlagSet("", flag.ContinueOnError)
flagSet.StringVar(&downloadURL, "url", "", "new version download url")
_ = flagSet.Parse(os.Args[2:])
var manager = utils.NewUpgradeManager("admin", downloadURL)
log.Println("checking latest version ...")
var ticker = time.NewTicker(1 * time.Second)
go func() {

1
docker/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.zip

37
docker/Dockerfile Normal file
View File

@@ -0,0 +1,37 @@
FROM alpine:latest
LABEL maintainer="iwind.liu@gmail.com"
ENV TZ "Asia/Shanghai"
ENV VERSION 1.0.0
ENV ROOT_DIR /usr/local/goedge
ENV TAR_FILE edge-admin-linux-amd64-plus-v${VERSION}.zip
ENV TAR_URL "https://dl.goedge.cn/edge/v${VERSION}/edge-admin-linux-amd64-plus-v${VERSION}.zip"
#ENV TAR_URL "http://192.168.2.60:8080/edge-admin-linux-amd64-plus-v${VERSION}.zip" # your local repository
RUN apk add --no-cache tzdata
RUN apk add wget
RUN mkdir ${ROOT_DIR}; \
cd ${ROOT_DIR}; \
wget ${TAR_URL} -O ${TAR_FILE}; \
apk add unzip; \
unzip ${TAR_FILE}; \
rm -f ${TAR_FILE}
RUN apk add mysql mysql-client; \
sed -e "s/\[mysqld\]/\[mysqld\]\n\ndatadir=\/var\/lib\/mysql\nport=3306\ninnodb_flush_log_at_trx_commit=2\nmax_connections=256\nmax_prepared_stmt_count=65535\nbinlog_cache_size=1M\nbinlog_stmt_cache_size=1M\nthread_cache_size=32\nbinlog_expire_logs_seconds=1209600\n\n/" /etc/my.cnf > /tmp/my.cnf; \
cp /tmp/my.cnf /etc/my.cnf; \
sed -e "s/skip-networking/#skip-networking/" /etc/my.cnf.d/mariadb-server.cnf > /tmp/mariadb-server.cnf; \
cp /tmp/mariadb-server.cnf /etc/my.cnf.d/mariadb-server.cnf; \
mysql_install_db --user=mysql
RUN mysqld_safe --user=mysql & \
sleep 5; \
mysql -uroot -hlocalhost --execute="ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';"
RUN echo -e "#!/usr/bin/env sh\n\nmysqld_safe --user=mysql &\n/usr/local/goedge/edge-admin/bin/edge-admin\n" > ${ROOT_DIR}/run.sh; \
chmod u+x ${ROOT_DIR}/run.sh
EXPOSE 7788
EXPOSE 8001
EXPOSE 3306
ENTRYPOINT [ "/usr/local/goedge/run.sh" ]

5
docker/build.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/usr/bin/env bash
VERSION=latest
docker build --no-cache -t goedge/edge-admin:${VERSION} .

5
docker/run.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/usr/bin/env bash
VERSION=latest
docker run -d -p 7788:7788 -p 8001:8001 -p 3306:3306 --name edge-admin goedge/edge-admin:${VERSION}

14
go.mod
View File

@@ -8,16 +8,17 @@ require (
github.com/TeaOSLab/EdgeCommon v0.0.0-00010101000000-000000000000
github.com/cespare/xxhash v1.1.0
github.com/go-sql-driver/mysql v1.5.0
github.com/iwind/TeaGo v0.0.0-20220304043459-0dd944a5b475
github.com/iwind/TeaGo v0.0.0-20230304012706-c1f4a4e27470
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4
github.com/miekg/dns v1.1.43
github.com/shirou/gopsutil/v3 v3.22.5
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/tealeg/xlsx/v3 v3.2.3
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
golang.org/x/crypto v0.7.0
golang.org/x/sys v0.6.0
google.golang.org/grpc v1.45.0
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
gopkg.in/yaml.v3 v3.0.1
)
require (
@@ -26,18 +27,15 @@ require (
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/btree v1.0.0 // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kr/pretty v0.2.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/rogpeppe/fastuuid v1.2.0 // indirect
github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/text v0.8.0 // indirect
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect

37
go.sum
View File

@@ -21,7 +21,6 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200624174652-8d2f3be8b2d9/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -74,13 +73,12 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iwind/TeaGo v0.0.0-20220304043459-0dd944a5b475 h1:EseyfFaQOjWanGiby9KMw7PjDBMg/95tLDgIw/ns0Cw=
github.com/iwind/TeaGo v0.0.0-20220304043459-0dd944a5b475/go.mod h1:HRHK0zoC/og3c9/hKosD9yYVMTnnzm3PgXUdhRYHaLc=
github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/TeaGo v0.0.0-20230304012706-c1f4a4e27470 h1:TuRxvKRv9PxKVijWOkUnZm5TeanQqWGUJyPx9u6cra4=
github.com/iwind/TeaGo v0.0.0-20230304012706-c1f4a4e27470/go.mod h1:fi/Pq+/5m2HZoseM+39dMF57ANXRt6w4PkGu3NXPc5s=
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4 h1:VWGsCqTzObdlbf7UUE3oceIpcEKi4C/YBUszQXk118A=
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@@ -92,11 +90,7 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
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/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/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -111,7 +105,6 @@ github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+v
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/profile v1.5.0 h1:042Buzk+NhDI+DeSAA62RwJL8VAuZUMQZUjCsRz1Mug=
github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
@@ -131,7 +124,6 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tealeg/xlsx/v3 v3.2.3 h1:MXnVh+9Y8cUglowItTy2HL3Kv6z+q/0aNjeKuTsVqZQ=
github.com/tealeg/xlsx/v3 v3.2.3/go.mod h1:0hGmAEoZ48SS1ZAE6eqZJkJVXgOMY+8a33vjXa8S8HA=
@@ -146,6 +138,8 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
@@ -170,8 +164,8 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
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-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -198,20 +192,17 @@ golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7w
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-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -266,7 +257,7 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -209,7 +209,7 @@ func (this *AppCmd) runStop() {
fmt.Println(this.product+" stopped ok, pid:", types.String(pid))
}
// 重启
// RunRestart 重启
func (this *AppCmd) RunRestart() {
this.runStop()
time.Sleep(1 * time.Second)

View File

@@ -1,96 +0,0 @@
package configloaders
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/iwind/TeaGo/logs"
"reflect"
)
var sharedUserUIConfig *systemconfigs.UserUIConfig = nil
const (
UserUISettingName = "userUIConfig"
)
func LoadUserUIConfig() (*systemconfigs.UserUIConfig, error) {
locker.Lock()
defer locker.Unlock()
config, err := loadUserUIConfig()
if err != nil {
return nil, err
}
v := reflect.Indirect(reflect.ValueOf(config)).Interface().(systemconfigs.UserUIConfig)
return &v, nil
}
func UpdateUserUIConfig(uiConfig *systemconfigs.UserUIConfig) error {
locker.Lock()
defer locker.Unlock()
var rpcClient, err = rpc.SharedRPC()
if err != nil {
return err
}
valueJSON, err := json.Marshal(uiConfig)
if err != nil {
return err
}
_, err = rpcClient.SysSettingRPC().UpdateSysSetting(rpcClient.Context(0), &pb.UpdateSysSettingRequest{
Code: UserUISettingName,
ValueJSON: valueJSON,
})
if err != nil {
return err
}
sharedUserUIConfig = uiConfig
return nil
}
func loadUserUIConfig() (*systemconfigs.UserUIConfig, error) {
if sharedUserUIConfig != nil {
return sharedUserUIConfig, nil
}
var rpcClient, err = rpc.SharedRPC()
if err != nil {
return nil, err
}
resp, err := rpcClient.SysSettingRPC().ReadSysSetting(rpcClient.Context(0), &pb.ReadSysSettingRequest{
Code: UserUISettingName,
})
if err != nil {
return nil, err
}
if len(resp.ValueJSON) == 0 {
sharedUserUIConfig = defaultUserUIConfig()
return sharedUserUIConfig, nil
}
config := &systemconfigs.UserUIConfig{}
err = json.Unmarshal(resp.ValueJSON, config)
if err != nil {
logs.Println("[UI_MANAGER]" + err.Error())
sharedUserUIConfig = defaultUserUIConfig()
return sharedUserUIConfig, nil
}
sharedUserUIConfig = config
return sharedUserUIConfig, nil
}
func defaultUserUIConfig() *systemconfigs.UserUIConfig {
return &systemconfigs.UserUIConfig{
ProductName: "GoEdge",
UserSystemName: "GoEdge用户系统",
ShowOpenSourceInfo: true,
ShowVersion: true,
ShowFinance: true,
BandwidthUnit: systemconfigs.BandwidthUnitBit,
ShowBandwidthCharts: true,
ShowTrafficCharts: true,
}
}

View File

@@ -21,9 +21,9 @@ type APIConfig struct {
// LoadAPIConfig 加载API配置
func LoadAPIConfig() (*APIConfig, error) {
// 候选文件
localFile := Tea.ConfigFile("api.yaml")
isFromLocal := false
paths := []string{localFile}
var localFile = Tea.ConfigFile("api.yaml")
var isFromLocal = false
var paths = []string{localFile}
homeDir, homeErr := os.UserHomeDir()
if homeErr == nil {
paths = append(paths, homeDir+"/."+teaconst.ProcessName+"/api.yaml")
@@ -45,7 +45,7 @@ func LoadAPIConfig() (*APIConfig, error) {
return nil, err
}
config := &APIConfig{}
var config = &APIConfig{}
err = yaml.Unmarshal(data, config)
if err != nil {
return nil, err
@@ -155,3 +155,15 @@ func (this *APIConfig) WriteFile(path string) error {
return nil
}
// Clone 克隆当前配置
func (this *APIConfig) Clone() *APIConfig {
return &APIConfig{
RPC: struct {
Endpoints []string `yaml:"endpoints"`
DisableUpdate bool `yaml:"disableUpdate"`
}{},
NodeId: this.NodeId,
Secret: this.Secret,
}
}

View File

@@ -1,42 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package configs
import (
"encoding/json"
"github.com/iwind/TeaGo/Tea"
"os"
)
var plusConfigFile = "plus.cache.json"
type PlusConfig struct {
IsPlus bool `json:"isPlus"`
Components []string `json:"components"`
DayTo string `json:"dayTo"`
}
func ReadPlusConfig() *PlusConfig {
data, err := os.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 = os.WriteFile(Tea.ConfigFile(plusConfigFile), configJSON, 0777)
if err != nil {
return err
}
return nil
}

View File

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

View File

@@ -2,6 +2,11 @@
package teaconst
import (
"os"
"strings"
)
var (
IsRecoverMode = false
@@ -10,4 +15,18 @@ var (
NewVersionCode = "" // 有新的版本
NewVersionDownloadURL = "" // 新版本下载地址
IsMain = checkMain()
)
// 检查是否为主程序
func checkMain() bool {
if len(os.Args) == 1 ||
(len(os.Args) >= 2 && os.Args[1] == "pprof") {
return true
}
exe, _ := os.Executable()
return strings.HasSuffix(exe, ".test") ||
strings.HasSuffix(exe, ".test.exe") ||
strings.Contains(exe, "___")
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/conds/condutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/files"
@@ -115,6 +116,16 @@ func generateComponentsJSFile() error {
buffer.Write([]byte{'\n', '\n'})
}
// WAF操作符
wafOperatorsJSON, err := json.Marshal(firewallconfigs.AllRuleOperators)
if err != nil {
logs.Println("ComponentsAction marshal waf rule operators failed: " + err.Error())
} else {
buffer.WriteString("window.WAF_RULE_OPERATORS = ")
buffer.Write(wafOperatorsJSON)
buffer.Write([]byte{'\n', '\n'})
}
fp, err := os.OpenFile(filepath.Clean(Tea.PublicFile("/js/components.src.js")), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0777)
if err != nil {
return err

View File

@@ -12,7 +12,6 @@ import (
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/sessions"
"github.com/iwind/TeaGo/types"
"github.com/iwind/gosock/pkg/gosock"
"gopkg.in/yaml.v3"
@@ -21,6 +20,8 @@ import (
"os"
"os/exec"
"os/signal"
"path/filepath"
"strings"
"syscall"
"time"
)
@@ -85,10 +86,15 @@ func (this *AdminNode) Run() {
this.startAPINode()
// 启动Web服务
sessionManager, err := NewSessionManager()
if err != nil {
log.Fatal("start session failed: " + err.Error())
return
}
TeaGo.NewServer(false).
AccessLog(false).
EndAll().
Session(sessions.NewFileSessionManager(86400, secret), teaconst.CookieSID).
Session(sessionManager, teaconst.CookieSID).
ReadHeaderTimeout(3 * time.Second).
ReadTimeout(1200 * time.Second).
Start()
@@ -360,6 +366,16 @@ func (this *AdminNode) listenSock() error {
}
}
// 停止当前目录下的API节点
var apiSock = gosock.NewTmpSock("edge-api")
apiReply, err := apiSock.Send(&gosock.Command{Code: "info"})
if err == nil {
adminExe, _ := os.Executable()
if len(adminExe) > 0 && apiReply != nil && strings.HasPrefix(maps.NewMap(apiReply.Params).GetString("path"), filepath.Dir(filepath.Dir(adminExe))) {
_, _ = apiSock.Send(&gosock.Command{Code: "stop"})
}
}
// 退出主进程
events.Notify(events.EventQuit)
os.Exit(0)

View File

@@ -0,0 +1,97 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package nodes
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/logs"
"strings"
)
// SessionManager SESSION管理
type SessionManager struct {
life uint
}
func NewSessionManager() (*SessionManager, error) {
return &SessionManager{}, nil
}
func (this *SessionManager) Init(config *actions.SessionConfig) {
this.life = config.Life
}
func (this *SessionManager) Read(sid string) map[string]string {
// 忽略OTP
if strings.HasSuffix(sid, "_otp") {
return map[string]string{}
}
var result = map[string]string{}
rpcClient, err := rpc.SharedRPC()
if err != nil {
return map[string]string{}
}
resp, err := rpcClient.LoginSessionRPC().FindLoginSession(rpcClient.Context(0), &pb.FindLoginSessionRequest{Sid: sid})
if err != nil {
logs.Println("SESSION", "read '"+sid+"' failed: "+err.Error())
return result
}
var session = resp.LoginSession
if session == nil || len(session.ValuesJSON) == 0 {
return result
}
err = json.Unmarshal(session.ValuesJSON, &result)
if err != nil {
logs.Println("SESSION", "decode '"+sid+"' values failed: "+err.Error())
}
return result
}
func (this *SessionManager) WriteItem(sid string, key string, value string) bool {
// 忽略OTP
if strings.HasSuffix(sid, "_otp") {
return false
}
rpcClient, err := rpc.SharedRPC()
if err != nil {
return false
}
_, err = rpcClient.LoginSessionRPC().WriteLoginSessionValue(rpcClient.Context(0), &pb.WriteLoginSessionValueRequest{
Sid: sid,
Key: key,
Value: value,
})
if err != nil {
logs.Println("SESSION", "write sid:'"+sid+"' key:'"+key+"' failed: "+err.Error())
}
return true
}
func (this *SessionManager) Delete(sid string) bool {
// 忽略OTP
if strings.HasSuffix(sid, "_otp") {
return false
}
rpcClient, err := rpc.SharedRPC()
if err != nil {
return false
}
_, err = rpcClient.LoginSessionRPC().DeleteLoginSession(rpcClient.Context(0), &pb.DeleteLoginSessionRequest{Sid: sid})
if err != nil {
logs.Println("SESSION", "delete '"+sid+"' failed: "+err.Error())
}
return true
}

View File

@@ -407,6 +407,10 @@ func (this *RPCClient) LoginRPC() pb.LoginServiceClient {
return pb.NewLoginServiceClient(this.pickConn())
}
func (this *RPCClient) LoginSessionRPC() pb.LoginSessionServiceClient {
return pb.NewLoginSessionServiceClient(this.pickConn())
}
func (this *RPCClient) NodeTaskRPC() pb.NodeTaskServiceClient {
return pb.NewNodeTaskServiceClient(this.pickConn())
}
@@ -540,8 +544,11 @@ func (this *RPCClient) init() error {
}
var conn *grpc.ClientConn
var callOptions = grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(128*1024*1024),
grpc.UseCompressor(gzip.Name))
var callOptions = grpc.WithDefaultCallOptions(
grpc.MaxCallRecvMsgSize(128<<20),
grpc.MaxCallSendMsgSize(128<<20),
grpc.UseCompressor(gzip.Name),
)
if u.Scheme == "http" {
conn, err = grpc.Dial(apiHost, grpc.WithTransportCredentials(insecure.NewCredentials()), callOptions)
} else if u.Scheme == "https" {
@@ -571,41 +578,30 @@ func (this *RPCClient) pickConn() *grpc.ClientConn {
defer this.locker.Unlock()
// 检查连接状态
if len(this.conns) > 0 {
var availableConns = []*grpc.ClientConn{}
for _, state := range []connectivity.State{connectivity.Ready, connectivity.Idle, connectivity.Connecting} {
var countConns = len(this.conns)
if countConns > 0 {
if countConns == 1 {
return this.conns[0]
}
for _, state := range []connectivity.State{
connectivity.Ready,
connectivity.Idle,
connectivity.Connecting,
connectivity.TransientFailure,
} {
var availableConns = []*grpc.ClientConn{}
for _, conn := range this.conns {
if conn.GetState() == state {
availableConns = append(availableConns, conn)
}
}
if len(availableConns) > 0 {
break
return this.randConn(availableConns)
}
}
if len(availableConns) > 0 {
return availableConns[rands.Int(0, len(availableConns)-1)]
}
// 关闭
for _, conn := range this.conns {
_ = conn.Close()
}
}
// 重新初始化
err := this.init()
if err != nil {
// 错误提示已经在构造对象时打印过,所以这里不再重复打印
return nil
}
if len(this.conns) == 0 {
return nil
}
return this.conns[rands.Int(0, len(this.conns)-1)]
return this.randConn(this.conns)
}
// Close 关闭
@@ -639,3 +635,14 @@ func (this *RPCClient) localIPAddrs() []string {
}
return localIPAddrs
}
func (this *RPCClient) randConn(conns []*grpc.ClientConn) *grpc.ClientConn {
var l = len(conns)
if l == 0 {
return nil
}
if l == 1 {
return conns[0]
}
return conns[rands.Int(0, l-1)]
}

View File

@@ -87,6 +87,7 @@ func (this *CheckUpdatesTask) Loop() error {
var apiURL = teaconst.UpdatesURL
apiURL = strings.ReplaceAll(apiURL, "${os}", runtime.GOOS)
apiURL = strings.ReplaceAll(apiURL, "${arch}", runtime.GOARCH)
apiURL = strings.ReplaceAll(apiURL, "${version}", teaconst.Version)
resp, err := http.Get(apiURL)
if err != nil {
return errors.New("read api failed: " + err.Error())

View File

@@ -0,0 +1,30 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package apinodeutils
var SharedManager = NewManager()
type Manager struct {
upgraderMap map[int64]*Upgrader
}
func NewManager() *Manager {
return &Manager{
upgraderMap: map[int64]*Upgrader{},
}
}
func (this *Manager) AddUpgrader(upgrader *Upgrader) {
this.upgraderMap[upgrader.apiNodeId] = upgrader
}
func (this *Manager) FindUpgrader(apiNodeId int64) *Upgrader {
return this.upgraderMap[apiNodeId]
}
func (this *Manager) RemoveUpgrader(upgrader *Upgrader) {
if upgrader == nil {
return
}
delete(this.upgraderMap, upgrader.apiNodeId)
}

View File

@@ -0,0 +1,201 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package apinodeutils
import (
"compress/gzip"
"crypto/md5"
"errors"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/types"
stringutil "github.com/iwind/TeaGo/utils/string"
"io"
"os"
"path/filepath"
"runtime"
)
type Progress struct {
Percent float64
}
type Upgrader struct {
progress *Progress
apiExe string
apiNodeId int64
}
func NewUpgrader(apiNodeId int64) *Upgrader {
return &Upgrader{
apiExe: apiExe(),
progress: &Progress{Percent: 0},
apiNodeId: apiNodeId,
}
}
func (this *Upgrader) Upgrade() error {
sharedClient, err := rpc.SharedRPC()
if err != nil {
return err
}
apiNodeResp, err := sharedClient.APINodeRPC().FindEnabledAPINode(sharedClient.Context(0), &pb.FindEnabledAPINodeRequest{ApiNodeId: this.apiNodeId})
if err != nil {
return err
}
var apiNode = apiNodeResp.ApiNode
if apiNode == nil {
return errors.New("could not find api node with id '" + types.String(this.apiNodeId) + "'")
}
apiConfig, err := configs.LoadAPIConfig()
if err != nil {
return err
}
var newAPIConfig = apiConfig.Clone()
newAPIConfig.RPC.Endpoints = apiNode.AccessAddrs
rpcClient, err := rpc.NewRPCClient(newAPIConfig, false)
if err != nil {
return err
}
versionResp, err := rpcClient.APINodeRPC().FindCurrentAPINodeVersion(sharedClient.Context(0), &pb.FindCurrentAPINodeVersionRequest{})
if err != nil {
return err
}
if !Tea.IsTesting() /** 开发环境下允许突破此限制方便测试 **/ &&
(stringutil.VersionCompare(versionResp.Version, "0.6.4" /** 从0.6.4开始支持 **/) < 0 || versionResp.Os != runtime.GOOS || versionResp.Arch != runtime.GOARCH) {
return errors.New("could not upgrade api node v" + versionResp.Version + "/" + versionResp.Os + "/" + versionResp.Arch)
}
// 检查本地文件版本
canUpgrade, reason := CanUpgrade(versionResp.Version, versionResp.Os, versionResp.Arch)
if !canUpgrade {
return errors.New(reason)
}
localVersion, err := localVersion()
if err != nil {
return errors.New("lookup version failed: " + err.Error())
}
// 检查要升级的文件
var gzFile = this.apiExe + "." + localVersion + ".gz"
gzReader, err := os.Open(gzFile)
if err != nil {
if !os.IsNotExist(err) {
return err
}
err = func() error {
// 压缩文件
exeReader, err := os.Open(this.apiExe)
if err != nil {
return err
}
defer func() {
_ = exeReader.Close()
}()
var tmpGzFile = gzFile + ".tmp"
gzFileWriter, err := os.OpenFile(tmpGzFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
if err != nil {
return err
}
var gzWriter = gzip.NewWriter(gzFileWriter)
defer func() {
_ = gzWriter.Close()
_ = gzFileWriter.Close()
_ = os.Rename(tmpGzFile, gzFile)
}()
_, err = io.Copy(gzWriter, exeReader)
if err != nil {
return err
}
return nil
}()
if err != nil {
return err
}
gzReader, err = os.Open(gzFile)
if err != nil {
return err
}
}
defer func() {
_ = gzReader.Close()
}()
// 开始上传
var hash = md5.New()
var buf = make([]byte, 128*4096)
var isFirst = true
stat, err := gzReader.Stat()
if err != nil {
return err
}
var totalSize = stat.Size()
if totalSize == 0 {
_ = gzReader.Close()
_ = os.Remove(gzFile)
return errors.New("invalid gz file")
}
var uploadedSize int64 = 0
for {
n, err := gzReader.Read(buf)
if n > 0 {
// 计算Hash
hash.Write(buf[:n])
// 上传
_, uploadErr := rpcClient.APINodeRPC().UploadAPINodeFile(rpcClient.Context(0), &pb.UploadAPINodeFileRequest{
Filename: filepath.Base(this.apiExe),
Sum: "",
ChunkData: buf[:n],
IsFirstChunk: isFirst,
IsLastChunk: false,
})
if uploadErr != nil {
return uploadErr
}
// 进度
uploadedSize += int64(n)
this.progress = &Progress{Percent: float64(uploadedSize*100) / float64(totalSize)}
}
if isFirst {
isFirst = false
}
if err != nil {
if err != io.EOF {
return err
}
if err == io.EOF {
_, uploadErr := rpcClient.APINodeRPC().UploadAPINodeFile(rpcClient.Context(0), &pb.UploadAPINodeFileRequest{
Filename: filepath.Base(this.apiExe),
Sum: fmt.Sprintf("%x", hash.Sum(nil)),
ChunkData: buf[:n],
IsFirstChunk: isFirst,
IsLastChunk: true,
})
if uploadErr != nil {
return uploadErr
}
break
}
}
}
return nil
}
func (this *Upgrader) Progress() *Progress {
return this.progress
}

View File

@@ -0,0 +1,22 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package apinodeutils_test
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils/apinodeutils"
_ "github.com/iwind/TeaGo/bootstrap"
"runtime"
"testing"
)
func TestUpgrader_CanUpgrade(t *testing.T) {
t.Log(apinodeutils.CanUpgrade("0.6.3", runtime.GOOS, runtime.GOARCH))
}
func TestUpgrader_Upgrade(t *testing.T) {
var upgrader = apinodeutils.NewUpgrader(1)
err := upgrader.Upgrade()
if err != nil {
t.Fatal(err)
}
}

View File

@@ -0,0 +1,80 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package apinodeutils
import (
"bytes"
"errors"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/iwind/TeaGo/Tea"
stringutil "github.com/iwind/TeaGo/utils/string"
"os"
"os/exec"
"regexp"
"runtime"
"strings"
)
func CanUpgrade(apiVersion string, osName string, arch string) (canUpgrade bool, reason string) {
if len(apiVersion) == 0 {
return false, "current api version should not be empty"
}
if stringutil.VersionCompare(apiVersion, "0.6.4") < 0 {
return false, "api node version must greater than or equal to 0.6.4"
}
if osName != runtime.GOOS {
return false, "os not match: " + osName
}
if arch != runtime.GOARCH {
return false, "arch not match: " + arch
}
stat, err := os.Stat(apiExe())
if err != nil {
return false, "stat error: " + err.Error()
}
if stat.IsDir() {
return false, "is directory"
}
localVersion, err := localVersion()
if err != nil {
return false, "lookup version failed: " + err.Error()
}
if localVersion != teaconst.APINodeVersion {
return false, "not newest api node"
}
if stringutil.VersionCompare(localVersion, apiVersion) <= 0 {
return false, "need not upgrade, local '" + localVersion + "' vs remote '" + apiVersion + "'"
}
return true, ""
}
func localVersion() (string, error) {
var cmd = exec.Command(apiExe(), "-V")
var output = &bytes.Buffer{}
cmd.Stdout = output
err := cmd.Run()
if err != nil {
return "", err
}
var localVersion = strings.TrimSpace(output.String())
// 检查版本号
var reg = regexp.MustCompile(`^[\d.]+$`)
if !reg.MatchString(localVersion) {
return "", errors.New("lookup version failed: " + localVersion)
}
return localVersion, nil
}
func apiExe() string {
return Tea.Root + "/edge-api/bin/edge-api"
}

View File

@@ -0,0 +1,12 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package dateutils
// SplitYmd 分隔Ymd格式的日期
// Ymd => Y-m-d
func SplitYmd(day string) string {
if len(day) != 8 {
return day
}
return day[:4] + "-" + day[4:6] + "-" + day[6:]
}

View File

@@ -3,7 +3,7 @@
package utils_test
import (
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/iwind/TeaGo/assert"
"testing"
)

View File

@@ -1,26 +1,40 @@
package utils
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/iwind/TeaGo/logs"
"github.com/miekg/dns"
)
// LookupCNAME 获取CNAME
func LookupCNAME(host string) (string, error) {
config, err := dns.ClientConfigFromFile("/etc/resolv.conf")
if err != nil {
return "", err
var sharedDNSClient *dns.Client
var sharedDNSConfig *dns.ClientConfig
func init() {
if !teaconst.IsMain {
return
}
c := new(dns.Client)
m := new(dns.Msg)
config, err := dns.ClientConfigFromFile("/etc/resolv.conf")
if err != nil {
logs.Println("ERROR: configure dns client failed: " + err.Error())
return
}
sharedDNSConfig = config
sharedDNSClient = &dns.Client{}
}
// LookupCNAME 获取CNAME
func LookupCNAME(host string) (string, error) {
var m = new(dns.Msg)
m.SetQuestion(host+".", dns.TypeCNAME)
m.RecursionDesired = true
var lastErr error
for _, serverAddr := range config.Servers {
r, _, err := c.Exchange(m, configutils.QuoteIP(serverAddr)+":"+config.Port)
for _, serverAddr := range sharedDNSConfig.Servers {
r, _, err := sharedDNSClient.Exchange(m, configutils.QuoteIP(serverAddr)+":"+sharedDNSConfig.Port)
if err != nil {
lastErr = err
continue

View File

@@ -0,0 +1,12 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package utils_test
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"testing"
)
func TestLookupCNAME(t *testing.T) {
t.Log(utils.LookupCNAME("www.yun4s.cn"))
}

View File

@@ -128,7 +128,7 @@ After=network-online.target
[Service]
Type=simple
Restart=always
RestartSec=1s
RestartSec=5s
ExecStart=` + exePath + ` daemon
ExecStop=` + exePath + ` stop
ExecReload=` + exePath + ` reload

55
internal/utils/time.go Normal file
View File

@@ -0,0 +1,55 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package utils
import (
"errors"
"fmt"
"github.com/iwind/TeaGo/types"
"regexp"
)
// RangeTimes 计算时间点
func RangeTimes(timeFrom string, timeTo string, everyMinutes int32) (result []string, err error) {
if everyMinutes <= 0 {
return nil, errors.New("invalid 'everyMinutes'")
}
var reg = regexp.MustCompile(`^\d{4}$`)
if !reg.MatchString(timeFrom) {
return nil, errors.New("invalid timeFrom '" + timeFrom + "'")
}
if !reg.MatchString(timeTo) {
return nil, errors.New("invalid timeTo '" + timeTo + "'")
}
if timeFrom > timeTo {
// swap
timeFrom, timeTo = timeTo, timeFrom
}
var everyMinutesInt = int(everyMinutes)
var fromHour = types.Int(timeFrom[:2])
var fromMinute = types.Int(timeFrom[2:])
var toHour = types.Int(timeTo[:2])
var toMinute = types.Int(timeTo[2:])
if fromMinute%everyMinutesInt == 0 {
result = append(result, timeFrom)
}
for {
fromMinute += everyMinutesInt
if fromMinute > 59 {
fromHour += fromMinute / 60
fromMinute = fromMinute % 60
}
if fromHour > toHour || (fromHour == toHour && fromMinute > toMinute) {
break
}
result = append(result, fmt.Sprintf("%02d%02d", fromHour, fromMinute))
}
return
}

View File

@@ -52,11 +52,14 @@ type UpgradeManager struct {
writer *UpgradeFileWriter
body io.ReadCloser
isCancelled bool
downloadURL string
}
func NewUpgradeManager(component string) *UpgradeManager {
func NewUpgradeManager(component string, downloadURL string) *UpgradeManager {
return &UpgradeManager{
component: component,
component: component,
downloadURL: downloadURL,
client: &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
@@ -96,8 +99,8 @@ func (this *UpgradeManager) Start() error {
}
// 检查新版本
var downloadURL = ""
{
var downloadURL = this.downloadURL
if len(downloadURL) == 0 {
var url = teaconst.UpdatesURL
var osName = runtime.GOOS
if Tea.IsTesting() && osName == "darwin" {
@@ -105,10 +108,12 @@ func (this *UpgradeManager) Start() error {
}
url = strings.ReplaceAll(url, "${os}", osName)
url = strings.ReplaceAll(url, "${arch}", runtime.GOARCH)
url = strings.ReplaceAll(url, "${version}", teaconst.Version)
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return errors.New("create url request failed: " + err.Error())
}
req.Header.Set("User-Agent", "Edge-Admin/"+teaconst.Version)
resp, err := this.client.Do(req)
if err != nil {
@@ -169,6 +174,7 @@ func (this *UpgradeManager) Start() error {
if err != nil {
return errors.New("create download request failed: " + err.Error())
}
req.Header.Set("User-Agent", "Edge-Admin/"+teaconst.Version)
resp, err := this.client.Do(req)
if err != nil {

View File

@@ -9,7 +9,7 @@ import (
)
func TestNewUpgradeManager(t *testing.T) {
var manager = utils.NewUpgradeManager("admin")
var manager = utils.NewUpgradeManager("admin", "")
var ticker = time.NewTicker(2 * time.Second)
go func() {

View File

@@ -49,7 +49,11 @@ func (this *ParentAction) ErrorText(err string) {
}
func (this *ParentAction) NotFound(name string, itemId int64) {
this.ErrorPage(errors.New(name + " id: '" + strconv.FormatInt(itemId, 10) + "' is not found"))
if itemId > 0 {
this.ErrorPage(errors.New(name + " id: '" + strconv.FormatInt(itemId, 10) + "' is not found"))
} else {
this.ErrorPage(errors.New(name + " is not found"))
}
}
func (this *ParentAction) NewPage(total int64, size ...int64) *Page {
@@ -126,11 +130,8 @@ func (this *ParentAction) RPC() *rpc.RPCClient {
}
// AdminContext 获取Context
// 每个请求的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 {

View File

@@ -102,7 +102,7 @@ func FailPage(action actions.ActionWrapper, err error) {
var html = `<!DOCTYPE html>
<html>
<head>
<title>有系统错误需要处理</title>
<title>正在处理...</title>
<meta charset="UTF-8"/>
<style type="text/css">
hr { border-top: 1px #ccc solid; }
@@ -116,7 +116,7 @@ func FailPage(action actions.ActionWrapper, err error) {
html += "<div class=\"red\">API节点正在启动请耐心等待完成"
if len(apiNodeProgress) > 0 {
html += "" + apiNodeProgress
html += "" + apiNodeProgress + "(刷新当前页面查看最新状态)"
}
html += "</div>"

View File

@@ -45,6 +45,7 @@ func (this *AdminAction) RunGet(params struct {
"isOn": admin.IsOn,
"isSuper": admin.IsSuper,
"canLogin": admin.CanLogin,
"hasWeakPassword": admin.HasWeakPassword,
"countAccessKeys": countAccessKeys,
}

View File

@@ -15,34 +15,46 @@ func (this *IndexAction) Init() {
this.Nav("", "", "")
}
func (this *IndexAction) RunGet(params struct{}) {
countResp, err := this.RPC().AdminRPC().CountAllEnabledAdmins(this.AdminContext(), &pb.CountAllEnabledAdminsRequest{})
if err != nil {
this.ErrorPage(err)
return
}
page := this.NewPage(countResp.Count)
this.Data["page"] = page.AsHTML()
func (this *IndexAction) RunGet(params struct {
Keyword string
HasWeakPassword bool
}) {
this.Data["keyword"] = params.Keyword
this.Data["hasWeakPassword"] = params.HasWeakPassword
adminsResp, err := this.RPC().AdminRPC().ListEnabledAdmins(this.AdminContext(), &pb.ListEnabledAdminsRequest{
Offset: page.Offset,
Size: page.Size,
countResp, err := this.RPC().AdminRPC().CountAllEnabledAdmins(this.AdminContext(), &pb.CountAllEnabledAdminsRequest{
Keyword: params.Keyword,
HasWeakPassword: params.HasWeakPassword,
})
if err != nil {
this.ErrorPage(err)
return
}
adminMaps := []maps.Map{}
var page = this.NewPage(countResp.Count)
this.Data["page"] = page.AsHTML()
adminsResp, err := this.RPC().AdminRPC().ListEnabledAdmins(this.AdminContext(), &pb.ListEnabledAdminsRequest{
Keyword: params.Keyword,
HasWeakPassword: params.HasWeakPassword,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var adminMaps = []maps.Map{}
for _, admin := range adminsResp.Admins {
adminMaps = append(adminMaps, maps.Map{
"id": admin.Id,
"isOn": admin.IsOn,
"isSuper": admin.IsSuper,
"username": admin.Username,
"fullname": admin.Fullname,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", admin.CreatedAt),
"otpLoginIsOn": admin.OtpLogin != nil && admin.OtpLogin.IsOn,
"canLogin": admin.CanLogin,
"id": admin.Id,
"isOn": admin.IsOn,
"isSuper": admin.IsSuper,
"username": admin.Username,
"fullname": admin.Fullname,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", admin.CreatedAt),
"otpLoginIsOn": admin.OtpLogin != nil && admin.OtpLogin.IsOn,
"canLogin": admin.CanLogin,
"hasWeakPassword": admin.HasWeakPassword,
})
}
this.Data["admins"] = adminMaps

View File

@@ -22,7 +22,7 @@ func init() {
Post("/options", new(OptionsAction)).
// AccessKeys
Prefix("/admins/accessKeys").
Prefix("/admins/accesskeys").
Get("", new(accesskeys.IndexAction)).
GetPost("/createPopup", new(accesskeys.CreatePopupAction)).
Post("/delete", new(accesskeys.DeleteAction)).

View File

@@ -3,12 +3,15 @@ package api
import (
"encoding/json"
"fmt"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/apinodeutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
stringutil "github.com/iwind/TeaGo/utils/string"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
@@ -44,7 +47,7 @@ func (this *IndexAction) RunGet(params struct{}) {
for _, node := range nodesResp.ApiNodes {
// 状态
status := &nodeconfigs.NodeStatus{}
var status = &nodeconfigs.NodeStatus{}
if len(node.StatusJSON) > 0 {
err = json.Unmarshal(node.StatusJSON, &status)
if err != nil {
@@ -55,7 +58,7 @@ func (this *IndexAction) RunGet(params struct{}) {
}
// Rest地址
restAccessAddrs := []string{}
var restAccessAddrs = []string{}
if node.RestIsOn {
if len(node.RestHTTPJSON) > 0 {
httpConfig := &serverconfigs.HTTPProtocolConfig{}
@@ -79,13 +82,16 @@ func (this *IndexAction) RunGet(params struct{}) {
this.ErrorPage(err)
return
}
_ = httpsConfig.Init()
_ = httpsConfig.Init(nil)
if httpsConfig.IsOn && len(httpsConfig.Listen) > 0 {
restAccessAddrs = append(restAccessAddrs, httpsConfig.FullAddresses()...)
}
}
}
var shouldUpgrade = status.IsActive && len(status.BuildVersion) > 0 && stringutil.VersionCompare(teaconst.APINodeVersion, status.BuildVersion) > 0
canUpgrade, _ := apinodeutils.CanUpgrade(status.BuildVersion, status.OS, status.Arch)
nodeMaps = append(nodeMaps, maps.Map{
"id": node.Id,
"isOn": node.IsOn,
@@ -94,14 +100,17 @@ func (this *IndexAction) RunGet(params struct{}) {
"restAccessAddrs": restAccessAddrs,
"isPrimary": node.IsPrimary,
"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),
"buildVersion": status.BuildVersion,
"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),
"buildVersion": status.BuildVersion,
"latestVersion": teaconst.APINodeVersion,
"shouldUpgrade": shouldUpgrade,
"canUpgrade": shouldUpgrade && canUpgrade,
},
})
}

View File

@@ -4,10 +4,12 @@ import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
)
type CreatePopupAction struct {
@@ -40,11 +42,11 @@ func (this *CreatePopupAction) RunPost(params struct {
Field("name", params.Name).
Require("请输入API节点名称")
httpConfig := &serverconfigs.HTTPProtocolConfig{}
httpsConfig := &serverconfigs.HTTPSProtocolConfig{}
var httpConfig = &serverconfigs.HTTPProtocolConfig{}
var httpsConfig = &serverconfigs.HTTPSProtocolConfig{}
// 监听地址
listens := []*serverconfigs.NetworkAddressConfig{}
var listens = []*serverconfigs.NetworkAddressConfig{}
err := json.Unmarshal(params.ListensJSON, &listens)
if err != nil {
this.ErrorPage(err)
@@ -64,15 +66,19 @@ func (this *CreatePopupAction) RunPost(params struct {
}
// Rest监听地址
restHTTPConfig := &serverconfigs.HTTPProtocolConfig{}
restHTTPSConfig := &serverconfigs.HTTPSProtocolConfig{}
var restHTTPConfig = &serverconfigs.HTTPProtocolConfig{}
var restHTTPSConfig = &serverconfigs.HTTPSProtocolConfig{}
if params.RestIsOn {
restListens := []*serverconfigs.NetworkAddressConfig{}
var restListens = []*serverconfigs.NetworkAddressConfig{}
err = json.Unmarshal(params.RestListensJSON, &restListens)
if err != nil {
this.ErrorPage(err)
return
}
if len(restListens) == 0 {
this.Fail("请至少添加一个HTTP API监听端口")
return
}
for _, addr := range restListens {
if addr.Protocol.IsHTTPFamily() {
restHTTPConfig.IsOn = true
@@ -82,10 +88,35 @@ func (this *CreatePopupAction) RunPost(params struct {
restHTTPSConfig.Listen = append(restHTTPSConfig.Listen, addr)
}
}
// 是否有端口冲突
var rpcAddresses = []string{}
for _, listen := range listens {
err := listen.Init()
if err != nil {
this.Fail("校验配置失败:" + configutils.QuoteIP(listen.Host) + ":" + listen.PortRange + ": " + err.Error())
return
}
rpcAddresses = append(rpcAddresses, listen.Addresses()...)
}
for _, listen := range restListens {
err := listen.Init()
if err != nil {
this.Fail("校验配置失败:" + configutils.QuoteIP(listen.Host) + ":" + listen.PortRange + ": " + err.Error())
return
}
for _, address := range listen.Addresses() {
if lists.ContainsString(rpcAddresses, address) {
this.Fail("HTTP API地址 '" + address + "' 和 GRPC地址冲突请修改后提交")
return
}
}
}
}
// 证书
certIds := []int64{}
var certIds = []int64{}
if len(params.CertIdsJSON) > 0 {
err = json.Unmarshal(params.CertIdsJSON, &certIds)
if err != nil {
@@ -97,7 +128,7 @@ func (this *CreatePopupAction) RunPost(params struct {
this.Fail("请添加至少一个证书")
}
certRefs := []*sslconfigs.SSLCertRef{}
var certRefs = []*sslconfigs.SSLCertRef{}
for _, certId := range certIds {
certRefs = append(certRefs, &sslconfigs.SSLCertRef{
IsOn: true,
@@ -131,7 +162,7 @@ func (this *CreatePopupAction) RunPost(params struct {
}
// 访问地址
accessAddrs := []*serverconfigs.NetworkAddressConfig{}
var accessAddrs = []*serverconfigs.NetworkAddressConfig{}
err = json.Unmarshal(params.AccessAddrsJSON, &accessAddrs)
if err != nil {
this.ErrorPage(err)

View File

@@ -65,7 +65,10 @@ func (this *IndexAction) RunGet(params struct {
// 证书信息
var certs = []*sslconfigs.SSLCertConfig{}
if httpsConfig.SSLPolicyRef != nil && httpsConfig.SSLPolicyRef.SSLPolicyId > 0 {
sslPolicyConfigResp, err := this.RPC().SSLPolicyRPC().FindEnabledSSLPolicyConfig(this.AdminContext(), &pb.FindEnabledSSLPolicyConfigRequest{SslPolicyId: httpsConfig.SSLPolicyRef.SSLPolicyId})
sslPolicyConfigResp, err := this.RPC().SSLPolicyRPC().FindEnabledSSLPolicyConfig(this.AdminContext(), &pb.FindEnabledSSLPolicyConfigRequest{
SslPolicyId: httpsConfig.SSLPolicyRef.SSLPolicyId,
IgnoreData: true,
})
if err != nil {
this.ErrorPage(err)
return

View File

@@ -24,7 +24,10 @@ func init() {
GetPost("/update", new(UpdateAction)).
Get("/install", new(InstallAction)).
Get("/logs", new(LogsAction)).
GetPost("/upgradePopup", new(UpgradePopupAction)).
Post("/upgradeCheck", new(UpgradeCheckAction)).
//
EndAll()
})
}

View File

@@ -4,10 +4,12 @@ import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
)
@@ -83,7 +85,10 @@ func (this *UpdateAction) RunGet(params struct {
var certs = []*sslconfigs.SSLCertConfig{}
var sslPolicyId = int64(0)
if httpsConfig.SSLPolicyRef != nil && httpsConfig.SSLPolicyRef.SSLPolicyId > 0 {
sslPolicyConfigResp, err := this.RPC().SSLPolicyRPC().FindEnabledSSLPolicyConfig(this.AdminContext(), &pb.FindEnabledSSLPolicyConfigRequest{SslPolicyId: httpsConfig.SSLPolicyRef.SSLPolicyId})
sslPolicyConfigResp, err := this.RPC().SSLPolicyRPC().FindEnabledSSLPolicyConfig(this.AdminContext(), &pb.FindEnabledSSLPolicyConfigRequest{
SslPolicyId: httpsConfig.SSLPolicyRef.SSLPolicyId,
IgnoreData: true,
})
if err != nil {
this.ErrorPage(err)
return
@@ -175,12 +180,16 @@ func (this *UpdateAction) RunPost(params struct {
var restHTTPConfig = &serverconfigs.HTTPProtocolConfig{}
var restHTTPSConfig = &serverconfigs.HTTPSProtocolConfig{}
if params.RestIsOn {
restListens := []*serverconfigs.NetworkAddressConfig{}
var restListens = []*serverconfigs.NetworkAddressConfig{}
err = json.Unmarshal(params.RestListensJSON, &restListens)
if err != nil {
this.ErrorPage(err)
return
}
if len(restListens) == 0 {
this.Fail("请至少添加一个HTTP API监听端口")
return
}
for _, addr := range restListens {
if addr.Protocol.IsHTTPFamily() {
restHTTPConfig.IsOn = true
@@ -190,6 +199,31 @@ func (this *UpdateAction) RunPost(params struct {
restHTTPSConfig.Listen = append(restHTTPSConfig.Listen, addr)
}
}
// 是否有端口冲突
var rpcAddresses = []string{}
for _, listen := range listens {
err := listen.Init()
if err != nil {
this.Fail("校验配置失败:" + configutils.QuoteIP(listen.Host) + ":" + listen.PortRange + ": " + err.Error())
return
}
rpcAddresses = append(rpcAddresses, listen.Addresses()...)
}
for _, listen := range restListens {
err := listen.Init()
if err != nil {
this.Fail("校验配置失败:" + configutils.QuoteIP(listen.Host) + ":" + listen.PortRange + ": " + err.Error())
return
}
for _, address := range listen.Addresses() {
if lists.ContainsString(rpcAddresses, address) {
this.Fail("HTTP API地址 '" + address + "' 和 GRPC地址冲突请修改后提交")
return
}
}
}
}
// 证书

View File

@@ -0,0 +1,67 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package node
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
// UpgradeCheckAction 检查升级结果
type UpgradeCheckAction struct {
actionutils.ParentAction
}
func (this *UpgradeCheckAction) Init() {
this.Nav("", "", "")
}
func (this *UpgradeCheckAction) RunPost(params struct {
NodeId int64
}) {
this.Data["isOk"] = false
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
if err != nil {
this.Success()
return
}
var node = nodeResp.ApiNode
if node == nil || len(node.AccessAddrs) == 0 {
this.Success()
return
}
apiConfig, err := configs.LoadAPIConfig()
if err != nil {
this.Success()
return
}
var newAPIConfig = apiConfig.Clone()
newAPIConfig.RPC.Endpoints = node.AccessAddrs
rpcClient, err := rpc.NewRPCClient(newAPIConfig, false)
if err != nil {
this.Success()
return
}
versionResp, err := rpcClient.APINodeRPC().FindCurrentAPINodeVersion(rpcClient.Context(0), &pb.FindCurrentAPINodeVersionRequest{})
if err != nil {
this.Success()
return
}
if versionResp.Version != teaconst.Version {
this.Success()
return
}
this.Data["isOk"] = true
this.Success()
}

View File

@@ -0,0 +1,124 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package node
import (
"encoding/json"
"errors"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/apinodeutils"
"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/actions"
"strings"
)
type UpgradePopupAction struct {
actionutils.ParentAction
}
func (this *UpgradePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpgradePopupAction) RunGet(params struct {
NodeId int64
}) {
this.Data["nodeId"] = params.NodeId
this.Data["nodeName"] = ""
this.Data["currentVersion"] = ""
this.Data["latestVersion"] = ""
this.Data["result"] = ""
this.Data["resultIsOk"] = true
this.Data["canUpgrade"] = false
this.Data["isUpgrading"] = false
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
var node = nodeResp.ApiNode
if node == nil {
this.Data["result"] = "要升级的节点不存在"
this.Data["resultIsOk"] = false
this.Show()
return
}
this.Data["nodeName"] = node.Name + " / [" + strings.Join(node.AccessAddrs, ", ") + "]"
// 节点状态
var status = &nodeconfigs.NodeStatus{}
if len(node.StatusJSON) > 0 {
err = json.Unmarshal(node.StatusJSON, &status)
if err != nil {
this.ErrorPage(errors.New("decode status failed: " + err.Error()))
return
}
this.Data["currentVersion"] = status.BuildVersion
} else {
this.Data["result"] = "无法检测到节点当前版本"
this.Data["resultIsOk"] = false
this.Show()
return
}
this.Data["latestVersion"] = teaconst.APINodeVersion
if status.IsActive && len(status.BuildVersion) > 0 {
canUpgrade, reason := apinodeutils.CanUpgrade(status.BuildVersion, status.OS, status.Arch)
if !canUpgrade {
this.Data["result"] = reason
this.Data["resultIsOk"] = false
this.Show()
return
}
this.Data["canUpgrade"] = true
this.Data["result"] = "等待升级"
this.Data["resultIsOk"] = true
} else {
this.Data["result"] = "当前节点非连接状态无法远程升级"
this.Data["resultIsOk"] = false
this.Show()
return
}
// 是否正在升级
var oldUpgrader = apinodeutils.SharedManager.FindUpgrader(params.NodeId)
if oldUpgrader != nil {
this.Data["result"] = "正在升级中..."
this.Data["resultIsOk"] = false
this.Data["isUpgrading"] = true
}
this.Show()
}
func (this *UpgradePopupAction) RunPost(params struct {
NodeId int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var manager = apinodeutils.SharedManager
var oldUpgrader = manager.FindUpgrader(params.NodeId)
if oldUpgrader != nil {
this.Fail("正在升级中,无需重复提交 ...")
return
}
var upgrader = apinodeutils.NewUpgrader(params.NodeId)
manager.AddUpgrader(upgrader)
defer func() {
manager.RemoveUpgrader(upgrader)
}()
err := upgrader.Upgrade()
if err != nil {
this.Fail("升级失败:" + err.Error())
return
}
this.Success()
}

View File

@@ -10,6 +10,7 @@ import (
"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"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
@@ -88,7 +89,7 @@ func (this *DetailAction) RunGet(params struct {
return
}
var ipAddresses = ipAddressesResp.NodeIPAddresses
ipAddressMaps := []maps.Map{}
var ipAddressMaps = []maps.Map{}
for _, addr := range ipAddressesResp.NodeIPAddresses {
thresholds, err := ipaddressutils.InitNodeIPAddressThresholds(this.Parent(), addr.Id)
if err != nil {
@@ -102,6 +103,15 @@ func (this *DetailAction) RunGet(params struct {
addr.Ip = addr.BackupIP
}
// 专属集群
var addrClusterMaps = []maps.Map{}
for _, addrCluster := range addr.NodeClusters {
addrClusterMaps = append(addrClusterMaps, maps.Map{
"id": addrCluster.Id,
"name": addrCluster.Name,
})
}
ipAddressMaps = append(ipAddressMaps, maps.Map{
"id": addr.Id,
"name": addr.Name,
@@ -110,6 +120,7 @@ func (this *DetailAction) RunGet(params struct {
"canAccess": addr.CanAccess,
"isOn": addr.IsOn,
"isUp": addr.IsUp,
"clusters": addrClusterMaps,
"thresholds": thresholds,
})
}
@@ -151,16 +162,31 @@ func (this *DetailAction) RunGet(params struct {
if !addr.CanAccess || !addr.IsUp || !addr.IsOn {
continue
}
// 过滤集群
if len(addr.NodeClusters) > 0 {
var inCluster = false
for _, addrCluster := range addr.NodeClusters {
if addrCluster.Id == cluster.Id {
inCluster = true
}
}
if !inCluster {
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,
"name": dnsInfo.NodeClusterDNSName + "." + domainName,
"type": recordType,
"route": route.Name,
"value": addr.Ip,
"clusterName": cluster.Name,
})
}
}
@@ -178,8 +204,8 @@ func (this *DetailAction) RunGet(params struct {
}
}
grantMap := maps.Map{}
grantId := loginParams.GetInt64("grantId")
var grantMap = maps.Map{}
var grantId = loginParams.GetInt64("grantId")
if grantId > 0 {
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: grantId})
if err != nil {
@@ -300,6 +326,22 @@ func (this *DetailAction) RunGet(params struct {
lnAddrs = []string{}
}
// API节点地址
var apiNodeAddrStrings = []string{}
var apiNodeAddrs = []*serverconfigs.NetworkAddressConfig{}
if len(node.ApiNodeAddrsJSON) > 0 {
err = json.Unmarshal(node.ApiNodeAddrsJSON, &apiNodeAddrs)
if err != nil {
this.ErrorPage(err)
return
}
for _, addr := range apiNodeAddrs {
if addr.Init() == nil {
apiNodeAddrStrings = append(apiNodeAddrStrings, addr.FullAddresses()...)
}
}
}
this.Data["node"] = maps.Map{
"id": node.Id,
"name": node.Name,
@@ -319,6 +361,7 @@ func (this *DetailAction) RunGet(params struct {
"levelInfo": nodeconfigs.FindNodeLevel(int(node.Level)),
"lnAddrs": lnAddrs,
"enableIPLists": node.EnableIPLists,
"apiNodeAddrs": apiNodeAddrStrings,
"status": maps.Map{
"isActive": status.IsActive,
@@ -338,6 +381,8 @@ func (this *DetailAction) RunGet(params struct {
"cacheTotalDiskSize": numberutils.FormatBytes(status.CacheTotalDiskSize),
"cacheTotalMemorySize": numberutils.FormatBytes(status.CacheTotalMemorySize),
"exePath": status.ExePath,
"apiSuccessPercent": status.APISuccessPercent,
"apiAvgCostSeconds": status.APIAvgCostSeconds,
},
"group": groupMap,

View File

@@ -7,6 +7,7 @@ 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/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
@@ -57,9 +58,19 @@ func (this *IndexAction) RunGet(params struct {
}
}
var diskSubDirs = []*serverconfigs.CacheDir{}
if len(node.CacheDiskSubDirsJSON) > 0 {
err = json.Unmarshal(node.CacheDiskSubDirsJSON, &diskSubDirs)
if err != nil {
this.ErrorPage(err)
return
}
}
var nodeMap = this.Data["node"].(maps.Map)
nodeMap["maxCacheDiskCapacity"] = maxCacheDiskCapacity
nodeMap["cacheDiskDir"] = node.CacheDiskDir
nodeMap["cacheDiskSubDirs"] = diskSubDirs
nodeMap["maxCacheMemoryCapacity"] = maxCacheMemoryCapacity
this.Show()
@@ -69,6 +80,7 @@ func (this *IndexAction) RunPost(params struct {
NodeId int64
MaxCacheDiskCapacityJSON []byte
CacheDiskDir string
CacheDiskSubDirsJSON []byte
MaxCacheMemoryCapacityJSON []byte
Must *actions.Must
@@ -105,10 +117,20 @@ func (this *IndexAction) RunPost(params struct {
}
}
if len(params.CacheDiskSubDirsJSON) > 0 {
var cacheSubDirs = []*serverconfigs.CacheDir{}
err := json.Unmarshal(params.CacheDiskSubDirsJSON, &cacheSubDirs)
if err != nil {
this.ErrorPage(err)
return
}
}
_, err := this.RPC().NodeRPC().UpdateNodeCache(this.AdminContext(), &pb.UpdateNodeCacheRequest{
NodeId: params.NodeId,
MaxCacheDiskCapacity: pbMaxCacheDiskCapacity,
CacheDiskDir: params.CacheDiskDir,
CacheDiskSubDirsJSON: params.CacheDiskSubDirsJSON,
MaxCacheMemoryCapacity: pbMaxCacheMemoryCapacity,
})
if err != nil {

View File

@@ -8,6 +8,7 @@ import (
"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/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
@@ -50,6 +51,22 @@ func (this *IndexAction) RunGet(params struct {
}
this.Data["dnsResolverConfig"] = dnsResolverConfig
// API相关
apiConfigResp, err := this.RPC().NodeRPC().FindNodeAPIConfig(this.AdminContext(), &pb.FindNodeAPIConfigRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
var apiNodeAddrs = []*serverconfigs.NetworkAddressConfig{}
if len(apiConfigResp.ApiNodeAddrsJSON) > 0 {
err = json.Unmarshal(apiConfigResp.ApiNodeAddrsJSON, &apiNodeAddrs)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["apiNodeAddrs"] = apiNodeAddrs
this.Show()
}
@@ -59,6 +76,8 @@ func (this *IndexAction) RunPost(params struct {
DnsResolverJSON []byte
ApiNodeAddrsJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
@@ -68,6 +87,7 @@ func (this *IndexAction) RunPost(params struct {
this.Fail("CPU线程数不能小于0")
}
// 系统设置
_, err := this.RPC().NodeRPC().UpdateNodeSystem(this.AdminContext(), &pb.UpdateNodeSystemRequest{
NodeId: params.NodeId,
MaxCPU: params.MaxCPU,
@@ -77,6 +97,7 @@ func (this *IndexAction) RunPost(params struct {
return
}
// DNS解析设置
var dnsResolverConfig = nodeconfigs.DefaultDNSResolverConfig()
err = json.Unmarshal(params.DnsResolverJSON, dnsResolverConfig)
if err != nil {
@@ -98,5 +119,28 @@ func (this *IndexAction) RunPost(params struct {
return
}
// API节点设置
var apiNodeAddrs = []*serverconfigs.NetworkAddressConfig{}
if len(params.ApiNodeAddrsJSON) > 0 {
err = json.Unmarshal(params.ApiNodeAddrsJSON, &apiNodeAddrs)
if err != nil {
this.Fail("API节点地址校验错误" + err.Error())
}
for _, addr := range apiNodeAddrs {
err = addr.Init()
if err != nil {
this.Fail("API节点地址校验错误" + err.Error())
}
}
}
_, err = this.RPC().NodeRPC().UpdateNodeAPIConfig(this.AdminContext(), &pb.UpdateNodeAPIConfigRequest{
NodeId: params.NodeId,
ApiNodeAddrsJSON: params.ApiNodeAddrsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -64,12 +64,22 @@ func (this *UpdateAction) RunGet(params struct {
}
var ipAddressMaps = []maps.Map{}
for _, addr := range ipAddressesResp.NodeIPAddresses {
// 阈值
thresholds, err := ipaddressutils.InitNodeIPAddressThresholds(this.Parent(), addr.Id)
if err != nil {
this.ErrorPage(err)
return
}
// 专属集群
var clusterMaps = []maps.Map{}
for _, addrCluster := range addr.NodeClusters {
clusterMaps = append(clusterMaps, maps.Map{
"id": addrCluster.Id,
"name": addrCluster.Name,
})
}
ipAddressMaps = append(ipAddressMaps, maps.Map{
"id": addr.Id,
"name": addr.Name,
@@ -78,6 +88,7 @@ func (this *UpdateAction) RunGet(params struct {
"isOn": addr.IsOn,
"isUp": addr.IsUp,
"thresholds": thresholds,
"clusters": clusterMaps,
})
}

View File

@@ -32,11 +32,12 @@ func (this *NodesAction) RunGet(params struct {
Keyword string
Level int32
CpuOrder string
MemoryOrder string
TrafficInOrder string
TrafficOutOrder string
LoadOrder string
CpuOrder string
MemoryOrder string
TrafficInOrder string
TrafficOutOrder string
LoadOrder string
ConnectionsOrder string
}) {
this.Data["groupId"] = params.GroupId
this.Data["regionId"] = params.RegionId
@@ -44,7 +45,7 @@ func (this *NodesAction) RunGet(params struct {
this.Data["activeState"] = params.ActiveState
this.Data["keyword"] = params.Keyword
this.Data["level"] = params.Level
this.Data["hasOrder"] = len(params.CpuOrder) > 0 || len(params.MemoryOrder) > 0 || len(params.TrafficInOrder) > 0 || len(params.TrafficOutOrder) > 0 || len(params.LoadOrder) > 0
this.Data["hasOrder"] = len(params.CpuOrder) > 0 || len(params.MemoryOrder) > 0 || len(params.TrafficInOrder) > 0 || len(params.TrafficOutOrder) > 0 || len(params.LoadOrder) > 0 || len(params.ConnectionsOrder) > 0
// 集群是否已经设置了线路
clusterDNSResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeClusterDNS(this.AdminContext(), &pb.FindEnabledNodeClusterDNSRequest{NodeClusterId: params.ClusterId})
@@ -112,6 +113,10 @@ func (this *NodesAction) RunGet(params struct {
req.LoadAsc = true
} else if params.LoadOrder == "desc" {
req.LoadDesc = true
} else if params.ConnectionsOrder == "asc" {
req.ConnectionsAsc = true
} else if params.ConnectionsOrder == "desc" {
req.ConnectionsDesc = true
}
nodesResp, err := this.RPC().NodeRPC().ListEnabledNodesMatch(this.AdminContext(), req)
if err != nil {
@@ -121,8 +126,8 @@ func (this *NodesAction) RunGet(params struct {
var nodeMaps = []maps.Map{}
for _, node := range nodesResp.Nodes {
// 状态
isSynced := false
status := &nodeconfigs.NodeStatus{}
var isSynced = false
var status = &nodeconfigs.NodeStatus{}
if len(node.StatusJSON) > 0 {
err = json.Unmarshal(node.StatusJSON, &status)
if err != nil {
@@ -142,8 +147,17 @@ func (this *NodesAction) RunGet(params struct {
this.ErrorPage(err)
return
}
ipAddresses := []maps.Map{}
var ipAddresses = []maps.Map{}
for _, addr := range ipAddressesResp.NodeIPAddresses {
// 专属集群
var addrClusterMaps = []maps.Map{}
for _, addrCluster := range addr.NodeClusters {
addrClusterMaps = append(addrClusterMaps, maps.Map{
"id": addrCluster.Id,
"name": addrCluster.Name,
})
}
ipAddresses = append(ipAddresses, maps.Map{
"id": addr.Id,
"name": addr.Name,
@@ -151,6 +165,7 @@ func (this *NodesAction) RunGet(params struct {
"canAccess": addr.CanAccess,
"isUp": addr.IsUp,
"isOn": addr.IsOn,
"clusters": addrClusterMaps,
})
}
@@ -201,16 +216,17 @@ func (this *NodesAction) RunGet(params struct {
"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,
"load1m": numberutils.FormatFloat2(status.Load1m),
"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,
"load1m": numberutils.FormatFloat2(status.Load1m),
"countConnections": status.ConnectionCount,
},
"cluster": maps.Map{
"id": node.NodeCluster.Id,

View File

@@ -78,13 +78,20 @@ func (this *IndexAction) RunPost(params struct {
HttpAllAllowNodeIP bool
HttpAllDefaultDomain string
HttpAllSupportsLowVersionHTTP bool
HttpAccessLogEnableRequestHeaders bool
HttpAccessLogEnableResponseHeaders bool
HttpAccessLogCommonRequestHeadersOnly bool
HttpAccessLogEnableCookies bool
HttpAccessLogEnableServerNotFound bool
LogRecordServerError bool
PerformanceAutoReadTimeout bool
PerformanceAutoWriteTimeout bool
PerformanceDebug bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
@@ -124,17 +131,29 @@ func (this *IndexAction) RunPost(params struct {
}
}
// 域名
config.HTTPAll.AllowMismatchDomains = allowMismatchDomains
config.HTTPAll.AllowNodeIP = params.HttpAllAllowNodeIP
config.HTTPAll.DefaultDomain = params.HttpAllDefaultDomain
// HTTP All
config.HTTPAll.SupportsLowVersionHTTP = params.HttpAllSupportsLowVersionHTTP
// 访问日志
config.HTTPAccessLog.EnableRequestHeaders = params.HttpAccessLogEnableRequestHeaders
config.HTTPAccessLog.EnableResponseHeaders = params.HttpAccessLogEnableResponseHeaders
config.HTTPAccessLog.CommonRequestHeadersOnly = params.HttpAccessLogCommonRequestHeadersOnly
config.HTTPAccessLog.EnableCookies = params.HttpAccessLogEnableCookies
config.HTTPAccessLog.EnableServerNotFound = params.HttpAccessLogEnableServerNotFound
// 日志
config.Log.RecordServerError = params.LogRecordServerError
// 性能
config.Performance.AutoReadTimeout = params.PerformanceAutoReadTimeout
config.Performance.AutoWriteTimeout = params.PerformanceAutoWriteTimeout
config.Performance.Debug = params.PerformanceDebug
err = config.Init()
if err != nil {
this.Fail("配置校验失败:" + err.Error())

View File

@@ -89,6 +89,19 @@ func (this *IndexAction) RunGet(params struct {
}
}
// DNS信息
var fullDomainName = ""
if len(cluster.DnsName) > 0 && cluster.DnsDomainId > 0 {
domainResp, err := this.RPC().DNSDomainRPC().FindBasicDNSDomain(this.AdminContext(), &pb.FindBasicDNSDomainRequest{DnsDomainId: cluster.DnsDomainId})
if err != nil {
this.ErrorPage(err)
return
}
if domainResp.DnsDomain != nil {
fullDomainName = cluster.DnsName + "." + domainResp.DnsDomain.Name
}
}
this.Data["cluster"] = maps.Map{
"id": cluster.Id,
"name": cluster.Name,
@@ -100,6 +113,7 @@ func (this *IndexAction) RunGet(params struct {
"autoRemoteStart": cluster.AutoRemoteStart,
"autoInstallNftables": cluster.AutoInstallNftables,
"sshParams": sshParams,
"domainName": fullDomainName,
}
// 默认值
@@ -122,6 +136,7 @@ func (this *IndexAction) RunPost(params struct {
AutoOpenPorts bool
ClockAutoSync bool
ClockServer string
ClockCheckChrony bool
AutoRemoteStart bool
AutoInstallNftables bool
@@ -154,6 +169,7 @@ func (this *IndexAction) RunPost(params struct {
var clockConfig = nodeconfigs.DefaultClockConfig()
clockConfig.AutoSync = params.ClockAutoSync
clockConfig.Server = params.ClockServer
clockConfig.CheckChrony = params.ClockCheckChrony
clockConfigJSON, err := json.Marshal(clockConfig)
if err != nil {
this.ErrorPage(err)

View File

@@ -60,7 +60,7 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
var tabbar = actionutils.NewTabbar()
tabbar.Add("集群列表", "", "/clusters", "", false)
if teaconst.IsPlus {
tabbar.Add("集群看板", "", "/clusters/cluster/boards?clusterId="+clusterIdString, "board", selectedTabbar == "board")
tabbar.Add("集群看板", "", "/clusters/cluster/boards?clusterId="+clusterIdString, "chart line area", selectedTabbar == "board")
}
tabbar.Add("集群节点", "", "/clusters/cluster/nodes?clusterId="+clusterIdString, "server", selectedTabbar == "node")
tabbar.Add("集群设置", "", "/clusters/cluster/settings?clusterId="+clusterIdString, "setting", selectedTabbar == "setting")
@@ -68,7 +68,7 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
actionutils.SetTabbar(action, tabbar)
// 左侧菜单
secondMenuItem := action.Data.GetString("secondMenuItem")
var secondMenuItem = action.Data.GetString("secondMenuItem")
switch selectedTabbar {
case "setting":
var menuItems = this.createSettingMenu(cluster, clusterInfo, secondMenuItem)
@@ -157,6 +157,7 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, info *pb.F
"name": "服务设置",
"url": "/clusters/cluster/settings/global-server-config?clusterId=" + clusterId,
"isActive": selectedItem == "globalServerConfig",
"isOn": true,
})
items = append(items, maps.Map{

View File

@@ -6,6 +6,7 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"golang.org/x/crypto/ssh"
)
type CreateAction struct {
@@ -50,6 +51,18 @@ func (this *CreateAction) RunPost(params struct {
if len(params.PrivateKey) == 0 {
this.FailField("privateKey", "请输入RSA私钥")
}
// 验证私钥
var err error
if len(params.Passphrase) > 0 {
_, err = ssh.ParsePrivateKeyWithPassphrase([]byte(params.PrivateKey), []byte(params.Passphrase))
} else {
_, err = ssh.ParsePrivateKey([]byte(params.PrivateKey))
}
if err != nil {
this.Fail("私钥验证失败,请检查格式:" + err.Error())
return
}
default:
this.Fail("请选择正确的认证方式")
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"golang.org/x/crypto/ssh"
)
type CreatePopupAction struct {
@@ -51,6 +52,18 @@ func (this *CreatePopupAction) RunPost(params struct {
if len(params.PrivateKey) == 0 {
this.FailField("privateKey", "请输入RSA私钥")
}
// 验证私钥
var err error
if len(params.Passphrase) > 0 {
_, err = ssh.ParsePrivateKeyWithPassphrase([]byte(params.PrivateKey), []byte(params.Passphrase))
} else {
_, err = ssh.ParsePrivateKey([]byte(params.PrivateKey))
}
if err != nil {
this.Fail("私钥验证失败,请检查格式:" + err.Error())
return
}
default:
this.Fail("请选择正确的认证方式")
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"golang.org/x/crypto/ssh"
)
type UpdateAction struct {
@@ -83,6 +84,18 @@ func (this *UpdateAction) RunPost(params struct {
if len(params.PrivateKey) == 0 {
this.FailField("privateKey", "请输入RSA私钥")
}
// 验证私钥
var err error
if len(params.Passphrase) > 0 {
_, err = ssh.ParsePrivateKeyWithPassphrase([]byte(params.PrivateKey), []byte(params.Passphrase))
} else {
_, err = ssh.ParsePrivateKey([]byte(params.PrivateKey))
}
if err != nil {
this.Fail("私钥验证失败,请检查格式:" + err.Error())
return
}
default:
this.Fail("请选择正确的认证方式")
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"golang.org/x/crypto/ssh"
)
type UpdatePopupAction struct {
@@ -83,6 +84,18 @@ func (this *UpdatePopupAction) RunPost(params struct {
if len(params.PrivateKey) == 0 {
this.FailField("privateKey", "请输入RSA私钥")
}
// 验证私钥
var err error
if len(params.Passphrase) > 0 {
_, err = ssh.ParsePrivateKeyWithPassphrase([]byte(params.PrivateKey), []byte(params.Passphrase))
} else {
_, err = ssh.ParsePrivateKey([]byte(params.PrivateKey))
}
if err != nil {
this.Fail("私钥验证失败,请检查格式:" + err.Error())
return
}
default:
this.Fail("请选择正确的认证方式")
}

View File

@@ -0,0 +1,60 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package logs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAllAction struct {
actionutils.ParentAction
}
func (this *DeleteAllAction) RunPost(params struct {
DayFrom string
DayTo string
Keyword string
Level string
Type string // unread, needFix
Tag string
ClusterId int64
NodeId int64
}) {
defer this.CreateLogInfo("批量删除节点运行日志")
// 目前仅允许通过关键词删除,防止误删
if len(params.Keyword) == 0 {
this.Fail("目前仅允许通过关键词删除")
return
}
var fixedState configutils.BoolState = 0
var allServers = false
if params.Type == "needFix" {
fixedState = configutils.BoolStateNo
allServers = true
}
_, err := this.RPC().NodeLogRPC().DeleteNodeLogs(this.AdminContext(), &pb.DeleteNodeLogsRequest{
NodeClusterId: params.ClusterId,
NodeId: params.NodeId,
Role: nodeconfigs.NodeRoleNode,
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
IsUnread: params.Type == "unread",
Tag: params.Tag,
FixedState: int32(fixedState),
AllServers: allServers,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -39,6 +39,7 @@ func (this *IndexAction) RunGet(params struct {
this.Data["dayFrom"] = params.DayFrom
this.Data["dayTo"] = params.DayTo
this.Data["keyword"] = params.Keyword
this.Data["searchedKeyword"] = params.Keyword
this.Data["level"] = params.Level
this.Data["type"] = params.Type
this.Data["tag"] = params.Tag
@@ -97,6 +98,8 @@ func (this *IndexAction) RunGet(params struct {
return
}
var count = countResp.Count
this.Data["countLogs"] = count
var page = this.NewPage(count)
this.Data["page"] = page.AsHTML()

View File

@@ -18,6 +18,7 @@ func init() {
Post("/readAllLogs", new(ReadAllLogsAction)).
Post("/fix", new(FixAction)).
Post("/fixAll", new(FixAllAction)).
Post("/deleteAll", new(DeleteAllAction)).
EndAll()
})
}

View File

@@ -33,11 +33,12 @@ func (this *NodesAction) RunGet(params struct {
Keyword string
Level int32
CpuOrder string
MemoryOrder string
TrafficInOrder string
TrafficOutOrder string
LoadOrder string
CpuOrder string
MemoryOrder string
TrafficInOrder string
TrafficOutOrder string
LoadOrder string
ConnectionsOrder string
}) {
this.Data["groupId"] = params.GroupId
this.Data["regionId"] = params.RegionId
@@ -46,7 +47,7 @@ func (this *NodesAction) RunGet(params struct {
this.Data["keyword"] = params.Keyword
this.Data["level"] = params.Level
this.Data["clusterId"] = params.ClusterId
this.Data["hasOrder"] = len(params.CpuOrder) > 0 || len(params.MemoryOrder) > 0 || len(params.TrafficInOrder) > 0 || len(params.TrafficOutOrder) > 0 || len(params.LoadOrder) > 0
this.Data["hasOrder"] = len(params.CpuOrder) > 0 || len(params.MemoryOrder) > 0 || len(params.TrafficInOrder) > 0 || len(params.TrafficOutOrder) > 0 || len(params.LoadOrder) > 0 || len(params.ConnectionsOrder) > 0
// 集群是否已经设置了线路
clusterDNSResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeClusterDNS(this.AdminContext(), &pb.FindEnabledNodeClusterDNSRequest{NodeClusterId: params.ClusterId})
@@ -114,6 +115,10 @@ func (this *NodesAction) RunGet(params struct {
req.LoadAsc = true
} else if params.LoadOrder == "desc" {
req.LoadDesc = true
} else if params.ConnectionsOrder == "asc" {
req.ConnectionsAsc = true
} else if params.ConnectionsOrder == "desc" {
req.ConnectionsDesc = true
}
nodesResp, err := this.RPC().NodeRPC().ListEnabledNodesMatch(this.AdminContext(), req)
if err != nil {
@@ -123,8 +128,8 @@ func (this *NodesAction) RunGet(params struct {
var nodeMaps = []maps.Map{}
for _, node := range nodesResp.Nodes {
// 状态
isSynced := false
status := &nodeconfigs.NodeStatus{}
var isSynced = false
var status = &nodeconfigs.NodeStatus{}
if len(node.StatusJSON) > 0 {
err = json.Unmarshal(node.StatusJSON, &status)
if err != nil {
@@ -144,8 +149,17 @@ func (this *NodesAction) RunGet(params struct {
this.ErrorPage(err)
return
}
ipAddresses := []maps.Map{}
var ipAddresses = []maps.Map{}
for _, addr := range ipAddressesResp.NodeIPAddresses {
// 专属集群
var addrClusterMaps = []maps.Map{}
for _, addrCluster := range addr.NodeClusters {
addrClusterMaps = append(addrClusterMaps, maps.Map{
"id": addrCluster.Id,
"name": addrCluster.Name,
})
}
ipAddresses = append(ipAddresses, maps.Map{
"id": addr.Id,
"name": addr.Name,
@@ -153,6 +167,7 @@ func (this *NodesAction) RunGet(params struct {
"canAccess": addr.CanAccess,
"isUp": addr.IsUp,
"isOn": addr.IsOn,
"clusters": addrClusterMaps,
})
}
@@ -175,7 +190,7 @@ func (this *NodesAction) RunGet(params struct {
}
// DNS
dnsRouteNames := []string{}
var dnsRouteNames = []string{}
for _, route := range node.DnsRoutes {
dnsRouteNames = append(dnsRouteNames, route.Name)
}
@@ -203,16 +218,17 @@ func (this *NodesAction) RunGet(params struct {
"error": node.InstallStatus.Error,
},
"status": maps.Map{
"isActive": status.IsActive,
"updatedAt": status.UpdatedAt,
"hostname": status.Hostname,
"cpuUsage": status.CPUUsage,
"cpuUsageText": numberutils.FormatFloat2(status.CPUUsage * 100),
"memUsage": status.MemoryUsage,
"memUsageText": numberutils.FormatFloat2(status.MemoryUsage * 100),
"trafficInBytes": status.TrafficInBytes,
"trafficOutBytes": status.TrafficOutBytes,
"load1m": numberutils.FormatFloat2(status.Load1m),
"isActive": status.IsActive,
"updatedAt": status.UpdatedAt,
"hostname": status.Hostname,
"cpuUsage": status.CPUUsage,
"cpuUsageText": numberutils.FormatFloat2(status.CPUUsage * 100),
"memUsage": status.MemoryUsage,
"memUsageText": numberutils.FormatFloat2(status.MemoryUsage * 100),
"trafficInBytes": status.TrafficInBytes,
"trafficOutBytes": status.TrafficOutBytes,
"load1m": numberutils.FormatFloat2(status.Load1m),
"countConnections": status.ConnectionCount,
},
"cluster": maps.Map{
"id": node.NodeCluster.Id,

View File

@@ -3,23 +3,38 @@ package tasks
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"time"
)
type CheckAction struct {
actionutils.ParentAction
}
func (this *CheckAction) RunPost(params struct{}) {
resp, err := this.RPC().NodeTaskRPC().ExistsNodeTasks(this.AdminContext(), &pb.ExistsNodeTasksRequest{
ExcludeTypes: []string{"ipItemChanged"},
})
if err != nil {
this.ErrorPage(err)
return
}
func (this *CheckAction) RunPost(params struct {
IsDoing bool
HasError bool
IsUpdated bool
}) {
var maxTries = 10
for i := 0; i < maxTries; i++ {
resp, err := this.RPC().NodeTaskRPC().ExistsNodeTasks(this.AdminContext(), &pb.ExistsNodeTasksRequest{
ExcludeTypes: []string{"ipItemChanged"},
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["isDoing"] = resp.ExistTasks
this.Data["hasError"] = resp.ExistError
// 如果没有数据变化,继续查询
if i < maxTries-1 && params.IsUpdated && resp.ExistTasks == params.IsDoing && resp.ExistError == params.HasError {
time.Sleep(3 * time.Second)
continue
}
this.Data["isDoing"] = resp.ExistTasks
this.Data["hasError"] = resp.ExistError
break
}
this.Success()
}

View File

@@ -30,7 +30,7 @@ func (this *IndexAction) RunGet(params struct{}) {
helpers.NotifyIPItemsCountChanges()
helpers.NotifyNodeLogsCountChange()
if teaconst.IsPlus {
if this.checkPlus() {
this.RedirectURL("/dashboard/boards")
return
}
@@ -276,5 +276,13 @@ func (this *IndexAction) RunPost(params struct{}) {
}
}
// 弱密码提示
countWeakAdminsResp, err := this.RPC().AdminRPC().CountAllEnabledAdmins(this.AdminContext(), &pb.CountAllEnabledAdminsRequest{HasWeakPassword: true})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["countWeakAdmins"] = countWeakAdminsResp.Count
this.Success()
}

View File

@@ -0,0 +1,8 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package dashboard
func (this *IndexAction) checkPlus() bool {
return false
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/db/dbnodeutils"
"github.com/iwind/TeaGo/maps"
"strings"
)
type NodeAction struct {
@@ -33,7 +34,7 @@ func (this *NodeAction) RunGet(params struct {
"host": node.Host,
"port": node.Port,
"username": node.Username,
"password": node.Password,
"password": strings.Repeat("*", len(node.Password)),
"description": node.Description,
}

View File

@@ -25,7 +25,7 @@ func (this *ClusterAction) RunGet(params struct {
this.ErrorPage(err)
return
}
cluster := clusterResp.NodeCluster
var cluster = clusterResp.NodeCluster
if cluster == nil {
this.NotFound("nodeCluster", params.ClusterId)
return
@@ -42,7 +42,7 @@ func (this *ClusterAction) RunGet(params struct {
return
}
var defaultRoute = dnsResp.DefaultRoute
domainName := ""
var domainName = ""
var dnsMap = maps.Map{
"dnsName": dnsResp.Name,
"domainId": 0,
@@ -70,19 +70,42 @@ func (this *ClusterAction) RunGet(params struct {
this.Data["dnsInfo"] = dnsMap
// 节点DNS解析记录
nodesResp, err := this.RPC().NodeRPC().FindAllEnabledNodesDNSWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledNodesDNSWithNodeClusterIdRequest{NodeClusterId: params.ClusterId})
// 未安装的节点
notInstalledNodesResp, err := this.RPC().NodeRPC().FindAllEnabledNodesDNSWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledNodesDNSWithNodeClusterIdRequest{
NodeClusterId: params.ClusterId,
IsInstalled: false,
})
if err != nil {
this.ErrorPage(err)
return
}
var nodeMaps = []maps.Map{}
var allNodes = notInstalledNodesResp.Nodes
// 节点DNS解析记录
nodesResp, err := this.RPC().NodeRPC().FindAllEnabledNodesDNSWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledNodesDNSWithNodeClusterIdRequest{
NodeClusterId: params.ClusterId,
IsInstalled: true,
})
if err != nil {
this.ErrorPage(err)
return
}
var installedNodeIdsMap = map[int64]bool{}
for _, node := range nodesResp.Nodes {
installedNodeIdsMap[node.Id] = true
}
allNodes = append(allNodes, nodesResp.Nodes...)
var nodeMaps = []maps.Map{}
for _, node := range allNodes {
var isInstalled = installedNodeIdsMap[node.Id]
if len(node.Routes) > 0 {
for _, route := range node.Routes {
// 检查是否已解析
var isResolved = false
if cluster.DnsDomainId > 0 && len(cluster.DnsName) > 0 && len(node.IpAddr) > 0 {
if isInstalled && cluster.DnsDomainId > 0 && len(cluster.DnsName) > 0 && len(node.IpAddr) > 0 {
var recordType = "A"
if utils.IsIPv6(node.IpAddr) {
recordType = "AAAA"
@@ -110,14 +133,15 @@ func (this *ClusterAction) RunGet(params struct {
"name": route.Name,
"code": route.Code,
},
"clusterId": node.NodeClusterId,
"isResolved": isResolved,
"clusterId": node.NodeClusterId,
"isResolved": isResolved,
"isInstalled": isInstalled,
})
}
} else {
// 默认线路
var isResolved = false
if len(defaultRoute) > 0 {
if isInstalled && len(defaultRoute) > 0 {
var recordType = "A"
if utils.IsIPv6(node.IpAddr) {
recordType = "AAAA"
@@ -144,8 +168,9 @@ func (this *ClusterAction) RunGet(params struct {
"name": "",
"code": "",
},
"clusterId": node.NodeClusterId,
"isResolved": isResolved,
"clusterId": node.NodeClusterId,
"isResolved": isResolved,
"isInstalled": isInstalled,
})
}
}

View File

@@ -71,7 +71,10 @@ func ValidateRecordName(name string) bool {
}
pieces := strings.Split(name, ".")
for _, piece := range pieces {
for index, piece := range pieces {
if index == 0 && piece == "*" {
continue
}
if piece == "-" ||
strings.HasPrefix(piece, "-") ||
strings.HasSuffix(piece, "-") ||
@@ -130,6 +133,11 @@ func ValidateRecordValue(recordType dnsconfigs.RecordType, value string) (messag
message = "请输入正确的邮件服务器域名"
return
}
case dnsconfigs.RecordTypeSRV:
if len(value) == 0 {
message = "请输入主机名"
return
}
case dnsconfigs.RecordTypeTXT:
if len(value) > 512 {
message = "文本长度不能超出512字节"

View File

@@ -18,6 +18,8 @@ func (this *NodesPopupAction) Init() {
func (this *NodesPopupAction) RunGet(params struct {
DomainId int64
}) {
this.Data["domainId"] = params.DomainId
// 域名信息
domainResp, err := this.RPC().DNSDomainRPC().FindBasicDNSDomain(this.AdminContext(), &pb.FindBasicDNSDomainRequest{
DnsDomainId: params.DomainId,
@@ -26,7 +28,7 @@ func (this *NodesPopupAction) RunGet(params struct {
this.ErrorPage(err)
return
}
domain := domainResp.DnsDomain
var domain = domainResp.DnsDomain
if domain == nil {
this.NotFound("dnsDomain", params.DomainId)
return
@@ -35,7 +37,7 @@ func (this *NodesPopupAction) RunGet(params struct {
this.Data["domain"] = domain.Name
// 集群
clusterMaps := []maps.Map{}
var clusterMaps = []maps.Map{}
clustersResp, err := this.RPC().NodeClusterRPC().FindAllEnabledNodeClustersWithDNSDomainId(this.AdminContext(), &pb.FindAllEnabledNodeClustersWithDNSDomainIdRequest{DnsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
@@ -43,18 +45,24 @@ func (this *NodesPopupAction) RunGet(params struct {
}
for _, cluster := range clustersResp.NodeClusters {
// 默认值
var defaultRoute = cluster.DnsDefaultRoute
// 节点DNS解析记录
nodesResp, err := this.RPC().NodeRPC().FindAllEnabledNodesDNSWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledNodesDNSWithNodeClusterIdRequest{NodeClusterId: cluster.Id})
nodesResp, err := this.RPC().NodeRPC().FindAllEnabledNodesDNSWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledNodesDNSWithNodeClusterIdRequest{
NodeClusterId: cluster.Id,
IsInstalled: true,
})
if err != nil {
this.ErrorPage(err)
return
}
nodeMaps := []maps.Map{}
var nodeMaps = []maps.Map{}
for _, node := range nodesResp.Nodes {
if len(node.Routes) > 0 {
for _, route := range node.Routes {
// 检查是否有域名解析记录
isOk := false
var isResolved = false
if len(route.Name) > 0 && len(node.IpAddr) > 0 && len(cluster.DnsName) > 0 {
var recordType = "A"
if utils.IsIPv6(node.IpAddr) {
@@ -71,7 +79,7 @@ func (this *NodesPopupAction) RunGet(params struct {
this.ErrorPage(err)
return
}
isOk = checkResp.IsOk
isResolved = checkResp.IsOk
}
nodeMaps = append(nodeMaps, maps.Map{
@@ -83,10 +91,30 @@ func (this *NodesPopupAction) RunGet(params struct {
"code": route.Code,
},
"clusterId": node.NodeClusterId,
"isOk": isOk,
"isOk": isResolved,
})
}
} else {
// 默认线路
var isResolved = false
if len(defaultRoute) > 0 {
var recordType = "A"
if utils.IsIPv6(node.IpAddr) {
recordType = "AAAA"
}
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{
DnsDomainId: cluster.DnsDomainId,
Name: cluster.DnsName,
Type: recordType,
Route: defaultRoute,
Value: node.IpAddr,
})
if err != nil {
this.ErrorPage(err)
return
}
isResolved = checkResp.IsOk
}
nodeMaps = append(nodeMaps, maps.Map{
"id": node.Id,
"name": node.Name,
@@ -96,7 +124,7 @@ func (this *NodesPopupAction) RunGet(params struct {
"code": "",
},
"clusterId": node.NodeClusterId,
"isOk": false,
"isOk": isResolved,
})
}
}

View File

@@ -17,6 +17,8 @@ func (this *ServersPopupAction) Init() {
func (this *ServersPopupAction) RunGet(params struct {
DomainId int64
}) {
this.Data["domainId"] = params.DomainId
// 域名信息
domainResp, err := this.RPC().DNSDomainRPC().FindBasicDNSDomain(this.AdminContext(), &pb.FindBasicDNSDomainRequest{
DnsDomainId: params.DomainId,
@@ -25,7 +27,7 @@ func (this *ServersPopupAction) RunGet(params struct {
this.ErrorPage(err)
return
}
domain := domainResp.DnsDomain
var domain = domainResp.DnsDomain
if domain == nil {
this.NotFound("dnsDomain", params.DomainId)
return
@@ -34,7 +36,7 @@ func (this *ServersPopupAction) RunGet(params struct {
this.Data["domain"] = domain.Name
// 服务信息
clusterMaps := []maps.Map{}
var clusterMaps = []maps.Map{}
clustersResp, err := this.RPC().NodeClusterRPC().FindAllEnabledNodeClustersWithDNSDomainId(this.AdminContext(), &pb.FindAllEnabledNodeClustersWithDNSDomainIdRequest{DnsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
@@ -46,9 +48,9 @@ func (this *ServersPopupAction) RunGet(params struct {
this.ErrorPage(err)
return
}
serverMaps := []maps.Map{}
var serverMaps = []maps.Map{}
for _, server := range serversResp.Servers {
isOk := false
var isOk = false
if len(cluster.DnsName) > 0 && len(server.DnsName) > 0 {
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{
DnsDomainId: params.DomainId,

View File

@@ -64,6 +64,7 @@ func (this *CreatePopupAction) RunPost(params struct {
// HuaweiDNS
ParamHuaweiAccessKeyId string
ParamHuaweiAccessKeySecret string
ParamHuaweiEndpoint string
// CloudFlare
ParamCloudFlareAPIKey string
@@ -119,6 +120,7 @@ func (this *CreatePopupAction) RunPost(params struct {
apiParams["accessKeyId"] = params.ParamHuaweiAccessKeyId
apiParams["accessKeySecret"] = params.ParamHuaweiAccessKeySecret
apiParams["endpoint"] = params.ParamHuaweiEndpoint
case "cloudFlare":
params.Must.
Field("paramCloudFlareAPIKey", params.ParamCloudFlareAPIKey).

View File

@@ -91,6 +91,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
// HuaweiDNS
ParamHuaweiAccessKeyId string
ParamHuaweiAccessKeySecret string
ParamHuaweiEndpoint string
// CloudFlare
ParamCloudFlareAPIKey string
@@ -148,6 +149,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
apiParams["accessKeyId"] = params.ParamHuaweiAccessKeyId
apiParams["accessKeySecret"] = params.ParamHuaweiAccessKeySecret
apiParams["endpoint"] = params.ParamHuaweiEndpoint
case "cloudFlare":
params.Must.
Field("paramCloudFlareAPIKey", params.ParamCloudFlareAPIKey).

View File

@@ -3,21 +3,36 @@ package tasks
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"time"
)
type CheckAction struct {
actionutils.ParentAction
}
func (this *CheckAction) RunPost(params struct{}) {
resp, err := this.RPC().DNSTaskRPC().ExistsDNSTasks(this.AdminContext(), &pb.ExistsDNSTasksRequest{})
if err != nil {
this.ErrorPage(err)
return
}
func (this *CheckAction) RunPost(params struct {
IsDoing bool
HasError bool
IsUpdated bool
}) {
var maxTries = 10
for i := 0; i < maxTries; i++ {
resp, err := this.RPC().DNSTaskRPC().ExistsDNSTasks(this.AdminContext(), &pb.ExistsDNSTasksRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["isDoing"] = resp.ExistTasks
this.Data["hasError"] = resp.ExistError
// 如果没有数据变化,继续查询
if i < maxTries-1 && params.IsUpdated && resp.ExistTasks == params.IsDoing && resp.ExistError == params.HasError {
time.Sleep(3 * time.Second)
continue
}
this.Data["isDoing"] = resp.ExistTasks
this.Data["hasError"] = resp.ExistError
break
}
this.Success()
}

View File

@@ -1,30 +0,0 @@
package index
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
)
// 检查是否需要OTP
type CheckOTPAction struct {
actionutils.ParentAction
}
func (this *CheckOTPAction) Init() {
this.Nav("", "", "")
}
func (this *CheckOTPAction) RunPost(params struct {
Username string
Must *actions.Must
}) {
checkResp, err := this.RPC().AdminRPC().CheckAdminOTPWithUsername(this.AdminContext(), &pb.CheckAdminOTPWithUsernameRequest{Username: params.Username})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["requireOTP"] = checkResp.RequireOTP
this.Success()
}

View File

@@ -1,7 +1,6 @@
package index
import (
"encoding/json"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
@@ -10,14 +9,16 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/setup"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index/loginutils"
adminserverutils "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/server/admin-server-utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
stringutil "github.com/iwind/TeaGo/utils/string"
"github.com/xlzd/gotp"
"net"
"time"
)
@@ -27,13 +28,33 @@ type IndexAction struct {
// 首页(登录页)
var TokenSalt = stringutil.Rand(32)
// TokenKey 加密用的密钥
var TokenKey = stringutil.Rand(32)
func (this *IndexAction) RunGet(params struct {
From string
Auth *helpers.UserShouldAuth
}) {
// 是否自动从HTTP跳转到HTTPS
if this.Request.TLS == nil {
httpsPort, _ := adminserverutils.ReadServerHTTPS()
if httpsPort > 0 {
currentHost, _, err := net.SplitHostPort(this.Request.Host)
if err != nil {
currentHost = this.Request.Host
}
var newHost = configutils.QuoteIP(currentHost)
if httpsPort != 443 /** default https port **/ {
newHost += ":" + types.String(httpsPort)
}
this.RedirectURL("https://" + newHost + this.Request.RequestURI)
return
}
}
// DEMO模式
this.Data["isDemo"] = teaconst.IsDemoMode
@@ -59,7 +80,7 @@ func (this *IndexAction) RunGet(params struct {
this.Data["menu"] = "signIn"
var timestamp = fmt.Sprintf("%d", time.Now().Unix())
this.Data["token"] = stringutil.Md5(TokenSalt+timestamp) + timestamp
this.Data["token"] = stringutil.Md5(TokenKey+timestamp) + timestamp
this.Data["from"] = params.From
uiConfig, err := configloaders.LoadAdminUIConfig()
@@ -83,6 +104,9 @@ func (this *IndexAction) RunGet(params struct {
this.Data["rememberLogin"] = securityConfig.AllowRememberLogin
}
// 删除Cookie
loginutils.UnsetCookie(this.Object())
this.Show()
}
@@ -93,9 +117,10 @@ func (this *IndexAction) RunPost(params struct {
Password string
OtpCode string
Remember bool
Must *actions.Must
Auth *helpers.UserShouldAuth
CSRF *actionutils.CSRF
Must *actions.Must
Auth *helpers.UserShouldAuth
CSRF *actionutils.CSRF
}) {
params.Must.
Field("username", params.Username).
@@ -112,7 +137,7 @@ func (this *IndexAction) RunPost(params struct {
this.Fail("请通过登录页面登录")
}
var timestampString = params.Token[32:]
if stringutil.Md5(TokenSalt+timestampString) != params.Token[:32] {
if stringutil.Md5(TokenKey+timestampString) != params.Token[:32] {
this.FailField("refresh", "登录页面已过期,请刷新后重试")
}
var timestamp = types.Int64(timestampString)
@@ -123,6 +148,7 @@ func (this *IndexAction) RunPost(params struct {
rpcClient, err := rpc.SharedRPC()
if err != nil {
this.Fail("服务器出了点小问题:" + err.Error())
return
}
resp, err := rpcClient.AdminRPC().LoginAdmin(rpcClient.Context(0), &pb.LoginAdminRequest{
Username: params.Username,
@@ -136,6 +162,7 @@ func (this *IndexAction) RunPost(params struct {
}
actionutils.Fail(this, err)
return
}
if !resp.IsOk {
@@ -145,31 +172,37 @@ func (this *IndexAction) RunPost(params struct {
}
this.Fail("请输入正确的用户名密码")
return
}
var adminId = resp.AdminId
// 检查OTP
otpLoginResp, err := this.RPC().LoginRPC().FindEnabledLogin(this.AdminContext(), &pb.FindEnabledLoginRequest{
AdminId: resp.AdminId,
Type: "otp",
})
// 检查是否支持OTP
checkOTPResp, err := this.RPC().AdminRPC().CheckAdminOTPWithUsername(this.AdminContext(), &pb.CheckAdminOTPWithUsernameRequest{Username: params.Username})
if err != nil {
this.ErrorPage(err)
return
}
if otpLoginResp.Login != nil && otpLoginResp.Login.IsOn {
var loginParams = maps.Map{}
err = json.Unmarshal(otpLoginResp.Login.ParamsJSON, &loginParams)
var requireOTP = checkOTPResp.RequireOTP
this.Data["requireOTP"] = requireOTP
if requireOTP {
this.Data["remember"] = params.Remember
var sid = this.Session().Sid
this.Data["sid"] = sid
_, err = this.RPC().LoginSessionRPC().WriteLoginSessionValue(this.AdminContext(), &pb.WriteLoginSessionValueRequest{
Sid: sid + "_otp",
Key: "adminId",
Value: types.String(adminId),
})
if err != nil {
this.ErrorPage(err)
return
}
secret := loginParams.GetString("secret")
if gotp.NewDefaultTOTP(secret).Now() != params.OtpCode {
this.Fail("请输入正确的OTP动态密码")
}
this.Success()
return
}
var adminId = resp.AdminId
// 写入SESSION
params.Auth.StoreAdmin(adminId, params.Remember)
// 记录日志

View File

@@ -7,9 +7,9 @@ import (
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Post("/checkOTP", new(CheckOTPAction)).
Prefix("/").
GetPost("", new(IndexAction)).
Prefix("").
GetPost("/", new(IndexAction)).
GetPost("/index/otp", new(OtpAction)).
EndAll()
})
}

View File

@@ -0,0 +1,60 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package loginutils
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/iwind/TeaGo/actions"
stringutil "github.com/iwind/TeaGo/utils/string"
"net/http"
)
// CalculateClientFingerprint 计算客户端指纹
func CalculateClientFingerprint(action *actions.ActionObject) string {
return stringutil.Md5(action.RequestRemoteIP() + "@" + action.Request.UserAgent())
}
func SetCookie(action *actions.ActionObject, remember bool) {
if remember {
var cookie = &http.Cookie{
Name: teaconst.CookieSID,
Value: action.Session().Sid,
Path: "/",
MaxAge: 14 * 86400,
HttpOnly: true,
}
if action.Request.TLS != nil {
cookie.SameSite = http.SameSiteStrictMode
cookie.Secure = true
}
action.AddCookie(cookie)
} else {
var cookie = &http.Cookie{
Name: teaconst.CookieSID,
Value: action.Session().Sid,
Path: "/",
MaxAge: 0,
HttpOnly: true,
}
if action.Request.TLS != nil {
cookie.SameSite = http.SameSiteStrictMode
cookie.Secure = true
}
action.AddCookie(cookie)
}
}
func UnsetCookie(action *actions.ActionObject) {
cookie := &http.Cookie{
Name: teaconst.CookieSID,
Value: action.Session().Sid,
Path: "/",
MaxAge: -1,
HttpOnly: true,
}
if action.Request.TLS != nil {
cookie.SameSite = http.SameSiteStrictMode
cookie.Secure = true
}
action.AddCookie(cookie)
}

View File

@@ -0,0 +1,154 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package index
import (
"encoding/json"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeAdmin/internal/setup"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
stringutil "github.com/iwind/TeaGo/utils/string"
"github.com/xlzd/gotp"
"time"
)
type OtpAction struct {
actionutils.ParentAction
}
func (this *OtpAction) Init() {
this.Nav("", "", "")
}
func (this *OtpAction) RunGet(params struct {
From string
Sid string
Remember bool
}) {
// 检查系统是否已经配置过
if !setup.IsConfigured() {
this.RedirectURL("/setup")
return
}
//// 是否新安装
if setup.IsNewInstalled() {
this.RedirectURL("/setup/confirm")
return
}
this.Data["isUser"] = false
this.Data["menu"] = "signIn"
var timestamp = fmt.Sprintf("%d", time.Now().Unix())
this.Data["token"] = stringutil.Md5(TokenKey+timestamp) + timestamp
this.Data["from"] = params.From
this.Data["sid"] = params.Sid
uiConfig, err := configloaders.LoadAdminUIConfig()
if err != nil {
this.ErrorPage(err)
return
}
this.Data["systemName"] = uiConfig.AdminSystemName
this.Data["showVersion"] = uiConfig.ShowVersion
if len(uiConfig.Version) > 0 {
this.Data["version"] = uiConfig.Version
} else {
this.Data["version"] = teaconst.Version
}
this.Data["faviconFileId"] = uiConfig.FaviconFileId
this.Data["remember"] = params.Remember
this.Show()
}
func (this *OtpAction) RunPost(params struct {
Sid string
OtpCode string
Remember bool
Must *actions.Must
Auth *helpers.UserShouldAuth
}) {
if len(params.OtpCode) == 0 {
this.FailField("otpCode", "请输入正确的OTP动态密码")
return
}
var sid = params.Sid
if len(sid) == 0 || len(sid) > 64 {
this.Fail("参数错误请重新登录001")
return
}
sid += "_otp"
// 获取SESSION
sessionResp, err := this.RPC().LoginSessionRPC().FindLoginSession(this.AdminContext(), &pb.FindLoginSessionRequest{Sid: sid})
if err != nil {
this.ErrorPage(err)
return
}
var session = sessionResp.LoginSession
if session == nil || session.AdminId <= 0 {
this.Fail("参数错误请重新登录002")
return
}
var adminId = session.AdminId
// 检查OTP
otpLoginResp, err := this.RPC().LoginRPC().FindEnabledLogin(this.AdminContext(), &pb.FindEnabledLoginRequest{
AdminId: adminId,
Type: "otp",
})
if err != nil {
this.ErrorPage(err)
return
}
if otpLoginResp.Login != nil && otpLoginResp.Login.IsOn {
var loginParams = maps.Map{}
err = json.Unmarshal(otpLoginResp.Login.ParamsJSON, &loginParams)
if err != nil {
this.ErrorPage(err)
return
}
var secret = loginParams.GetString("secret")
if gotp.NewDefaultTOTP(secret).Now() != params.OtpCode {
this.FailField("otpCode", "请输入正确的OTP动态密码")
return
}
}
// 写入SESSION
params.Auth.StoreAdmin(adminId, params.Remember)
// 删除OTP SESSION
_, err = this.RPC().LoginSessionRPC().DeleteLoginSession(this.AdminContext(), &pb.DeleteLoginSessionRequest{Sid: sid})
if err != nil {
this.ErrorPage(err)
return
}
// 记录日志
rpcClient, err := rpc.SharedRPC()
if err != nil {
this.ErrorPage(err)
return
}
err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(adminId), oplogs.LevelInfo, this.Request.URL.Path, "成功通过OTP验证登录系统", this.RequestRemoteIP())
if err != nil {
utils.PrintError(err)
}
this.Success()
}

View File

@@ -23,6 +23,7 @@ func (this *ExportExcelAction) RunGet(params struct {
DayTo string
Keyword string
UserType string
Level string
}) {
logsResp, err := this.RPC().LogRPC().ListLogs(this.AdminContext(), &pb.ListLogsRequest{
Offset: 0,
@@ -31,6 +32,7 @@ func (this *ExportExcelAction) RunGet(params struct {
DayTo: params.DayTo,
Keyword: params.Keyword,
UserType: params.UserType,
Level: params.Level,
})
if err != nil {
this.ErrorPage(err)
@@ -56,6 +58,7 @@ func (this *ExportExcelAction) RunGet(params struct {
row.AddCell().SetString("区域")
row.AddCell().SetString("运营商")
row.AddCell().SetString("页面地址")
row.AddCell().SetString("级别")
}
// 数据
@@ -95,6 +98,17 @@ func (this *ExportExcelAction) RunGet(params struct {
row.AddCell().SetString(regionName)
row.AddCell().SetString(ispName)
row.AddCell().SetString(log.Action)
var levelName = ""
switch log.Level {
case "info":
levelName = "信息"
case "warn", "warning":
levelName = "警告"
case "error":
levelName = "错误"
}
row.AddCell().SetString(levelName)
}
this.AddHeader("Content-Type", "application/vnd.ms-excel")

View File

@@ -21,6 +21,7 @@ func (this *IndexAction) RunGet(params struct {
DayTo string
Keyword string
UserType string
Level string
}) {
// 读取配置
config, err := configloaders.LoadLogConfig()
@@ -35,18 +36,36 @@ func (this *IndexAction) RunGet(params struct {
this.Data["keyword"] = params.Keyword
this.Data["userType"] = params.UserType
// 级别
this.Data["level"] = params.Level
this.Data["levelOptions"] = []maps.Map{
{
"code": "info",
"name": "信息",
},
{
"code": "warn",
"name": "警告",
},
{
"code": "error",
"name": "错误",
},
}
countResp, err := this.RPC().LogRPC().CountLogs(this.AdminContext(), &pb.CountLogRequest{
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
UserType: params.UserType,
Level: params.Level,
})
if err != nil {
this.ErrorPage(err)
return
}
count := countResp.Count
page := this.NewPage(count)
var count = countResp.Count
var page = this.NewPage(count)
this.Data["page"] = page.AsHTML()
logsResp, err := this.RPC().LogRPC().ListLogs(this.AdminContext(), &pb.ListLogsRequest{
@@ -56,12 +75,13 @@ func (this *IndexAction) RunGet(params struct {
DayTo: params.DayTo,
Keyword: params.Keyword,
UserType: params.UserType,
Level: params.Level,
})
if err != nil {
this.ErrorPage(err)
return
}
logMaps := []maps.Map{}
var logMaps = []maps.Map{}
for _, log := range logsResp.Logs {
regionName := ""
regionResp, err := this.RPC().IPLibraryRPC().LookupIPRegion(this.AdminContext(), &pb.LookupIPRegionRequest{Ip: log.Ip})

View File

@@ -5,6 +5,7 @@ import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/ipAddresses/ipaddressutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
@@ -20,8 +21,18 @@ func (this *CreatePopupAction) Init() {
}
func (this *CreatePopupAction) RunGet(params struct {
NodeId int64
SupportThresholds bool
}) {
// 专属集群
clusterMaps, err := ipaddressutils.FindNodeClusterMapsWithNodeId(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["clusters"] = clusterMaps
// 阈值
this.Data["supportThresholds"] = params.SupportThresholds
this.Show()
@@ -33,6 +44,7 @@ func (this *CreatePopupAction) RunPost(params struct {
Name string
IsUp bool
ThresholdsJSON []byte
ClusterIds []int64
Must *actions.Must
}) {
@@ -57,6 +69,14 @@ func (this *CreatePopupAction) RunPost(params struct {
_ = json.Unmarshal(params.ThresholdsJSON, &thresholds)
}
// 专属集群
// 目前只考虑CDN边缘集群
clusterMaps, err := ipaddressutils.FindNodeClusterMaps(this.Parent(), params.ClusterIds)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["ipAddress"] = maps.Map{
"name": params.Name,
"canAccess": params.CanAccess,
@@ -65,6 +85,7 @@ func (this *CreatePopupAction) RunPost(params struct {
"isOn": true,
"isUp": params.IsUp,
"thresholds": thresholds,
"clusters": clusterMaps,
}
this.Success()
}

View File

@@ -11,14 +11,28 @@ import (
// UpdateNodeIPAddresses 保存一组IP地址
func UpdateNodeIPAddresses(parentAction *actionutils.ParentAction, nodeId int64, role nodeconfigs.NodeRole, ipAddressesJSON []byte) error {
addresses := []maps.Map{}
var addresses = []maps.Map{}
err := json.Unmarshal(ipAddressesJSON, &addresses)
if err != nil {
return err
}
for _, addr := range addresses {
var resultAddrIds = []int64{}
addrId := addr.GetInt64("id")
var addrId = addr.GetInt64("id")
// 专属集群
var addrClusterIds = []int64{}
var addrClusters = addr.GetSlice("clusters")
if len(addrClusters) > 0 {
for _, addrCluster := range addrClusters {
var m = maps.NewMap(addrCluster)
var clusterId = m.GetInt64("id")
if clusterId > 0 {
addrClusterIds = append(addrClusterIds, clusterId)
}
}
}
if addrId > 0 {
resultAddrIds = append(resultAddrIds, addrId)
@@ -36,6 +50,7 @@ func UpdateNodeIPAddresses(parentAction *actionutils.ParentAction, nodeId int64,
CanAccess: addr.GetBool("canAccess"),
IsOn: isOn,
IsUp: addr.GetBool("isUp"),
ClusterIds: addrClusterIds,
})
if err != nil {
return err
@@ -47,12 +62,13 @@ func UpdateNodeIPAddresses(parentAction *actionutils.ParentAction, nodeId int64,
if len(result) == 1 {
// 单个创建
createResp, err := parentAction.RPC().NodeIPAddressRPC().CreateNodeIPAddress(parentAction.AdminContext(), &pb.CreateNodeIPAddressRequest{
NodeId: nodeId,
Role: role,
Name: addr.GetString("name"),
Ip: result[0],
CanAccess: addr.GetBool("canAccess"),
IsUp: addr.GetBool("isUp"),
NodeId: nodeId,
Role: role,
Name: addr.GetString("name"),
Ip: result[0],
CanAccess: addr.GetBool("canAccess"),
IsUp: addr.GetBool("isUp"),
NodeClusterIds: addrClusterIds,
})
if err != nil {
return err
@@ -62,13 +78,14 @@ func UpdateNodeIPAddresses(parentAction *actionutils.ParentAction, nodeId int64,
} else if len(result) > 1 {
// 批量创建
createResp, err := parentAction.RPC().NodeIPAddressRPC().CreateNodeIPAddresses(parentAction.AdminContext(), &pb.CreateNodeIPAddressesRequest{
NodeId: nodeId,
Role: role,
Name: addr.GetString("name"),
IpList: result,
CanAccess: addr.GetBool("canAccess"),
IsUp: addr.GetBool("isUp"),
GroupValue: ipStrings,
NodeId: nodeId,
Role: role,
Name: addr.GetString("name"),
IpList: result,
CanAccess: addr.GetBool("canAccess"),
IsUp: addr.GetBool("isUp"),
GroupValue: ipStrings,
NodeClusterIds: addrClusterIds,
})
if err != nil {
return err
@@ -140,3 +157,53 @@ func InitNodeIPAddressThresholds(parentAction *actionutils.ParentAction, addrId
}
return thresholds, nil
}
// FindNodeClusterMapsWithNodeId 根据节点读取集群信息
func FindNodeClusterMapsWithNodeId(parentAction *actionutils.ParentAction, nodeId int64) ([]maps.Map, error) {
var clusterMaps = []maps.Map{}
if nodeId > 0 { // CDN边缘节点
nodeResp, err := parentAction.RPC().NodeRPC().FindEnabledNode(parentAction.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: nodeId})
if err != nil {
return nil, err
}
var node = nodeResp.Node
if node != nil {
var clusters = []*pb.NodeCluster{}
if node.NodeCluster != nil {
clusters = append(clusters, nodeResp.Node.NodeCluster)
}
if len(node.SecondaryNodeClusters) > 0 {
clusters = append(clusters, node.SecondaryNodeClusters...)
}
for _, cluster := range clusters {
clusterMaps = append(clusterMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
"isChecked": false,
})
}
}
}
return clusterMaps, nil
}
// FindNodeClusterMaps 根据一组集群ID读取集群信息
func FindNodeClusterMaps(parentAction *actionutils.ParentAction, clusterIds []int64) ([]maps.Map, error) {
var clusterMaps = []maps.Map{}
if len(clusterIds) > 0 {
for _, clusterId := range clusterIds {
clusterResp, err := parentAction.RPC().NodeClusterRPC().FindEnabledNodeCluster(parentAction.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: clusterId})
if err != nil {
return nil, err
}
var cluster = clusterResp.NodeCluster
if cluster != nil {
clusterMaps = append(clusterMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
})
}
}
}
return clusterMaps, nil
}

View File

@@ -5,6 +5,7 @@ import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"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/actions"
@@ -21,9 +22,18 @@ func (this *UpdatePopupAction) Init() {
}
func (this *UpdatePopupAction) RunGet(params struct {
NodeId int64
AddressId int64
SupportThresholds bool
}) {
// 专属集群
clusterMaps, err := ipaddressutils.FindNodeClusterMapsWithNodeId(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["clusters"] = clusterMaps
this.Data["supportThresholds"] = params.SupportThresholds
this.Show()
@@ -37,6 +47,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
IsOn bool
IsUp bool
ThresholdsJSON []byte
ClusterIds []int64
Must *actions.Must
}) {
@@ -81,6 +92,14 @@ func (this *UpdatePopupAction) RunPost(params struct {
_ = json.Unmarshal(params.ThresholdsJSON, &thresholds)
}
// 专属集群
// 目前只考虑CDN边缘集群
clusterMaps, err := ipaddressutils.FindNodeClusterMaps(this.Parent(), params.ClusterIds)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["ipAddress"] = maps.Map{
"name": params.Name,
"ip": params.IP,
@@ -89,6 +108,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
"isOn": params.IsOn,
"isUp": isUp,
"thresholds": thresholds,
"clusters": clusterMaps,
}
this.Success()

View File

@@ -21,6 +21,7 @@ func init() {
Data("leftMenuItem", "cert").
Get("", new(IndexAction)).
GetPost("/uploadPopup", new(UploadPopupAction)).
GetPost("/uploadBatchPopup", new(UploadBatchPopupAction)).
Post("/delete", new(DeleteAction)).
GetPost("/updatePopup", new(UpdatePopupAction)).
Get("/certPopup", new(CertPopupAction)).

View File

@@ -2,9 +2,11 @@ package certs
import (
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
@@ -23,15 +25,82 @@ func (this *SelectPopupAction) Init() {
}
func (this *SelectPopupAction) RunGet(params struct {
ServerId int64 // 搜索的服务
UserId int64 // 搜索的用户名
SearchingDomains string // 搜索的域名
SearchingType string // 搜索类型match|all
ViewSize string
SelectedCertIds string
Keyword string
}) {
// TODO 列出常用和最新的证书供用户选择
// 服务相关
if params.ServerId > 0 {
serverResp, err := this.RPC().ServerRPC().FindEnabledUserServerBasic(this.AdminContext(), &pb.FindEnabledUserServerBasicRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
var server = serverResp.Server
if server != nil {
if server.UserId > 0 {
params.UserId = server.UserId
}
// 读取所有ServerNames
serverNamesResp, err := this.RPC().ServerRPC().FindServerNames(this.AdminContext(), &pb.FindServerNamesRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
if len(serverNamesResp.ServerNamesJSON) > 0 {
var serverNames = []*serverconfigs.ServerNameConfig{}
err = json.Unmarshal(serverNamesResp.ServerNamesJSON, &serverNames)
if err != nil {
this.ErrorPage(err)
return
}
params.SearchingDomains = strings.Join(serverconfigs.PlainServerNames(serverNames), ",")
}
}
}
// 用户相关
this.Data["userId"] = params.UserId
// 域名搜索相关
var url = this.Request.URL.Path
var query = this.Request.URL.Query()
query.Del("searchingType")
this.Data["baseURL"] = url + "?" + query.Encode()
var searchingDomains = []string{}
if len(params.SearchingDomains) > 0 {
searchingDomains = strings.Split(params.SearchingDomains, ",")
}
const maxDomains = 2_000 // 限制搜索的域名数量
if len(searchingDomains) > maxDomains {
searchingDomains = searchingDomains[:maxDomains]
}
this.Data["searchingDomains"] = searchingDomains
this.Data["keyword"] = params.Keyword
this.Data["selectedCertIds"] = params.SelectedCertIds
var searchingType = params.SearchingType
if len(searchingType) == 0 {
if len(params.SearchingDomains) == 0 {
searchingType = "all"
} else {
searchingType = "match"
}
}
if searchingType != "all" && searchingType != "match" {
this.ErrorPage(errors.New("invalid searching type '" + searchingType + "'"))
return
}
this.Data["searchingType"] = searchingType
// 已经选择的证书
var selectedCertIds = []string{}
if len(params.SelectedCertIds) > 0 {
@@ -43,24 +112,68 @@ func (this *SelectPopupAction) RunGet(params struct {
}
this.Data["viewSize"] = params.ViewSize
countResp, err := this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
// 全部证书数量
countAllResp, err := this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
UserId: params.UserId,
Keyword: params.Keyword,
})
if err != nil {
this.ErrorPage(err)
return
}
var totalAll = countAllResp.Count
this.Data["totalAll"] = totalAll
page := this.NewPage(countResp.Count)
// 已匹配证书数量
var totalMatch int64 = 0
if len(searchingDomains) > 0 {
countMatchResp, err := this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
UserId: params.UserId,
Keyword: params.Keyword,
Domains: searchingDomains,
})
if err != nil {
this.ErrorPage(err)
return
}
totalMatch = countMatchResp.Count
}
this.Data["totalMatch"] = totalMatch
var totalCerts int64
if searchingType == "all" {
totalCerts = totalAll
} else if searchingType == "match" {
totalCerts = totalMatch
}
var page = this.NewPage(totalCerts)
this.Data["page"] = page.AsHTML()
listResp, err := this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
Keyword: params.Keyword,
Offset: page.Offset,
Size: page.Size,
})
var listResp *pb.ListSSLCertsResponse
if searchingType == "all" {
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
UserId: params.UserId,
Keyword: params.Keyword,
Offset: page.Offset,
Size: page.Size,
})
} else if searchingType == "match" {
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
UserId: params.UserId,
Keyword: params.Keyword,
Domains: searchingDomains,
Offset: page.Offset,
Size: page.Size,
})
}
certConfigs := []*sslconfigs.SSLCertConfig{}
if listResp == nil {
this.ErrorPage(errors.New("'listResp' should not be nil"))
return
}
var certConfigs = []*sslconfigs.SSLCertConfig{}
err = json.Unmarshal(listResp.SslCertsJSON, &certConfigs)
if err != nil {
this.ErrorPage(err)
@@ -68,8 +181,8 @@ func (this *SelectPopupAction) RunGet(params struct {
}
this.Data["certs"] = certConfigs
certMaps := []maps.Map{}
nowTime := time.Now().Unix()
var certMaps = []maps.Map{}
var nowTime = time.Now().Unix()
for _, certConfig := range certConfigs {
countServersResp, err := this.RPC().ServerRPC().CountAllEnabledServersWithSSLCertId(this.AdminContext(), &pb.CountAllEnabledServersWithSSLCertIdRequest{SslCertId: certConfig.Id})
if err != nil {

View File

@@ -26,18 +26,20 @@ func (this *UpdatePopupAction) RunGet(params struct {
this.ErrorPage(err)
return
}
certConfigJSON := certConfigResp.SslCertJSON
var certConfigJSON = certConfigResp.SslCertJSON
if len(certConfigJSON) == 0 {
this.NotFound("cert", params.CertId)
return
}
certConfig := &sslconfigs.SSLCertConfig{}
var certConfig = &sslconfigs.SSLCertConfig{}
err = json.Unmarshal(certConfigJSON, certConfig)
if err != nil {
this.ErrorPage(err)
return
}
certConfig.CertData = nil // cert & key 不需要在界面上显示
certConfig.KeyData = nil
this.Data["certConfig"] = certConfig
this.Show()
@@ -118,7 +120,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
// 校验
certConfig.IsCA = params.IsCA
err = certConfig.Init()
err = certConfig.Init(nil)
if err != nil {
if params.IsCA {
this.Fail("证书校验错误:" + err.Error())

View File

@@ -0,0 +1,225 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package certs
import (
"bytes"
"crypto/tls"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/types"
"io"
"mime/multipart"
"strings"
)
// UploadBatchPopupAction 批量上传证书
type UploadBatchPopupAction struct {
actionutils.ParentAction
}
func (this *UploadBatchPopupAction) Init() {
this.Nav("", "", "")
}
func (this *UploadBatchPopupAction) RunGet(params struct {
ServerId int64
UserId int64
}) {
// 读取服务用户
if params.ServerId > 0 {
serverResp, err := this.RPC().ServerRPC().FindEnabledUserServerBasic(this.AdminContext(), &pb.FindEnabledUserServerBasicRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
var server = serverResp.Server
if server != nil {
params.UserId = server.UserId
}
}
this.Data["userId"] = params.UserId
this.Data["maxFiles"] = this.maxFiles()
this.Show()
}
func (this *UploadBatchPopupAction) RunPost(params struct {
UserId int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo("批量上传证书")
var files = this.Request.MultipartForm.File["certFiles"]
if len(files) == 0 {
this.Fail("请选择要上传的证书和私钥文件")
return
}
// 限制每次上传的文件数量
var maxFiles = this.maxFiles()
if len(files) > maxFiles {
this.Fail("每次上传最多不能超过" + types.String(maxFiles) + "个文件")
return
}
type certInfo struct {
filename string
data []byte
}
var certDataList = []*certInfo{}
var keyDataList = [][]byte{}
var failMessages = []string{}
for _, file := range files {
func(file *multipart.FileHeader) {
fp, err := file.Open()
if err != nil {
failMessages = append(failMessages, "文件"+file.Filename+"读取失败:"+err.Error())
return
}
defer func() {
_ = fp.Close()
}()
data, err := io.ReadAll(fp)
if err != nil {
failMessages = append(failMessages, "文件"+file.Filename+"读取失败:"+err.Error())
return
}
if bytes.Contains(data, []byte("CERTIFICATE-")) {
certDataList = append(certDataList, &certInfo{
filename: file.Filename,
data: data,
})
} else if bytes.Contains(data, []byte("PRIVATE KEY-")) {
keyDataList = append(keyDataList, data)
} else {
failMessages = append(failMessages, "文件"+file.Filename+"读取失败:文件格式错误,无法识别是证书还是私钥")
return
}
}(file)
}
if len(failMessages) > 0 {
this.Fail("发生了错误:" + strings.Join(failMessages, ""))
return
}
// 对比证书和私钥数量是否一致
if len(certDataList) != len(keyDataList) {
this.Fail("证书文件数量(" + types.String(len(certDataList)) + ")和私钥文件数量(" + types.String(len(keyDataList)) + ")不一致")
return
}
// 自动匹配
var pairs = [][2][]byte{} // [] { cert, key }
var keyIndexMap = map[int]bool{} // 方便下面跳过已匹配的Key
for _, cert := range certDataList {
var found = false
for keyIndex, keyData := range keyDataList {
if keyIndexMap[keyIndex] {
continue
}
_, err := tls.X509KeyPair(cert.data, keyData)
if err == nil {
found = true
pairs = append(pairs, [2][]byte{cert.data, keyData})
keyIndexMap[keyIndex] = true
break
}
}
if !found {
this.Fail("找不到" + cert.filename + "对应的私钥")
return
}
}
// 组织 CertConfig
var pbCerts = []*pb.CreateSSLCertsRequestCert{}
var certConfigs = []*sslconfigs.SSLCertConfig{}
for _, pair := range pairs {
certData, keyData := pair[0], pair[1]
var certConfig = &sslconfigs.SSLCertConfig{
IsCA: false,
CertData: certData,
KeyData: keyData,
}
err := certConfig.Init(nil)
if err != nil {
this.Fail("证书验证失败:" + err.Error())
return
}
certConfigs = append(certConfigs, certConfig)
var certName = ""
if len(certConfig.DNSNames) > 0 {
certName = certConfig.DNSNames[0]
if len(certConfig.DNSNames) > 1 {
certName += "等" + types.String(len(certConfig.DNSNames)) + "个域名"
}
}
certConfig.Name = certName
pbCerts = append(pbCerts, &pb.CreateSSLCertsRequestCert{
IsOn: true,
Name: certName,
Description: "",
ServerName: "",
IsCA: false,
CertData: certData,
KeyData: keyData,
TimeBeginAt: certConfig.TimeBeginAt,
TimeEndAt: certConfig.TimeEndAt,
DnsNames: certConfig.DNSNames,
CommonNames: certConfig.CommonNames,
})
}
createResp, err := this.RPC().SSLCertRPC().CreateSSLCerts(this.AdminContext(), &pb.CreateSSLCertsRequest{
UserId: params.UserId,
SSLCerts: pbCerts,
})
if err != nil {
this.ErrorPage(err)
return
}
var certIds = createResp.SslCertIds
if len(certIds) != len(certConfigs) {
this.Fail("上传成功但API返回的证书ID数量错误请反馈给开发者")
return
}
// 返回数据
this.Data["count"] = len(pbCerts)
var certRefs = []*sslconfigs.SSLCertRef{}
for index, cert := range certConfigs {
// ID
cert.Id = certIds[index]
// 减少不必要的数据
cert.CertData = nil
cert.KeyData = nil
certRefs = append(certRefs, &sslconfigs.SSLCertRef{
IsOn: true,
CertId: cert.Id,
})
}
this.Data["certs"] = certConfigs
this.Data["certRefs"] = certRefs
this.Success()
}

View File

@@ -0,0 +1,8 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package certs
func (this *UploadBatchPopupAction) maxFiles() int {
return 20
}

View File

@@ -18,11 +18,30 @@ func (this *UploadPopupAction) Init() {
this.Nav("", "", "")
}
func (this *UploadPopupAction) RunGet(params struct{}) {
func (this *UploadPopupAction) RunGet(params struct {
ServerId int64
UserId int64
}) {
// 读取服务用户
if params.ServerId > 0 {
serverResp, err := this.RPC().ServerRPC().FindEnabledUserServerBasic(this.AdminContext(), &pb.FindEnabledUserServerBasicRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
var server = serverResp.Server
if server != nil {
params.UserId = server.UserId
}
}
this.Data["userId"] = params.UserId
this.Show()
}
func (this *UploadPopupAction) RunPost(params struct {
UserId int64
TextMode bool
Name string
IsCA bool
@@ -85,7 +104,7 @@ func (this *UploadPopupAction) RunPost(params struct {
CertData: certData,
KeyData: keyData,
}
err := certConfig.Init()
err := certConfig.Init(nil)
if err != nil {
if params.IsCA {
this.Fail("证书校验错误:" + err.Error())
@@ -107,6 +126,7 @@ func (this *UploadPopupAction) RunPost(params struct {
// 保存
createResp, err := this.RPC().SSLCertRPC().CreateSSLCert(this.AdminContext(), &pb.CreateSSLCertRequest{
IsOn: params.IsOn,
UserId: params.UserId,
Name: params.Name,
Description: params.Description,
ServerName: "",

View File

@@ -32,6 +32,7 @@ func (this *CreateRulePopupAction) RunGet(params struct {
"params": checkpoint.Params,
"options": checkpoint.Options,
"isComposed": checkpoint.IsComposed,
"dataType": checkpoint.DataType,
})
}
}

View File

@@ -36,24 +36,6 @@ func (this *CreateAction) RunGet(params struct{}) {
}
this.Data["countAuditing"] = countAuditingResp.Count
// 所有集群
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["serverTypes"] = serverconfigs.AllServerTypes()
@@ -95,10 +77,6 @@ func (this *CreateAction) RunPost(params struct {
Must *actions.Must
}) {
params.Must.
Field("name", params.Name).
Require("请输入服务名称")
var clusterId = params.ClusterId
// 用户
@@ -129,7 +107,7 @@ func (this *CreateAction) RunPost(params struct {
switch params.ServerType {
case serverconfigs.ServerTypeHTTPProxy, serverconfigs.ServerTypeHTTPWeb:
listen := []*serverconfigs.NetworkAddressConfig{}
var listen = []*serverconfigs.NetworkAddressConfig{}
err := json.Unmarshal([]byte(params.Addresses), &listen)
if err != nil {
this.Fail("端口地址解析失败:" + err.Error())
@@ -166,7 +144,7 @@ func (this *CreateAction) RunPost(params struct {
this.Fail("DEMO模式下不能创建TCP反向代理")
}
listen := []*serverconfigs.NetworkAddressConfig{}
var listen = []*serverconfigs.NetworkAddressConfig{}
err := json.Unmarshal([]byte(params.Addresses), &listen)
if err != nil {
this.Fail("端口地址解析失败:" + err.Error())
@@ -197,13 +175,17 @@ func (this *CreateAction) RunPost(params struct {
tlsConfig.AddListen(addr)
}
}
if len(params.Name) == 0 {
params.Name = "TCP负载均衡"
}
case serverconfigs.ServerTypeUDPProxy:
// 在DEMO模式下不能创建
if teaconst.IsDemoMode {
this.Fail("DEMO模式下不能创建UDP反向代理")
}
listen := []*serverconfigs.NetworkAddressConfig{}
var listen = []*serverconfigs.NetworkAddressConfig{}
err := json.Unmarshal([]byte(params.Addresses), &listen)
if err != nil {
this.Fail("端口地址解析失败:" + err.Error())
@@ -225,20 +207,24 @@ func (this *CreateAction) RunPost(params struct {
udpConfig.AddListen(addr)
}
}
if len(params.Name) == 0 {
params.Name = "UDP负载均衡"
}
default:
this.Fail("请选择正确的服务类型")
}
// 证书
if len(params.CertIdsJSON) > 0 {
certIds := []int64{}
var certIds = []int64{}
err := json.Unmarshal(params.CertIdsJSON, &certIds)
if err != nil {
this.ErrorPage(err)
return
}
if len(certIds) > 0 {
certRefs := []*sslconfigs.SSLCertRef{}
var certRefs = []*sslconfigs.SSLCertRef{}
for _, certId := range certIds {
certRefs = append(certRefs, &sslconfigs.SSLCertRef{
IsOn: true,
@@ -265,7 +251,7 @@ func (this *CreateAction) RunPost(params struct {
this.ErrorPage(err)
return
}
sslPolicyId := sslPolicyIdResp.SslPolicyId
var sslPolicyId = sslPolicyIdResp.SslPolicyId
httpsConfig.SSLPolicyRef = &sslconfigs.SSLPolicyRef{
IsOn: true,
SSLPolicyId: sslPolicyId,
@@ -282,8 +268,13 @@ func (this *CreateAction) RunPost(params struct {
}
// 检查域名是否已经存在
allServerNames := serverconfigs.PlainServerNames(serverNames)
var allServerNames = serverconfigs.PlainServerNames(serverNames)
if len(allServerNames) > 0 {
// 指定默认名称
if len(params.Name) == 0 {
params.Name = allServerNames[0]
}
dupResp, err := this.RPC().ServerRPC().CheckServerNameDuplicationInNodeCluster(this.AdminContext(), &pb.CheckServerNameDuplicationInNodeClusterRequest{
ServerNames: allServerNames,
NodeClusterId: clusterId,
@@ -396,7 +387,7 @@ func (this *CreateAction) RunPost(params struct {
AdminId: this.AdminId(),
Type: params.ServerType,
Name: params.Name,
ServerNamesJON: params.ServerNames,
ServerNamesJSON: params.ServerNames,
Description: params.Description,
NodeClusterId: clusterId,
IncludeNodesJSON: includeNodesJSON,

View File

@@ -55,10 +55,11 @@ func (this *IndexAction) RunGet(params struct {
// 服务列表
serversResp, err := this.RPC().ServerRPC().ListEnabledServersMatch(this.AdminContext(), &pb.ListEnabledServersMatchRequest{
Offset: page.Offset,
Size: page.Size,
ServerGroupId: params.GroupId,
Keyword: params.Keyword,
Offset: page.Offset,
Size: page.Size,
ServerGroupId: params.GroupId,
Keyword: params.Keyword,
IgnoreSSLCerts: true,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -43,7 +43,7 @@ func (this *IndexAction) RunGet(params struct {
this.ErrorPage(err)
return
}
headerPolicyId := createHeaderPolicyResp.HeaderPolicyId
headerPolicyId := createHeaderPolicyResp.HttpHeaderPolicyId
ref := &shared.HTTPHeaderPolicyRef{
IsPrior: false,
IsOn: true,
@@ -70,7 +70,7 @@ func (this *IndexAction) RunGet(params struct {
this.ErrorPage(err)
return
}
headerPolicyId := createHeaderPolicyResp.HeaderPolicyId
headerPolicyId := createHeaderPolicyResp.HttpHeaderPolicyId
ref := &shared.HTTPHeaderPolicyRef{
IsPrior: false,
IsOn: true,

View File

@@ -65,14 +65,14 @@ func (this *SettingAction) RunPost(params struct {
// TODO 校验配置
reverseProxyConfig := &serverconfigs.ReverseProxyConfig{}
var reverseProxyConfig = &serverconfigs.ReverseProxyConfig{}
err := json.Unmarshal(params.ReverseProxyJSON, reverseProxyConfig)
if err != nil {
this.ErrorPage(err)
return
}
err = reverseProxyConfig.Init()
err = reverseProxyConfig.Init(nil)
if err != nil {
this.Fail("配置校验失败:" + err.Error())
}

View File

@@ -65,14 +65,14 @@ func (this *SettingAction) RunPost(params struct {
// TODO 校验配置
reverseProxyConfig := &serverconfigs.ReverseProxyConfig{}
var reverseProxyConfig = &serverconfigs.ReverseProxyConfig{}
err := json.Unmarshal(params.ReverseProxyJSON, reverseProxyConfig)
if err != nil {
this.ErrorPage(err)
return
}
err = reverseProxyConfig.Init()
err = reverseProxyConfig.Init(nil)
if err != nil {
this.Fail("配置校验失败:" + err.Error())
}

View File

@@ -72,7 +72,7 @@ func (this *SettingAction) RunPost(params struct {
return
}
err = reverseProxyConfig.Init()
err = reverseProxyConfig.Init(nil)
if err != nil {
this.Fail("配置校验失败:" + err.Error())
}

View File

@@ -99,6 +99,7 @@ func (this *IndexAction) RunGet(params struct {
TrafficOutAsc: params.TrafficOutOrder == "asc",
UserId: params.UserId,
IgnoreServerNames: true,
IgnoreSSLCerts: true,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -30,6 +30,9 @@ func (this *IndexAction) RunGet(params struct {
this.ErrorPage(err)
return
}
this.Data["regions"] = ""
this.Data["isp"] = ""
if regionResp.IpRegion != nil {
var regionName = regionResp.IpRegion.Summary
@@ -39,10 +42,8 @@ func (this *IndexAction) RunGet(params struct {
regionName = regionName[:index]
}
this.Data["regions"] = regionName
} else {
this.Data["regions"] = ""
this.Data["isp"] = regionResp.IpRegion.Isp
}
this.Data["isp"] = regionResp.IpRegion.Isp
// IP列表
ipListResp, err := this.RPC().IPListRPC().FindEnabledIPListContainsIP(this.AdminContext(), &pb.FindEnabledIPListContainsIPRequest{

View File

@@ -41,6 +41,10 @@ func (this *UpdateRefsAction) RunPost(params struct {
// 校验配置
var cacheConfig = webConfig.Cache
if cacheConfig == nil {
this.Success()
return
}
var refs = []*serverconfigs.HTTPCacheRef{}
err = json.Unmarshal(params.RefsJSON, &refs)

View File

@@ -48,7 +48,10 @@ func (this *UpdateCNAMEPopupAction) RunPost(params struct {
this.FailField("dnsName", "CNAME中只能包含数字、英文字母")
}
serverResp, err := this.RPC().ServerRPC().FindEnabledServer(this.AdminContext(), &pb.FindEnabledServerRequest{ServerId: params.ServerId})
serverResp, err := this.RPC().ServerRPC().FindEnabledServer(this.AdminContext(), &pb.FindEnabledServerRequest{
ServerId: params.ServerId,
IgnoreSSLCerts: true,
})
if err != nil {
this.ErrorPage(err)
return

View File

@@ -40,13 +40,13 @@ func (this *CreateDeletePopupAction) RunPost(params struct {
Field("name", params.Name).
Require("名称不能为空")
policyConfigResp, err := this.RPC().HTTPHeaderPolicyRPC().FindEnabledHTTPHeaderPolicyConfig(this.AdminContext(), &pb.FindEnabledHTTPHeaderPolicyConfigRequest{HeaderPolicyId: params.HeaderPolicyId})
policyConfigResp, err := this.RPC().HTTPHeaderPolicyRPC().FindEnabledHTTPHeaderPolicyConfig(this.AdminContext(), &pb.FindEnabledHTTPHeaderPolicyConfigRequest{HttpHeaderPolicyId: params.HeaderPolicyId})
if err != nil {
this.ErrorPage(err)
return
}
policyConfig := &shared.HTTPHeaderPolicy{}
err = json.Unmarshal(policyConfigResp.HeaderPolicyJSON, policyConfig)
err = json.Unmarshal(policyConfigResp.HttpHeaderPolicyJSON, policyConfig)
if err != nil {
this.ErrorPage(err)
return
@@ -55,8 +55,8 @@ func (this *CreateDeletePopupAction) RunPost(params struct {
deleteHeaders := policyConfig.DeleteHeaders
deleteHeaders = append(deleteHeaders, params.Name)
_, err = this.RPC().HTTPHeaderPolicyRPC().UpdateHTTPHeaderPolicyDeletingHeaders(this.AdminContext(), &pb.UpdateHTTPHeaderPolicyDeletingHeadersRequest{
HeaderPolicyId: params.HeaderPolicyId,
HeaderNames: deleteHeaders,
HttpHeaderPolicyId: params.HeaderPolicyId,
HeaderNames: deleteHeaders,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -52,13 +52,13 @@ func (this *CreateSetPopupAction) RunPost(params struct {
Field("name", params.Name).
Require("请输入Header名称")
configResp, err := this.RPC().HTTPHeaderPolicyRPC().FindEnabledHTTPHeaderPolicyConfig(this.AdminContext(), &pb.FindEnabledHTTPHeaderPolicyConfigRequest{HeaderPolicyId: params.HeaderPolicyId})
configResp, err := this.RPC().HTTPHeaderPolicyRPC().FindEnabledHTTPHeaderPolicyConfig(this.AdminContext(), &pb.FindEnabledHTTPHeaderPolicyConfigRequest{HttpHeaderPolicyId: params.HeaderPolicyId})
if err != nil {
this.ErrorPage(err)
return
}
policyConfig := &shared.HTTPHeaderPolicy{}
err = json.Unmarshal(configResp.HeaderPolicyJSON, policyConfig)
err = json.Unmarshal(configResp.HttpHeaderPolicyJSON, policyConfig)
if err != nil {
this.ErrorPage(err)
return
@@ -135,8 +135,8 @@ func (this *CreateSetPopupAction) RunPost(params struct {
}
_, err = this.RPC().HTTPHeaderPolicyRPC().UpdateHTTPHeaderPolicySettingHeaders(this.AdminContext(), &pb.UpdateHTTPHeaderPolicySettingHeadersRequest{
HeaderPolicyId: params.HeaderPolicyId,
HeadersJSON: refsJSON,
HttpHeaderPolicyId: params.HeaderPolicyId,
HeadersJSON: refsJSON,
})
if err != nil {
this.ErrorPage(err)

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