Compare commits

..

376 Commits

Author SHA1 Message Date
刘祥超
0ff8aecbea commit components.js 2023-06-18 19:51:47 +08:00
刘祥超
e6cae84764 优化文字提示 2023-06-18 16:19:16 +08:00
刘祥超
2d2d2931f1 commit components.js 2023-06-17 21:53:08 +08:00
刘祥超
69ad5e7d1f 安装mysql时预先安装 numactl-libs 2023-06-17 21:53:00 +08:00
刘祥超
97abc6aeb9 缓存条件类型增加“URL通配符” 2023-06-16 11:34:39 +08:00
刘祥超
8cf37dd37c 优化文字提示 2023-06-16 09:54:51 +08:00
刘祥超
e11d091ac5 优化错误提示 2023-06-16 08:17:16 +08:00
刘祥超
e952dfcedd 提交components.js 2023-06-15 15:25:01 +08:00
刘祥超
2565d5cab2 缓存条件增加"强制返回区间内容"选项 2023-06-15 15:15:11 +08:00
刘祥超
e4e66c74bc 更新Components.js 2023-06-13 20:52:46 +08:00
刘祥超
6607c41823 实现自动下载升级版本 2023-06-13 20:52:37 +08:00
刘祥超
43570f20b5 优化OSS源站相关代码 2023-06-13 15:40:40 +08:00
刘祥超
9ea7a93206 优化左侧主菜单显示 2023-06-12 19:50:53 +08:00
刘祥超
dec4922fe5 优化左侧主菜单显示 2023-06-12 19:46:37 +08:00
刘祥超
ef1f95b347 IP列表可以搜索单个IP 2023-06-12 18:29:11 +08:00
刘祥超
7317312f68 版本号改为1.2.0 2023-06-12 14:42:48 +08:00
刘祥超
2965e8df65 commit components.js 2023-06-12 11:16:56 +08:00
刘祥超
4fdcc7fae9 优化缓存条件添加表单 2023-06-10 17:58:03 +08:00
刘祥超
6be8f7539d 缓存条件类型支持“全站” 2023-06-10 17:35:45 +08:00
刘祥超
a6de65fe39 迁移后API地址确认页面提示更详细的API节点配置错误 2023-06-10 16:31:20 +08:00
刘祥超
fc5dae5a8e 优化源站列表界面 2023-06-09 17:46:15 +08:00
刘祥超
db69454d32 压缩utils.js 2023-06-09 17:29:14 +08:00
刘祥超
c824335b0e 增加备用音频 2023-06-09 17:28:49 +08:00
刘祥超
1f1ce11078 优化OSS相关代码 2023-06-08 17:50:22 +08:00
刘祥超
0ec1571d37 初步实现对象存储源站 2023-06-07 17:24:56 +08:00
刘祥超
407e8b52a5 允许在集群设置 -- “网站设置” 中设置节点IP访问显示的内容 2023-06-05 19:26:55 +08:00
刘祥超
1be13a151d 网站全局设置增加“强制Ln请求“选项 2023-06-05 17:05:42 +08:00
刘祥超
4d9d417d7a 增加<dns-resolvers-config-box>组件 2023-06-05 12:33:30 +08:00
刘祥超
aaff8f0c4a 将API设置URL从/api改为/settings/api 2023-06-05 09:41:52 +08:00
刘祥超
8c064daf3f “服务分组”改为“网站分组” 2023-06-04 10:30:57 +08:00
刘祥超
b0b3dae147 增加并发任务函数 2023-06-03 11:13:05 +08:00
刘祥超
2ea2be43d3 更新components.js 2023-06-03 09:09:18 +08:00
刘祥超
81f7364e93 改进文字提示 2023-06-02 16:33:24 +08:00
刘祥超
c530869530 优化代码 2023-06-01 18:07:46 +08:00
刘祥超
586ccd90ec 初步实现HTTP3 2023-06-01 17:44:39 +08:00
刘祥超
ece237c49f 增加不能登录的敏感区域 2023-06-01 15:56:45 +08:00
刘祥超
11da5ca98c 线图坐标增加xColorFunc参数 2023-05-29 13:56:32 +08:00
刘祥超
276a68bda6 Update Dockerfile 2023-05-29 09:08:59 +08:00
刘祥超
fba89d197a 优化交互 2023-05-28 20:24:58 +08:00
刘祥超
b3259e2489 commit components.js 2023-05-28 19:22:05 +08:00
刘祥超
880704dda0 优化WAF“跳转‘动作显示 2023-05-28 17:22:20 +08:00
刘祥超
d5e92e9c09 WAF增加“跳转”动作 2023-05-28 17:11:45 +08:00
刘祥超
df2f5692bd 版本号改为1.1.0 2023-05-28 16:06:05 +08:00
刘祥超
dacc99b8b6 优化文字 2023-05-28 15:17:33 +08:00
刘祥超
0e8ade3b61 手动执行健康检查时提示用户当前集群尚未部署网站 2023-05-28 15:06:53 +08:00
刘祥超
fe2d8f6261 优化界面 2023-05-28 14:41:38 +08:00
刘祥超
927381039b 优化界面 2023-05-28 11:31:59 +08:00
刘祥超
6b95947acb 集群列表页也增加“创建节点”链接 2023-05-27 19:55:06 +08:00
刘祥超
b05e53ce58 登录界面屏蔽个别敏感区域 2023-05-27 18:12:40 +08:00
刘祥超
a2546582f2 优化界面 2023-05-27 17:11:18 +08:00
刘祥超
a023b3401e 优化界面 2023-05-27 17:05:17 +08:00
刘祥超
07142e9872 删除EdgePlus 2023-05-27 16:46:30 +08:00
刘祥超
f5fea6c34d 创建节点表单显示剩余配额 2023-05-27 15:33:08 +08:00
刘祥超
5eb0fb3422 正则表达式填写多行时提示用户需要转换成竖杠(|)符号 2023-05-27 14:44:39 +08:00
刘祥超
fa915e7c35 部分“服务”文字改为“网站” 2023-05-26 11:55:21 +08:00
刘祥超
2b7905d31c 优化文字和界面 2023-05-26 10:52:45 +08:00
刘祥超
9cbbf37add 优化界面 2023-05-26 10:31:03 +08:00
刘祥超
642a0c3b0d 优化界面 2023-05-25 17:42:22 +08:00
刘祥超
ff47a19250 优化界面 2023-05-25 17:29:55 +08:00
刘祥超
1fc1a3fbe3 WAF国家/地区封禁、省份封禁增加例外URL、限制URL 2023-05-25 12:02:19 +08:00
刘祥超
996172bd69 改进界面 2023-05-25 10:59:18 +08:00
刘祥超
df038314ef commit components.js 2023-05-24 17:21:05 +08:00
刘祥超
67881c2e34 网站全局设置中增加“自动匹配证书”选项 2023-05-24 17:20:28 +08:00
刘祥超
9c691a17b2 优化集群菜单 2023-05-23 19:50:19 +08:00
刘祥超
54df86a4a2 优化文字提示 2023-05-23 19:15:35 +08:00
刘祥超
70aff759d7 优化文字显示 2023-05-23 11:25:25 +08:00
刘祥超
d903cc6e3b 优化源站列表专属域名显示 2023-05-23 11:25:13 +08:00
刘祥超
c520271ab5 实现自定义页面组件 2023-05-22 17:30:46 +08:00
刘祥超
f6936224d9 调整集群设置菜单位置 2023-05-22 09:49:56 +08:00
刘祥超
283984a6a6 HTTP Header中支持设置非标Header 2023-05-19 19:52:10 +08:00
刘祥超
c4fc09f72a 优化HTTP Header页面 2023-05-19 17:40:00 +08:00
刘祥超
efdbabfa04 优化HTTP Header页面文字提示 2023-05-19 17:34:54 +08:00
刘祥超
a43af333fd 优化文字提示 2023-05-19 16:51:49 +08:00
刘祥超
1f5894ff82 HTTP Header - CORS跨域设置增加多个选项 2023-05-19 16:32:28 +08:00
刘祥超
79b4054e31 在节点列表显示租期、是否为备用节点等信息 2023-05-19 11:11:34 +08:00
刘祥超
376d7d0c78 修改部分代码的联系方式 2023-05-19 11:09:57 +08:00
刘祥超
c8b85330ed 删除QQ群信息 2023-05-17 19:22:56 +08:00
刘祥超
c65dffee13 实现基础的智能调度 2023-05-17 18:41:27 +08:00
刘祥超
275320ad9b 如果管理系统前端有反向代理,则不要自动从HTTP跳转到HTTPS 2023-05-07 09:28:18 +08:00
刘祥超
f71cdf9065 防盗链增加”同时检查Origin选项“ 2023-05-02 17:05:35 +08:00
刘祥超
8e0a239de9 优化WAF CC2规则说明文字 2023-05-02 17:04:40 +08:00
刘祥超
ed2b95bc3f 集群设置中“服务设置”改为“网站设置” 2023-04-26 14:04:49 +08:00
刘祥超
2e23ee4c66 优化健康检查界面 2023-04-26 10:48:44 +08:00
刘祥超
58def52e30 数据库手动清理页面增加按表名和按占用空间排序/优化数据库相关界面 2023-04-26 09:49:28 +08:00
刘祥超
aea5ef3b68 Update Dockerfile 2023-04-24 10:29:31 +08:00
刘祥超
f2e14cf0a6 版本号修改为1.0.4 2023-04-24 10:17:05 +08:00
刘祥超
bc9b6d1595 为集群选择DNS域名时自动排序并不显示已下线域名 2023-04-24 09:38:57 +08:00
刘祥超
fd11b62390 更新components.js 2023-04-23 20:13:23 +08:00
刘祥超
8db00a5ab5 远程升级API节点时自动上传边缘节点安装文件 2023-04-23 19:48:07 +08:00
刘祥超
a6757e9374 申请ACME证书时可以指定平台用户 2023-04-23 15:01:43 +08:00
刘祥超
e0fe385404 证书申请任务可以使用用户筛选 2023-04-23 11:03:07 +08:00
刘祥超
f9f4258ec1 证书列表可以使用用户筛选 2023-04-23 10:44:37 +08:00
刘祥超
f939d563ad 选择证书时,如果证书列表为空,则提示可以搜索未指定用户证书 2023-04-22 12:04:04 +08:00
刘祥超
fad03add54 “网站服务”改为“网站”、“网站列表” 2023-04-21 17:51:25 +08:00
刘祥超
9ab6dab081 SESSION读取失败时不强制重新登录 2023-04-21 15:39:51 +08:00
刘祥超
5a55268830 修复在HTTP/1.x下开多个窗口访问非常慢的问题 2023-04-19 21:03:46 +08:00
刘祥超
fc3769239d 节点列表页CPU、内存使用比例增加百分号 2023-04-19 19:26:49 +08:00
刘祥超
c9fb3153eb 安全设置增加“检查客户端指纹"和"检查客户端区域"选项 2023-04-19 18:25:10 +08:00
刘祥超
a31f9ed9c5 节点列表负载数据如果是0,则显示0.00 2023-04-18 11:23:49 +08:00
刘祥超
2f75828ba4 优化滚动条在firefox上的显示 2023-04-11 16:33:22 +08:00
刘祥超
89679ec358 版本号改为1.1.0 2023-04-10 21:03:41 +08:00
刘祥超
98f77f52df Update Dockerfile 2023-04-10 09:20:59 +08:00
刘祥超
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
刘祥超
567ffc80b6 优化<domains-box>组件 2022-11-02 09:58:59 +08:00
刘祥超
5d15a08ac8 防盗链使用<domains-box>组件 2022-11-02 09:47:17 +08:00
刘祥超
2b84037346 修改一处注释 2022-11-01 21:16:48 +08:00
刘祥超
536f11e617 修复datetime组件时间戳生成错误 2022-10-31 11:08:36 +08:00
刘祥超
5d367a384e 修改版本为0.5.7 2022-10-31 10:39:10 +08:00
刘祥超
04933e6bf0 重置配置的同时也重置本地API节点的配置 2022-10-30 20:06:59 +08:00
刘祥超
12abe9aa69 优化界面显示 2022-10-30 20:06:38 +08:00
刘祥超
cba642a4bc 多处域名列表支持批量输入 2022-10-28 17:59:46 +08:00
刘祥超
9fce0ac0aa 优化API节点界面 2022-10-27 19:45:03 +08:00
刘祥超
681812b619 优化申请证书界面 2022-10-27 19:44:50 +08:00
刘祥超
6d0be57698 更新components.js 2022-10-27 11:19:10 +08:00
刘祥超
1d521602e1 节点SSH登录自动使用集群设置 2022-10-26 19:23:50 +08:00
刘祥超
2f67e7937a 集群全局服务配置中增加多个访问日志相关选项 2022-10-26 17:49:25 +08:00
刘祥超
e0078a42dc URL跳转中增加域名跳转、端口跳转 2022-10-26 16:06:50 +08:00
刘祥超
3ec875d49d 节点设置中增加“通过IP名单”选项 2022-10-26 10:41:42 +08:00
刘祥超
35028d1310 开源版本Dashboard不再显示用户节点相关信息 2022-10-26 09:51:09 +08:00
刘祥超
7ba3d7c4bb 修复窄屏显示时有些内容无法隐藏的问题 2022-10-26 09:30:41 +08:00
刘祥超
c05e64098c 更新components.js 2022-10-25 14:36:40 +08:00
刘祥超
e82ee56a2c IP名单支持模糊查询 2022-10-25 10:15:17 +08:00
刘祥超
5681b61aea 在选择区域弹出框中可以不选区域直接确定返回 2022-10-25 09:43:31 +08:00
刘祥超
d6ce7eab25 修复域名解析--集群中单节点多IP时无法修改IP的Bug 2022-10-24 16:33:56 +08:00
刘祥超
bf597fe41c 添加、修改、删除HTTP Header时增加通用Header提示 2022-10-24 15:42:18 +08:00
刘祥超
6a920f964f 集群服务设置--访问日志中可以设置是否只记录通用Header 2022-10-24 14:39:32 +08:00
刘祥超
977b66ba4e 防盗链功能增加禁止的来源域名 2022-10-24 10:20:44 +08:00
刘祥超
4659c29358 优化界面显示 2022-10-23 11:44:09 +08:00
刘祥超
e3bc95b275 管理安全设置中域名列表支持通配符 2022-10-21 18:36:33 +08:00
刘祥超
bf51255e13 优化区域列表界面 2022-10-20 15:17:03 +08:00
刘祥超
915fe6837b 区域设置中增加节点快速设置区域的功能 2022-10-20 15:11:26 +08:00
刘祥超
d1237215c0 更新components.js 2022-10-20 11:07:07 +08:00
刘祥超
8ba5cfdfa6 优化代码 2022-10-19 19:55:51 +08:00
刘祥超
0d1097425d 优化界面和文字 2022-10-18 10:31:20 +08:00
刘祥超
0789a9ecc8 优化数字格式化 2022-10-16 09:36:36 +08:00
刘祥超
400f764b74 优化数字格式化 2022-10-15 19:33:07 +08:00
刘祥超
46e036e3a4 删除代码 2022-10-15 16:58:45 +08:00
刘祥超
17e6264af8 删除不需要的代码 2022-10-14 09:57:24 +08:00
刘祥超
468b6ae125 添加源站的时候也提示8443和8080对应的协议 2022-10-07 09:01:22 +08:00
刘祥超
e5f5ee4f6a 提交components.js 2022-10-03 21:11:02 +08:00
刘祥超
3695082ec2 服务列表页带宽使用比特代替字节 2022-10-03 19:25:20 +08:00
刘祥超
f384d86014 版本修改为0.5.6 2022-10-01 08:49:00 +08:00
624 changed files with 18361 additions and 5062 deletions

View File

@@ -54,7 +54,10 @@
* [管理平台](https://github.com/TeaOSLab/EdgeAdmin)
## 联系我们
有什么问题和建议都可以加入QQ群 `659832182` 或者 [Telegram群](https://t.me/+5kVCMGxQhZxiODY9)
有什么问题和建议都可以加入 [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

@@ -1 +1 @@
这个目录下我们列举了所有需要公开声明的第三方License如果有遗漏烦请告知 iwind.liu@gmail.com。再次感谢这些开源软件项目和贡献人员
这个目录下我们列举了所有需要公开声明的第三方License如果有遗漏烦请告知 goedge.cdn@gmail.com。再次感谢这些开源软件项目和贡献人员

View File

@@ -1,6 +1,8 @@
package main
import (
"bytes"
"flag"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/apps"
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
@@ -9,10 +11,13 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/nodes"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web"
"github.com/iwind/TeaGo/Tea"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/gosock/pkg/gosock"
"log"
"os"
"os/exec"
"time"
)
@@ -20,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").
@@ -34,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()
@@ -53,6 +58,20 @@ func main() {
fmt.Println("[ERROR]reset failed: " + err.Error())
return
}
// reset local api
var apiNodeExe = Tea.Root + "/edge-api/bin/edge-api"
_, err = os.Stat(apiNodeExe)
if err == nil {
var cmd = exec.Command(apiNodeExe, "reset")
var stderr = &bytes.Buffer{}
cmd.Stderr = stderr
err = cmd.Run()
if err != nil {
fmt.Println("reset api node failed: " + stderr.String())
}
}
fmt.Println("done")
})
app.On("recover", func() {
@@ -120,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="goedge.cdn@gmail.com"
ENV TZ "Asia/Shanghai"
ENV VERSION 1.1.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

@@ -167,7 +167,7 @@ func AllModuleMaps() []maps.Map {
"url": "/dashboard",
},
{
"name": "网站服务",
"name": "网站列表",
"code": AdminModuleCodeServer,
"url": "/servers",
},

View File

@@ -29,7 +29,7 @@ func LoadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
return nil, err
}
v := reflect.Indirect(reflect.ValueOf(config)).Interface().(systemconfigs.SecurityConfig)
var v = reflect.Indirect(reflect.ValueOf(config)).Interface().(systemconfigs.SecurityConfig)
return &v, nil
}
@@ -83,7 +83,12 @@ func loadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
return sharedSecurityConfig, nil
}
config := &systemconfigs.SecurityConfig{}
var config = &systemconfigs.SecurityConfig{
Frame: FrameSameOrigin,
AllowLocal: true,
CheckClientFingerprint: false,
CheckClientRegion: true,
}
err = json.Unmarshal(resp.ValueJSON, config)
if err != nil {
logs.Println("[SECURITY_MANAGER]" + err.Error())
@@ -100,7 +105,9 @@ func loadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
func defaultSecurityConfig() *systemconfigs.SecurityConfig {
return &systemconfigs.SecurityConfig{
Frame: FrameSameOrigin,
AllowLocal: true,
Frame: FrameSameOrigin,
AllowLocal: true,
CheckClientFingerprint: false,
CheckClientRegion: true,
}
}

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
@@ -61,10 +61,11 @@ func LoadAPIConfig() (*APIConfig, error) {
// ResetAPIConfig 重置配置
func ResetAPIConfig() error {
filename := "api.yaml"
var filename = "api.yaml"
// 重置 configs/api.yaml
{
configFile := Tea.ConfigFile(filename)
var configFile = Tea.ConfigFile(filename)
stat, err := os.Stat(configFile)
if err == nil && !stat.IsDir() {
err = os.Remove(configFile)
@@ -77,7 +78,7 @@ func ResetAPIConfig() error {
// 重置 ~/.edge-admin/api.yaml
homeDir, homeErr := os.UserHomeDir()
if homeErr == nil {
configFile := homeDir + "/." + teaconst.ProcessName + "/" + filename
var configFile = homeDir + "/." + teaconst.ProcessName + "/" + filename
stat, err := os.Stat(configFile)
if err == nil && !stat.IsDir() {
err = os.Remove(configFile)
@@ -89,7 +90,7 @@ func ResetAPIConfig() error {
// 重置 /etc/edge-admin/api.yaml
{
configFile := "/etc/" + teaconst.ProcessName + "/" + filename
var configFile = "/etc/" + teaconst.ProcessName + "/" + filename
stat, err := os.Stat(configFile)
if err == nil && !stat.IsDir() {
err = os.Remove(configFile)
@@ -154,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.5"
Version = "1.2.0"
APINodeVersion = "0.5.5"
APINodeVersion = "1.2.0"
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"
)
@@ -84,11 +85,19 @@ func (this *AdminNode) Run() {
// 启动API节点
this.startAPINode()
// 启动IP库
this.startIPLibrary()
// 启动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 +369,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,18 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package nodes
import (
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"github.com/iwind/TeaGo/logs"
)
// 启动IP库
func (this *AdminNode) startIPLibrary() {
logs.Println("[NODE]initializing ip library ...")
err := iplibrary.InitDefault()
if err != nil {
logs.Println("[NODE]initialize ip library failed: "+err.Error())
}
}

View File

@@ -0,0 +1,98 @@
// 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())
result["@error"] = 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

@@ -98,10 +98,6 @@ func (this *RPCClient) NodeRegionRPC() pb.NodeRegionServiceClient {
return pb.NewNodeRegionServiceClient(this.pickConn())
}
func (this *RPCClient) NodePriceItemRPC() pb.NodePriceItemServiceClient {
return pb.NewNodePriceItemServiceClient(this.pickConn())
}
func (this *RPCClient) NodeIPAddressRPC() pb.NodeIPAddressServiceClient {
return pb.NewNodeIPAddressServiceClient(this.pickConn())
}
@@ -411,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())
}
@@ -455,14 +455,6 @@ func (this *RPCClient) ServerStatBoardChartRPC() pb.ServerStatBoardChartServiceC
return pb.NewServerStatBoardChartServiceClient(this.pickConn())
}
func (this *RPCClient) PlanRPC() pb.PlanServiceClient {
return pb.NewPlanServiceClient(this.pickConn())
}
func (this *RPCClient) UserPlanRPC() pb.UserPlanServiceClient {
return pb.NewUserPlanServiceClient(this.pickConn())
}
func (this *RPCClient) TrafficDailyStatRPC() pb.TrafficDailyStatServiceClient {
return pb.NewTrafficDailyStatServiceClient(this.pickConn())
}
@@ -552,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" {
@@ -583,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 关闭
@@ -651,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

@@ -2,6 +2,9 @@ package rpc
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"strings"
"sync"
)
@@ -28,3 +31,39 @@ func SharedRPC() (*RPCClient, error) {
sharedRPC = client
return sharedRPC, nil
}
// IsConnError 是否为连接错误
func IsConnError(err error) bool {
if err == nil {
return false
}
// 检查是否为连接错误
statusErr, ok := status.FromError(err)
if ok {
var errorCode = statusErr.Code()
return errorCode == codes.Unavailable || errorCode == codes.Canceled
}
if strings.Contains(err.Error(), "code = Canceled") {
return true
}
return false
}
// IsUnimplementedError 检查是否为未实现错误
func IsUnimplementedError(err error) bool {
if err == nil {
return false
}
statusErr, ok := status.FromError(err)
if ok {
if statusErr.Code() == codes.Unimplemented {
return true
}
}
return false
}

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())
@@ -118,7 +119,7 @@ func (this *CheckUpdatesTask) Loop() error {
var vMap = maps.NewMap(version)
if vMap.GetString("code") == "admin" {
var latestVersion = vMap.GetString("version")
if stringutil.VersionCompare(teaconst.Version, latestVersion) < 0 {
if stringutil.VersionCompare(teaconst.Version, latestVersion) < 0 && (len(config.IgnoredVersion) == 0 || stringutil.VersionCompare(latestVersion, config.IgnoredVersion) > 0) {
teaconst.NewVersionCode = latestVersion
teaconst.NewVersionDownloadURL = dlHost + vMap.GetString("url")
return nil

View File

@@ -0,0 +1,79 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package apinodeutils
import (
"crypto/md5"
"fmt"
"io"
"os"
)
// DeployFile 部署文件描述
type DeployFile struct {
OS string
Arch string
Version string
Path string
}
// Sum 计算概要
func (this *DeployFile) Sum() (string, error) {
fp, err := os.Open(this.Path)
if err != nil {
return "", err
}
defer func() {
_ = fp.Close()
}()
m := md5.New()
buffer := make([]byte, 128*1024)
for {
n, err := fp.Read(buffer)
if err != nil {
if err == io.EOF {
break
}
return "", err
}
_, err = m.Write(buffer[:n])
if err != nil {
return "", err
}
}
sum := m.Sum(nil)
return fmt.Sprintf("%x", sum), nil
}
// Read 读取一个片段数据
func (this *DeployFile) Read(offset int64) (data []byte, newOffset int64, err error) {
fp, err := os.Open(this.Path)
if err != nil {
return nil, offset, err
}
defer func() {
_ = fp.Close()
}()
stat, err := fp.Stat()
if err != nil {
return nil, offset, err
}
if offset >= stat.Size() {
return nil, offset, io.EOF
}
_, err = fp.Seek(offset, io.SeekStart)
if err != nil {
return nil, offset, err
}
buffer := make([]byte, 128*1024)
n, err := fp.Read(buffer)
if err != nil {
return nil, offset, err
}
return buffer[:n], offset + int64(n), nil
}

View File

@@ -0,0 +1,96 @@
package apinodeutils
import (
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/files"
stringutil "github.com/iwind/TeaGo/utils/string"
"regexp"
)
// DeployManager 节点部署文件管理器
// 如果节点部署文件有变化需要重启API节点以便于生效
type DeployManager struct {
dir string
}
// NewDeployManager 获取新节点部署文件管理器
func NewDeployManager() *DeployManager {
var manager = &DeployManager{
dir: Tea.Root + "/edge-api/deploy",
}
manager.LoadNodeFiles()
manager.LoadNSNodeFiles()
return manager
}
// LoadNodeFiles 加载所有边缘节点文件
func (this *DeployManager) LoadNodeFiles() []*DeployFile {
var keyMap = map[string]*DeployFile{} // key => File
var reg = regexp.MustCompile(`^edge-node-(\w+)-(\w+)-v([0-9.]+)\.zip$`)
for _, file := range files.NewFile(this.dir).List() {
var name = file.Name()
if !reg.MatchString(name) {
continue
}
var matches = reg.FindStringSubmatch(name)
var osName = matches[1]
var arch = matches[2]
var version = matches[3]
var key = osName + "_" + arch
oldFile, ok := keyMap[key]
if ok && stringutil.VersionCompare(oldFile.Version, version) > 0 {
continue
}
keyMap[key] = &DeployFile{
OS: osName,
Arch: arch,
Version: version,
Path: file.Path(),
}
}
var result = []*DeployFile{}
for _, v := range keyMap {
result = append(result, v)
}
return result
}
// LoadNSNodeFiles 加载所有NS节点安装文件
func (this *DeployManager) LoadNSNodeFiles() []*DeployFile {
var keyMap = map[string]*DeployFile{} // key => File
var reg = regexp.MustCompile(`^edge-dns-(\w+)-(\w+)-v([0-9.]+)\.zip$`)
for _, file := range files.NewFile(this.dir).List() {
var name = file.Name()
if !reg.MatchString(name) {
continue
}
var matches = reg.FindStringSubmatch(name)
var osName = matches[1]
var arch = matches[2]
var version = matches[3]
var key = osName + "_" + arch
oldFile, ok := keyMap[key]
if ok && stringutil.VersionCompare(oldFile.Version, version) > 0 {
continue
}
keyMap[key] = &DeployFile{
OS: osName,
Arch: arch,
Version: version,
Path: file.Path(),
}
}
var result = []*DeployFile{}
for _, v := range keyMap {
result = append(result, v)
}
return result
}

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,349 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package apinodeutils
import (
"compress/gzip"
"context"
"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
}
// 升级边缘节点
err = this.upgradeNodes(sharedClient.Context(0), rpcClient)
if err != nil {
return err
}
// 升级NS节点
err = this.upgradeNSNodes(sharedClient.Context(0), rpcClient)
if err != nil {
return err
}
// 升级API节点
err = this.upgradeAPINode(sharedClient.Context(0), rpcClient)
if err != nil {
return errors.New("upgrade api node failed: " + err.Error())
}
return nil
}
// Progress 查看升级进程
func (this *Upgrader) Progress() *Progress {
return this.progress
}
// 升级API节点
func (this *Upgrader) upgradeAPINode(ctx context.Context, rpcClient *rpc.RPCClient) error {
versionResp, err := rpcClient.APINodeRPC().FindCurrentAPINodeVersion(ctx, &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 := lookupLocalVersion()
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) upgradeNodes(ctx context.Context, rpcClient *rpc.RPCClient) error {
// 本地的
var manager = NewDeployManager()
var localFileMap = map[string]*DeployFile{} // os_arch => *DeployFile
for _, deployFile := range manager.LoadNodeFiles() {
localFileMap[deployFile.OS+"_"+deployFile.Arch] = deployFile
}
remoteFilesResp, err := rpcClient.APINodeRPC().FindLatestDeployFiles(ctx, &pb.FindLatestDeployFilesRequest{})
if err != nil {
return err
}
var remoteFileMap = map[string]*pb.FindLatestDeployFilesResponse_DeployFile{} // os_arch => *DeployFile
for _, nodeFile := range remoteFilesResp.NodeDeployFiles {
remoteFileMap[nodeFile.Os+"_"+nodeFile.Arch] = nodeFile
}
// 对比版本
for key, deployFile := range localFileMap {
remoteDeployFile, ok := remoteFileMap[key]
if !ok || stringutil.VersionCompare(remoteDeployFile.Version, deployFile.Version) < 0 {
err = this.uploadNodeDeployFile(ctx, rpcClient, deployFile.Path)
if err != nil {
return errors.New("upload deploy file '" + filepath.Base(deployFile.Path) + "' failed: " + err.Error())
}
}
}
return nil
}
// 升级NS节点
func (this *Upgrader) upgradeNSNodes(ctx context.Context, rpcClient *rpc.RPCClient) error {
// 本地的
var manager = NewDeployManager()
var localFileMap = map[string]*DeployFile{} // os_arch => *DeployFile
for _, deployFile := range manager.LoadNSNodeFiles() {
localFileMap[deployFile.OS+"_"+deployFile.Arch] = deployFile
}
remoteFilesResp, err := rpcClient.APINodeRPC().FindLatestDeployFiles(ctx, &pb.FindLatestDeployFilesRequest{})
if err != nil {
return err
}
var remoteFileMap = map[string]*pb.FindLatestDeployFilesResponse_DeployFile{} // os_arch => *DeployFile
for _, nodeFile := range remoteFilesResp.NsNodeDeployFiles {
remoteFileMap[nodeFile.Os+"_"+nodeFile.Arch] = nodeFile
}
// 对比版本
for key, deployFile := range localFileMap {
remoteDeployFile, ok := remoteFileMap[key]
if !ok || stringutil.VersionCompare(remoteDeployFile.Version, deployFile.Version) < 0 {
err = this.uploadNodeDeployFile(ctx, rpcClient, deployFile.Path)
if err != nil {
return errors.New("upload deploy file '" + filepath.Base(deployFile.Path) + "' failed: " + err.Error())
}
}
}
return nil
}
// 上传节点文件
func (this *Upgrader) uploadNodeDeployFile(ctx context.Context, rpcClient *rpc.RPCClient, path string) error {
fp, err := os.Open(path)
if err != nil {
return err
}
defer func() {
_ = fp.Close()
}()
var buf = make([]byte, 128*4096)
var isFirst = true
var hash = md5.New()
for {
n, err := fp.Read(buf)
if n > 0 {
hash.Write(buf[:n])
_, uploadErr := rpcClient.APINodeRPC().UploadDeployFileToAPINode(ctx, &pb.UploadDeployFileToAPINodeRequest{
Filename: filepath.Base(path),
Sum: "",
ChunkData: buf[:n],
IsFirstChunk: isFirst,
IsLastChunk: false,
})
if uploadErr != nil {
return uploadErr
}
isFirst = false
}
if err != nil {
if err == io.EOF {
err = nil
_, uploadErr := rpcClient.APINodeRPC().UploadDeployFileToAPINode(ctx, &pb.UploadDeployFileToAPINodeRequest{
Filename: filepath.Base(path),
Sum: fmt.Sprintf("%x", hash.Sum(nil)),
ChunkData: nil,
IsFirstChunk: false,
IsLastChunk: true,
})
if uploadErr != nil {
return uploadErr
}
break
}
return err
}
}
return nil
}

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,77 @@
// 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 := lookupLocalVersion()
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 lookupLocalVersion() (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

@@ -3,7 +3,9 @@ package numberutils
import (
"fmt"
"github.com/iwind/TeaGo/types"
"regexp"
"strconv"
"strings"
)
func FormatInt64(value int64) string {
@@ -28,17 +30,35 @@ func FormatBytes(bytes int64) string {
if bytes < Pow1024(1) {
return FormatInt64(bytes) + "B"
} else if bytes < Pow1024(2) {
return fmt.Sprintf("%.2fKB", float64(bytes)/float64(Pow1024(1)))
return TrimZeroSuffix(fmt.Sprintf("%.2fKB", float64(bytes)/float64(Pow1024(1))))
} else if bytes < Pow1024(3) {
return fmt.Sprintf("%.2fMB", float64(bytes)/float64(Pow1024(2)))
return TrimZeroSuffix(fmt.Sprintf("%.2fMB", float64(bytes)/float64(Pow1024(2))))
} else if bytes < Pow1024(4) {
return fmt.Sprintf("%.2fGB", float64(bytes)/float64(Pow1024(3)))
return TrimZeroSuffix(fmt.Sprintf("%.2fGB", float64(bytes)/float64(Pow1024(3))))
} else if bytes < Pow1024(5) {
return fmt.Sprintf("%.2fTB", float64(bytes)/float64(Pow1024(4)))
return TrimZeroSuffix(fmt.Sprintf("%.2fTB", float64(bytes)/float64(Pow1024(4))))
} else if bytes < Pow1024(6) {
return fmt.Sprintf("%.2fPB", float64(bytes)/float64(Pow1024(5)))
return TrimZeroSuffix(fmt.Sprintf("%.2fPB", float64(bytes)/float64(Pow1024(5))))
} else {
return fmt.Sprintf("%.2fEB", float64(bytes)/float64(Pow1024(6)))
return TrimZeroSuffix(fmt.Sprintf("%.2fEB", float64(bytes)/float64(Pow1024(6))))
}
}
func FormatBits(bits int64) string {
if bits < Pow1024(1) {
return FormatInt64(bits) + "bps"
} else if bits < Pow1024(2) {
return TrimZeroSuffix(fmt.Sprintf("%.4fKbps", float64(bits)/float64(Pow1024(1))))
} else if bits < Pow1024(3) {
return TrimZeroSuffix(fmt.Sprintf("%.4fMbps", float64(bits)/float64(Pow1024(2))))
} else if bits < Pow1024(4) {
return TrimZeroSuffix(fmt.Sprintf("%.4fGbps", float64(bits)/float64(Pow1024(3))))
} else if bits < Pow1024(5) {
return TrimZeroSuffix(fmt.Sprintf("%.4fTbps", float64(bits)/float64(Pow1024(4))))
} else if bits < Pow1024(6) {
return TrimZeroSuffix(fmt.Sprintf("%.4fPbps", float64(bits)/float64(Pow1024(5))))
} else {
return TrimZeroSuffix(fmt.Sprintf("%.4fEbps", float64(bits)/float64(Pow1024(6))))
}
}
@@ -55,22 +75,92 @@ func FormatCount(count int64) string {
return fmt.Sprintf("%.1fB", float32(count)/1000/1000/1000)
}
func FormatFloat(f interface{}, decimal int) string {
func FormatFloat(f any, decimal int) string {
if f == nil {
return ""
}
switch x := f.(type) {
case float32, float64:
var s = fmt.Sprintf("%."+types.String(decimal)+"f", x)
// 分隔
var dotIndex = strings.Index(s, ".")
if dotIndex > 0 {
var d = s[:dotIndex]
var f2 = s[dotIndex:]
f2 = strings.TrimRight(strings.TrimRight(f2, "0"), ".")
return formatDigit(d) + f2
}
return s
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
return types.String(x)
return formatDigit(types.String(x))
case string:
return x
}
return ""
}
func FormatFloat2(f interface{}) string {
func FormatFloat2(f any) string {
return FormatFloat(f, 2)
}
// PadFloatZero 为浮点型数字字符串填充足够的0
func PadFloatZero(s string, countZero int) string {
if countZero <= 0 {
return s
}
if len(s) == 0 {
s = "0"
}
var index = strings.Index(s, ".")
if index < 0 {
return s + "." + strings.Repeat("0", countZero)
}
var decimalLen = len(s) - 1 - index
if decimalLen < countZero {
return s + strings.Repeat("0", countZero-decimalLen)
}
return s
}
var decimalReg = regexp.MustCompile(`^(\d+\.\d+)([a-zA-Z]+)?$`)
// TrimZeroSuffix 去除小数数字尾部多余的0
func TrimZeroSuffix(s string) string {
var matches = decimalReg.FindStringSubmatch(s)
if len(matches) < 3 {
return s
}
return strings.TrimRight(strings.TrimRight(matches[1], "0"), ".") + matches[2]
}
func formatDigit(d string) string {
if len(d) == 0 {
return d
}
var prefix = ""
if d[0] < '0' || d[0] > '9' {
prefix = d[:1]
d = d[1:]
}
var l = len(d)
if l > 3 {
var pieces = l / 3
var commIndex = l - pieces*3
var d2 = ""
if commIndex > 0 {
d2 = d[:commIndex] + ", "
}
for i := 0; i < pieces; i++ {
d2 += d[commIndex+i*3 : commIndex+i*3+3]
if i != pieces-1 {
d2 += ", "
}
}
return prefix + d2
}
return prefix + d
}

View File

@@ -4,6 +4,7 @@ package numberutils_test
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/iwind/TeaGo/assert"
"testing"
)
@@ -33,4 +34,53 @@ func TestFormatFloat(t *testing.T) {
t.Log(numberutils.FormatFloat(100.23456, 2))
t.Log(numberutils.FormatFloat(100.000023, 2))
t.Log(numberutils.FormatFloat(100.012, 2))
t.Log(numberutils.FormatFloat(123.012, 2))
t.Log(numberutils.FormatFloat(1234.012, 2))
t.Log(numberutils.FormatFloat(12345.012, 2))
t.Log(numberutils.FormatFloat(123456.012, 2))
t.Log(numberutils.FormatFloat(1234567.012, 2))
t.Log(numberutils.FormatFloat(12345678.012, 2))
t.Log(numberutils.FormatFloat(123456789.012, 2))
t.Log(numberutils.FormatFloat(1234567890.012, 2))
t.Log(numberutils.FormatFloat(123, 2))
t.Log(numberutils.FormatFloat(1234, 2))
t.Log(numberutils.FormatFloat(1234.00001, 4))
t.Log(numberutils.FormatFloat(1234.56700, 4))
t.Log(numberutils.FormatFloat(-1234.56700, 2))
t.Log(numberutils.FormatFloat(-221745.12, 2))
}
func TestFormatFloat2(t *testing.T) {
t.Log(numberutils.FormatFloat2(0))
t.Log(numberutils.FormatFloat2(0.0))
t.Log(numberutils.FormatFloat2(1.23456))
t.Log(numberutils.FormatFloat2(1.0))
}
func TestPadFloatZero(t *testing.T) {
var a = assert.NewAssertion(t)
a.IsTrue(numberutils.PadFloatZero("1", 0) == "1")
a.IsTrue(numberutils.PadFloatZero("1", 2) == "1.00")
a.IsTrue(numberutils.PadFloatZero("1.1", 2) == "1.10")
a.IsTrue(numberutils.PadFloatZero("1.12", 2) == "1.12")
a.IsTrue(numberutils.PadFloatZero("1.123", 2) == "1.123")
a.IsTrue(numberutils.PadFloatZero("10000.123", 2) == "10000.123")
a.IsTrue(numberutils.PadFloatZero("", 2) == "0.00")
}
func TestTrimZeroSuffix(t *testing.T) {
for _, s := range []string{
"1",
"1.0000",
"1.10",
"100",
"100.0000",
"100.0",
"100.0123",
"100.0010",
"100.000KB",
"100.010MB",
} {
t.Log(s, "=>", numberutils.TrimZeroSuffix(s))
}
}

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

View File

@@ -0,0 +1,56 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package taskutils
import (
"errors"
"reflect"
"sync"
)
func RunConcurrent(tasks any, concurrent int, f func(task any)) error {
if tasks == nil {
return nil
}
var tasksValue = reflect.ValueOf(tasks)
if tasksValue.Type().Kind() != reflect.Slice {
return errors.New("ony works for slice")
}
var countTasks = tasksValue.Len()
if countTasks == 0 {
return nil
}
if concurrent <= 0 {
concurrent = 8
}
if concurrent > countTasks {
concurrent = countTasks
}
var taskChan = make(chan any, countTasks)
for i := 0; i < countTasks; i++ {
taskChan <- tasksValue.Index(i).Interface()
}
var wg = &sync.WaitGroup{}
wg.Add(concurrent)
for i := 0; i < concurrent; i++ {
go func() {
defer wg.Done()
for {
select {
case task := <-taskChan:
f(task)
default:
return
}
}
}()
}
wg.Wait()
return nil
}

View File

@@ -0,0 +1,17 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package taskutils_test
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils/taskutils"
"testing"
)
func TestRunConcurrent(t *testing.T) {
err := taskutils.RunConcurrent([]string{"a", "b", "c", "d", "e"}, 3, func(task any) {
t.Log("run", task)
})
if err != nil {
t.Fatal(err)
}
}

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

@@ -2,37 +2,49 @@ package actionutils
import (
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type TabItem struct {
Name string `json:"name"`
SubName string `json:"subName"`
URL string `json:"url"`
Icon string `json:"icon"`
IsActive bool `json:"isActive"`
IsRight bool `json:"isRight"`
IsTitle bool `json:"isTitle"`
IsDisabled bool `json:"isDisabled"`
}
// Tabbar Tabbar定义
type Tabbar struct {
items []maps.Map
items []*TabItem
}
// NewTabbar 获取新对象
func NewTabbar() *Tabbar {
return &Tabbar{
items: []maps.Map{},
items: []*TabItem{},
}
}
// Add 添加菜单项
func (this *Tabbar) Add(name string, subName string, url string, icon string, active bool) maps.Map {
m := maps.Map{
"name": name,
"subName": subName,
"url": url,
"icon": icon,
"active": active,
"right": false,
func (this *Tabbar) Add(name string, subName string, url string, icon string, active bool) *TabItem {
var m = &TabItem{
Name: name,
SubName: subName,
URL: url,
Icon: icon,
IsActive: active,
IsRight: false,
IsTitle: false,
IsDisabled: false,
}
this.items = append(this.items, m)
return m
}
// Items 取得所有的Items
func (this *Tabbar) Items() []maps.Map {
func (this *Tabbar) Items() []*TabItem {
return this.items
}

View File

@@ -24,10 +24,19 @@ import (
// Fail 提示服务器错误信息
func Fail(action actions.ActionWrapper, err error) {
if err != nil {
logs.Println("[" + reflect.TypeOf(action).String() + "]" + findStack(err.Error()))
if err == nil {
err = errors.New("unknown error")
}
action.Object().Fail(teaconst.ErrServer + "" + err.Error() + "")
logs.Println("[" + reflect.TypeOf(action).String() + "]" + findStack(err.Error()))
_, _, isLocalAPI, issuesHTML := parseAPIErr(action, err)
if isLocalAPI && len(issuesHTML) > 0 {
action.Object().Fail(teaconst.ErrServer + "" + err.Error() + ";最近一次错误提示:" + issuesHTML + "")
} else {
action.Object().Fail(teaconst.ErrServer + "" + err.Error() + "")
}
}
// FailPage 提示页面错误信息
@@ -38,71 +47,15 @@ func FailPage(action actions.ActionWrapper, err error) {
logs.Println("[" + reflect.TypeOf(action).String() + "]" + findStack(err.Error()))
// 当前API终端地址
var apiEndpoints = []string{}
apiConfig, apiConfigErr := configs.LoadAPIConfig()
if apiConfigErr == nil && apiConfig != nil {
apiEndpoints = append(apiEndpoints, apiConfig.RPC.Endpoints...)
}
var isRPCConnError bool
err, isRPCConnError = rpcerrors.HumanError(err, apiEndpoints, Tea.ConfigFile("api.yaml"))
var apiNodeIsStarting = false
var apiNodeProgress = ""
if isRPCConnError {
// API节点是否正在启动
var sock = gosock.NewTmpSock("edge-api")
reply, err := sock.SendTimeout(&gosock.Command{
Code: "starting",
Params: nil,
}, 1*time.Second)
if err == nil && reply != nil {
var params = maps.NewMap(reply.Params)
if params.GetBool("isStarting") {
apiNodeIsStarting = true
var progressMap = params.GetMap("progress")
apiNodeProgress = progressMap.GetString("description")
}
}
}
action.Object().ResponseWriter.WriteHeader(http.StatusInternalServerError)
if len(action.Object().Request.Header.Get("X-Requested-With")) > 0 {
action.Object().WriteString(teaconst.ErrServer)
} else {
// 本地的一些错误提示
var isLocalAPI = false
if isRPCConnError {
host, _, hostErr := net.SplitHostPort(action.Object().Request.Host)
if hostErr == nil {
for _, endpoint := range apiEndpoints {
if strings.HasPrefix(endpoint, "http://"+host) || strings.HasPrefix(endpoint, "https://"+host) || strings.HasPrefix(endpoint, host) {
isLocalAPI = true
break
}
}
}
}
var issuesHTML = ""
if isLocalAPI {
// 读取本地API节点的issues
issuesData, issuesErr := os.ReadFile(Tea.Root + "/edge-api/logs/issues.log")
if issuesErr == nil {
var issueMaps = []maps.Map{}
issuesErr = json.Unmarshal(issuesData, &issueMaps)
if issuesErr == nil && len(issueMaps) > 0 {
var issueMap = issueMaps[0]
issuesHTML = "本地API节点启动错误" + issueMap.GetString("message") + ",处理建议:" + issueMap.GetString("suggestion")
}
}
}
apiNodeIsStarting, apiNodeProgress, _, issuesHTML := parseAPIErr(action, err)
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 +69,7 @@ func FailPage(action actions.ActionWrapper, err error) {
html += "<div class=\"red\">API节点正在启动请耐心等待完成"
if len(apiNodeProgress) > 0 {
html += "" + apiNodeProgress
html += "" + apiNodeProgress + "(刷新当前页面查看最新状态)"
}
html += "</div>"
@@ -187,3 +140,61 @@ func findStack(err string) string {
return err
}
// 分析API节点的错误信息
func parseAPIErr(action actions.ActionWrapper, err error) (apiNodeIsStarting bool, apiNodeProgress string, isLocalAPI bool, issuesHTML string) {
// 当前API终端地址
var apiEndpoints = []string{}
apiConfig, apiConfigErr := configs.LoadAPIConfig()
if apiConfigErr == nil && apiConfig != nil {
apiEndpoints = append(apiEndpoints, apiConfig.RPC.Endpoints...)
}
var isRPCConnError bool
err, isRPCConnError = rpcerrors.HumanError(err, apiEndpoints, Tea.ConfigFile("api.yaml"))
if isRPCConnError {
// API节点是否正在启动
var sock = gosock.NewTmpSock("edge-api")
reply, err := sock.SendTimeout(&gosock.Command{
Code: "starting",
Params: nil,
}, 1*time.Second)
if err == nil && reply != nil {
var params = maps.NewMap(reply.Params)
if params.GetBool("isStarting") {
apiNodeIsStarting = true
var progressMap = params.GetMap("progress")
apiNodeProgress = progressMap.GetString("description")
}
}
}
// 本地的一些错误提示
if isRPCConnError {
host, _, hostErr := net.SplitHostPort(action.Object().Request.Host)
if hostErr == nil {
for _, endpoint := range apiEndpoints {
if strings.HasPrefix(endpoint, "http://"+host) || strings.HasPrefix(endpoint, "https://"+host) || strings.HasPrefix(endpoint, host) {
isLocalAPI = true
break
}
}
}
}
if isLocalAPI {
// 读取本地API节点的issues
issuesData, issuesErr := os.ReadFile(Tea.Root + "/edge-api/logs/issues.log")
if issuesErr == nil {
var issueMaps = []maps.Map{}
issuesErr = json.Unmarshal(issuesData, &issueMaps)
if issuesErr == nil && len(issueMaps) > 0 {
var issueMap = issueMaps[0]
issuesHTML = "本地API节点启动错误" + issueMap.GetString("message") + ",处理建议:" + issueMap.GetString("suggestion")
}
}
}
return
}

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

@@ -39,6 +39,17 @@ func (this *CreateBatchAction) RunGet(params struct {
}
this.Data["leftMenuItems"] = leftMenuItems
// 限额
maxNodes, leftNodes, err := this.findNodesQuota()
if err != nil {
this.ErrorPage(err)
return
}
this.Data["quota"] = maps.Map{
"maxNodes": maxNodes,
"leftNodes": leftNodes,
}
this.Show()
}

View File

@@ -0,0 +1,8 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package cluster
func (this *CreateBatchAction) findNodesQuota() (maxNodes int32, leftNodes int32, err error) {
return
}

View File

@@ -30,6 +30,11 @@ func (this *CreateNodeAction) Init() {
func (this *CreateNodeAction) RunGet(params struct {
ClusterId int64
}) {
if params.ClusterId <= 0 {
this.RedirectURL("/clusters")
return
}
var leftMenuItems = []maps.Map{
{
"name": "单个创建",
@@ -92,6 +97,17 @@ func (this *CreateNodeAction) RunGet(params struct {
// 安装文件下载
this.Data["installerFiles"] = clusterutils.ListInstallerFiles()
// 限额
maxNodes, leftNodes, err := this.findNodesQuota()
if err != nil {
this.ErrorPage(err)
return
}
this.Data["quota"] = maps.Map{
"maxNodes": maxNodes,
"leftNodes": leftNodes,
}
this.Show()
}

View File

@@ -0,0 +1,8 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package cluster
func (this *CreateNodeAction) findNodesQuota() (maxNodes int32, leftNodes int32, err error) {
return
}

View File

@@ -20,6 +20,7 @@ func init() {
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
Helper(clusters.NewClusterHelper()).
Data("teaMenu", "clusters").
Data("teaSubMenu", "cluster").
Prefix("/clusters/cluster").
Get("", new(IndexAction)).
Get("/nodes", new(NodesAction)).

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,33 @@ 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,
"isBackup": dnsInfo.IsBackupForCluster || dnsInfo.IsBackupForGroup,
"isOffline": dnsInfo.IsOffline,
})
}
}
@@ -178,8 +206,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,24 +328,46 @@ 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,
"ipAddresses": ipAddressMaps,
"cluster": clusterMap,
"secondaryClusters": secondaryClustersMaps,
"login": loginMap,
"installDir": node.InstallDir,
"isInstalled": node.IsInstalled,
"uniqueId": node.UniqueId,
"secret": node.Secret,
"maxCPU": node.MaxCPU,
"isOn": node.IsOn,
"records": recordMaps,
"routes": routeMaps,
"level": node.Level,
"levelInfo": nodeconfigs.FindNodeLevel(int(node.Level)),
"lnAddrs": lnAddrs,
"id": node.Id,
"name": node.Name,
"ipAddresses": ipAddressMaps,
"cluster": clusterMap,
"secondaryClusters": secondaryClustersMaps,
"login": loginMap,
"installDir": node.InstallDir,
"isInstalled": node.IsInstalled,
"uniqueId": node.UniqueId,
"secret": node.Secret,
"maxCPU": node.MaxCPU,
"isOn": node.IsOn,
"records": recordMaps,
"routes": routeMaps,
"level": node.Level,
"levelInfo": nodeconfigs.FindNodeLevel(int(node.Level)),
"lnAddrs": lnAddrs,
"enableIPLists": node.EnableIPLists,
"apiNodeAddrs": apiNodeAddrStrings,
"offlineDay": node.OfflineDay,
"isOffline": len(node.OfflineDay) > 0 && node.OfflineDay < timeutil.Format("Ymd"),
"isBackupForCluster": node.IsBackupForCluster,
"isBackupForGroup": node.IsBackupForGroup,
"status": maps.Map{
"isActive": status.IsActive,
@@ -337,6 +387,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,
})
}
@@ -100,14 +111,15 @@ func (this *UpdateAction) RunGet(params struct {
}
var nodeMap = maps.Map{
"id": node.Id,
"name": node.Name,
"ipAddresses": ipAddressMaps,
"cluster": clusterMap,
"isOn": node.IsOn,
"group": groupMap,
"region": regionMap,
"level": node.Level,
"id": node.Id,
"name": node.Name,
"ipAddresses": ipAddressMaps,
"cluster": clusterMap,
"isOn": node.IsOn,
"group": groupMap,
"region": regionMap,
"level": node.Level,
"enableIPLists": node.EnableIPLists,
}
if node.LnAddrs == nil {
@@ -157,6 +169,7 @@ func (this *UpdateAction) RunPost(params struct {
IsOn bool
Level int32
LnAddrs []string
EnableIPLists bool
Must *actions.Must
}) {
@@ -234,6 +247,7 @@ func (this *UpdateAction) RunPost(params struct {
IsOn: params.IsOn,
Level: params.Level,
LnAddrs: lnAddrs,
EnableIPLists: params.EnableIPLists,
})
if err != nil {
this.ErrorPage(err)

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,
})
}
@@ -194,6 +209,8 @@ func (this *NodesAction) RunGet(params struct {
"isInstalled": node.IsInstalled,
"isOn": node.IsOn,
"isUp": node.IsUp,
"isBackup": node.IsBackupForCluster || node.IsBackupForGroup,
"offlineDay": node.OfflineDay,
"installStatus": maps.Map{
"isRunning": node.InstallStatus.IsRunning,
"isFinished": node.InstallStatus.IsFinished,
@@ -201,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": 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.PadFloatZero(numberutils.FormatFloat2(status.Load1m), 2),
"countConnections": status.ConnectionCount,
},
"cluster": maps.Map{
"id": node.NodeCluster.Id,

View File

@@ -64,6 +64,7 @@ p { color: grey; }
</body>
</html>`
}
this.Data["httpAllDomainMismatchActionContentHTML"] = httpAllDomainMismatchActionContentHTML
this.Show()
@@ -77,9 +78,25 @@ func (this *IndexAction) RunPost(params struct {
HttpAllAllowMismatchDomainsJSON []byte
HttpAllAllowNodeIP bool
HttpAllDefaultDomain string
HttpAllNodeIPPageHTML string
HttpAllNodeIPShowPage bool
HttpAllForceLnRequest bool
HttpAllSupportsLowVersionHTTP bool
HttpAllMatchCertFromAllServers 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
}) {
@@ -119,12 +136,33 @@ func (this *IndexAction) RunPost(params struct {
}
}
// 域名
config.HTTPAll.AllowMismatchDomains = allowMismatchDomains
config.HTTPAll.AllowNodeIP = params.HttpAllAllowNodeIP
config.HTTPAll.DefaultDomain = params.HttpAllDefaultDomain
config.HTTPAll.NodeIPShowPage = params.HttpAllNodeIPShowPage
config.HTTPAll.NodeIPPageHTML = params.HttpAllNodeIPPageHTML
// HTTP All
config.HTTPAll.SupportsLowVersionHTTP = params.HttpAllSupportsLowVersionHTTP
config.HTTPAll.MatchCertFromAllServers = params.HttpAllMatchCertFromAllServers
config.HTTPAll.ForceLnRequest = params.HttpAllForceLnRequest
// 访问日志
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

@@ -15,7 +15,17 @@ func (this *RunPopupAction) Init() {
this.Nav("", "", "")
}
func (this *RunPopupAction) RunGet(params struct{}) {
func (this *RunPopupAction) RunGet(params struct {
ClusterId int64
}) {
// 检查是否已部署服务
countServersResp, err := this.RPC().ServerRPC().CountAllEnabledServersWithNodeClusterId(this.AdminContext(), &pb.CountAllEnabledServersWithNodeClusterIdRequest{NodeClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["hasServers"] = countServersResp.Count > 0
this.Show()
}

View File

@@ -45,7 +45,7 @@ func (this *IndexAction) RunGet(params struct {
this.ErrorPage(err)
return
}
grant := grantResp.NodeGrant
var grant = grantResp.NodeGrant
if grant != nil {
grantMap = maps.Map{
"id": grant.Id,
@@ -79,6 +79,29 @@ func (this *IndexAction) RunGet(params struct {
}
}
// SSH参数
var sshParams = nodeconfigs.DefaultSSHParams()
if len(cluster.SshParamsJSON) > 0 {
err = json.Unmarshal(cluster.SshParamsJSON, sshParams)
if err != nil {
this.ErrorPage(err)
return
}
}
// 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,
@@ -89,6 +112,8 @@ func (this *IndexAction) RunGet(params struct {
"clock": clockConfig,
"autoRemoteStart": cluster.AutoRemoteStart,
"autoInstallNftables": cluster.AutoInstallNftables,
"sshParams": sshParams,
"domainName": fullDomainName,
}
// 默认值
@@ -104,12 +129,14 @@ func (this *IndexAction) RunPost(params struct {
ClusterId int64
Name string
GrantId int64
SshParamsPort int
InstallDir string
TimeZone string
NodeMaxThreads int32
AutoOpenPorts bool
ClockAutoSync bool
ClockServer string
ClockCheckChrony bool
AutoRemoteStart bool
AutoInstallNftables bool
@@ -129,9 +156,20 @@ func (this *IndexAction) RunPost(params struct {
Lte(int64(nodeconfigs.DefaultMaxThreadsMax), "单节点最大线程数最大值不能大于"+types.String(nodeconfigs.DefaultMaxThreadsMax))
}
// ssh
var sshParams = nodeconfigs.DefaultSSHParams()
sshParams.Port = params.SshParamsPort
sshParamsJSON, err := json.Marshal(sshParams)
if err != nil {
this.ErrorPage(err)
return
}
// clock
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)
@@ -155,6 +193,7 @@ func (this *IndexAction) RunPost(params struct {
ClockJSON: clockConfigJSON,
AutoRemoteStart: params.AutoRemoteStart,
AutoInstallNftables: params.AutoInstallNftables,
SshParamsJSON: sshParamsJSON,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -24,6 +24,7 @@ func init() {
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
Helper(clusters.NewClusterHelper()).
Prefix("/clusters/cluster/settings").
Data("teaSubMenu", "cluster").
GetPost("", new(IndexAction)).
// 健康检查

View File

@@ -29,9 +29,9 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
action.Data["teaMenu"] = "clusters"
selectedTabbar := action.Data.GetString("mainTab")
clusterId := action.ParamInt64("clusterId")
clusterIdString := strconv.FormatInt(clusterId, 10)
var selectedTabbar = action.Data.GetString("mainTab")
var clusterId = action.ParamInt64("clusterId")
var clusterIdString = strconv.FormatInt(clusterId, 10)
action.Data["clusterId"] = clusterId
if clusterId > 0 {
@@ -57,18 +57,49 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
return
}
var nodeId = action.ParamInt64("nodeId")
var isInCluster = nodeId <= 0
var tabbar = actionutils.NewTabbar()
tabbar.Add("集群列表", "", "/clusters", "", false)
if teaconst.IsPlus {
tabbar.Add("集群看板", "", "/clusters/cluster/boards?clusterId="+clusterIdString, "board", selectedTabbar == "board")
{
var url = "/clusters"
if !isInCluster {
url = "/clusters/cluster/nodes?clusterId=" + clusterIdString
}
tabbar.Add("", "", url, "arrow left", false)
}
{
var url = "/clusters/cluster?clusterId=" + clusterIdString
if !isInCluster {
url = "/clusters/cluster/nodes?clusterId=" + clusterIdString
}
var item = tabbar.Add(cluster.Name, "", url, "angle right", true)
item.IsTitle = true
}
if teaconst.IsPlus {
{
var item = tabbar.Add("集群看板", "", "/clusters/cluster/boards?clusterId="+clusterIdString, "chart line area", selectedTabbar == "board")
item.IsDisabled = !isInCluster
}
}
{
var item = tabbar.Add("节点列表", "", "/clusters/cluster/nodes?clusterId="+clusterIdString, "server", selectedTabbar == "node")
item.IsDisabled = !isInCluster
}
{
var item = tabbar.Add("集群设置", "", "/clusters/cluster/settings?clusterId="+clusterIdString, "setting", selectedTabbar == "setting")
item.IsDisabled = !isInCluster
}
{
var item = tabbar.Add("删除集群", "", "/clusters/cluster/delete?clusterId="+clusterIdString, "trash", selectedTabbar == "delete")
item.IsDisabled = !isInCluster
}
tabbar.Add("集群节点", "", "/clusters/cluster/nodes?clusterId="+clusterIdString, "server", selectedTabbar == "node")
tabbar.Add("集群设置", "", "/clusters/cluster/settings?clusterId="+clusterIdString, "setting", selectedTabbar == "setting")
tabbar.Add("删除集群", "", "/clusters/cluster/delete?clusterId="+clusterIdString, "trash", selectedTabbar == "delete")
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)
@@ -97,14 +128,39 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, info *pb.F
"isActive": selectedItem == "basic",
"isOn": true,
})
items = append(items, maps.Map{
"name": "缓存设置",
"name": "DNS设置",
"url": "/clusters/cluster/settings/dns?clusterId=" + clusterId,
"isActive": selectedItem == "dns",
"isOn": cluster.DnsDomainId > 0 || len(cluster.DnsName) > 0,
})
items = append(items, maps.Map{
"name": "健康检查",
"url": "/clusters/cluster/settings/health?clusterId=" + clusterId,
"isActive": selectedItem == "health",
"isOn": info != nil && info.HealthCheckIsOn,
})
items = append(items, maps.Map{
"name": "-",
})
items = append(items, maps.Map{
"name": "网站设置",
"url": "/clusters/cluster/settings/global-server-config?clusterId=" + clusterId,
"isActive": selectedItem == "globalServerConfig",
"isOn": true,
})
items = append(items, maps.Map{
"name": "缓存策略",
"url": "/clusters/cluster/settings/cache?clusterId=" + clusterId,
"isActive": selectedItem == "cache",
"isOn": cluster.HttpCachePolicyId > 0,
})
items = append(items, maps.Map{
"name": "WAF设置",
"name": "WAF策略",
"url": "/clusters/cluster/settings/waf?clusterId=" + clusterId,
"isActive": selectedItem == "waf",
"isOn": cluster.HttpFirewallPolicyId > 0,
@@ -132,20 +188,6 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, info *pb.F
"isActive": false,
})
items = append(items, maps.Map{
"name": "DNS设置",
"url": "/clusters/cluster/settings/dns?clusterId=" + clusterId,
"isActive": selectedItem == "dns",
"isOn": cluster.DnsDomainId > 0 || len(cluster.DnsName) > 0,
})
items = append(items, maps.Map{
"name": "健康检查",
"url": "/clusters/cluster/settings/health?clusterId=" + clusterId,
"isActive": selectedItem == "health",
"isOn": info != nil && info.HealthCheckIsOn,
})
items = append(items, maps.Map{
"name": "DDoS防护",
"url": "/clusters/cluster/settings/ddos-protection?clusterId=" + clusterId,
@@ -153,12 +195,6 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, info *pb.F
"isOn": info != nil && info.HasDDoSProtection,
})
items = append(items, maps.Map{
"name": "服务设置",
"url": "/clusters/cluster/settings/global-server-config?clusterId=" + clusterId,
"isActive": selectedItem == "globalServerConfig",
})
items = append(items, maps.Map{
"name": "-",
})

View File

@@ -0,0 +1,52 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package clusters
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/types"
)
type CreateNodeAction struct {
actionutils.ParentAction
}
func (this *CreateNodeAction) Init() {
this.Nav("", "cluster", "createNode")
}
func (this *CreateNodeAction) RunGet(params struct{}) {
// 集群总数
totalClustersResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClusters(this.AdminContext(), &pb.CountAllEnabledNodeClustersRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["totalNodeClusters"] = totalClustersResp.Count
// 节点总数
totalNodesResp, err := this.RPC().NodeRPC().CountAllEnabledNodes(this.AdminContext(), &pb.CountAllEnabledNodesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["totalNodes"] = totalNodesResp.Count
// 如果只有一个默认集群,那么直接跳转到集群
clustersResp, err := this.RPC().NodeClusterRPC().ListEnabledNodeClusters(this.AdminContext(), &pb.ListEnabledNodeClustersRequest{
Offset: 0,
Size: 2,
Keyword: "",
})
if err != nil {
this.ErrorPage(err)
return
}
if len(clustersResp.NodeClusters) == 1 {
this.RedirectURL("/clusters/cluster/createNode?clusterId=" + types.String(clustersResp.NodeClusters[0].Id))
return
}
this.Show()
}

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

@@ -13,9 +13,11 @@ func init() {
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
Helper(clusterutils.NewClustersHelper()).
Data("teaMenu", "clusters").
Data("teaSubMenu", "cluster").
Prefix("/clusters").
Get("", new(IndexAction)).
GetPost("/create", new(CreateAction)).
GetPost("/createNode", new(CreateNodeAction)).
Post("/pin", new(PinAction)).
Get("/nodes", new(NodesAction)).

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)
}
@@ -196,6 +211,8 @@ func (this *NodesAction) RunGet(params struct {
"isInstalled": node.IsInstalled,
"isOn": node.IsOn,
"isUp": node.IsUp,
"isBackup": node.IsBackupForCluster || node.IsBackupForGroup,
"offlineDay": node.OfflineDay,
"installStatus": maps.Map{
"isRunning": node.InstallStatus.IsRunning,
"isFinished": node.InstallStatus.IsFinished,
@@ -203,16 +220,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.PadFloatZero(numberutils.FormatFloat2(status.Load1m), 2),
"countConnections": status.ConnectionCount,
},
"cluster": maps.Map{
"id": node.NodeCluster.Id,

View File

@@ -11,7 +11,7 @@ type IndexAction struct {
}
func (this *IndexAction) Init() {
this.Nav("", "", "region")
this.Nav("", "", "index")
}
func (this *IndexAction) RunGet(params struct{}) {
@@ -20,7 +20,7 @@ func (this *IndexAction) RunGet(params struct{}) {
this.ErrorPage(err)
return
}
regionMaps := []maps.Map{}
var regionMaps = []maps.Map{}
for _, region := range regionsResp.NodeRegions {
countNodesResp, err := this.RPC().NodeRPC().CountAllEnabledNodesWithNodeRegionId(this.AdminContext(), &pb.CountAllEnabledNodesWithNodeRegionIdRequest{NodeRegionId: region.Id})
if err != nil {

View File

@@ -18,9 +18,13 @@ func init() {
GetPost("/updatePopup", new(UpdatePopupAction)).
Post("/delete", new(DeleteAction)).
Post("/sort", new(SortAction)).
Get("/nodes", new(NodesAction)).
GetPost("/updateNodeRegionPopup", new(UpdateNodeRegionPopupAction)).
//
GetPost("/selectPopup", new(SelectPopupAction)).
GetPost("/prices", new(PricesAction)).
GetPost("/updatePricePopup", new(UpdatePricePopupAction)).
//
EndAll()
})
}

View File

@@ -1,50 +0,0 @@
package items
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions/regionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct{}) {
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
Name string
BitsFrom int64
BitsTo int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
params.Must.
Field("name", params.Name).
Require("请输入名称").
Field("bitsFrom", params.BitsFrom).
Gte(0, "请输入不小于0的整数").
Field("bitsTo", params.BitsTo).
Gte(0, "请输入不小于0的整数")
createResp, err := this.RPC().NodePriceItemRPC().CreateNodePriceItem(this.AdminContext(), &pb.CreateNodePriceItemRequest{
Name: params.Name,
Type: regionutils.PriceTypeTraffic,
BitsFrom: params.BitsFrom * 1000 * 1000,
BitsTo: params.BitsTo * 1000 * 1000,
})
if err != nil {
this.ErrorPage(err)
return
}
defer this.CreateLogInfo("创建流量价格项目", createResp.NodePriceItemId)
this.Success()
}

View File

@@ -1,24 +0,0 @@
package items
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
ItemId int64
}) {
defer this.CreateLogInfo("删除流量价格项目 %d", params.ItemId)
_, err := this.RPC().NodePriceItemRPC().DeleteNodePriceItem(this.AdminContext(), &pb.DeleteNodePriceItemRequest{NodePriceItemId: params.ItemId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -1,21 +0,0 @@
package items
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
Data("teaMenu", "clusters").
Data("teaSubMenu", "region").
Prefix("/clusters/regions/items").
GetPost("/createPopup", new(CreatePopupAction)).
GetPost("/updatePopup", new(UpdatePopupAction)).
Post("/delete", new(DeleteAction)).
EndAll()
})
}

View File

@@ -1,73 +0,0 @@
package items
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
ItemId int64
}) {
itemResp, err := this.RPC().NodePriceItemRPC().FindEnabledNodePriceItem(this.AdminContext(), &pb.FindEnabledNodePriceItemRequest{NodePriceItemId: params.ItemId})
if err != nil {
this.ErrorPage(err)
return
}
item := itemResp.NodePriceItem
if item == nil {
this.NotFound("nodePriceItem", params.ItemId)
return
}
this.Data["item"] = maps.Map{
"id": item.Id,
"name": item.Name,
"bitsFrom": item.BitsFrom,
"bitsTo": item.BitsTo,
}
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
ItemId int64
Name string
BitsFrom int64
BitsTo int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo("修改流量价格项目 %d", params.ItemId)
params.Must.
Field("name", params.Name).
Require("请输入名称").
Field("bitsFrom", params.BitsFrom).
Gte(0, "请输入不小于0的整数").
Field("bitsTo", params.BitsTo).
Gte(0, "请输入不小于0的整数")
_, err := this.RPC().NodePriceItemRPC().UpdateNodePriceItem(this.AdminContext(), &pb.UpdateNodePriceItemRequest{
NodePriceItemId: params.ItemId,
Name: params.Name,
BitsFrom: params.BitsFrom * 1000 * 1000,
BitsTo: params.BitsTo * 1000 * 1000,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,92 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package regions
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type NodesAction struct {
actionutils.ParentAction
}
func (this *NodesAction) Init() {
this.Nav("", "", "node")
}
func (this *NodesAction) RunGet(params struct {
RegionId int64
}) {
this.Data["regionId"] = params.RegionId
// 所有区域
regionsResp, err := this.RPC().NodeRegionRPC().FindAllAvailableNodeRegions(this.AdminContext(), &pb.FindAllAvailableNodeRegionsRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var regionMaps = []maps.Map{}
for _, region := range regionsResp.NodeRegions {
regionMaps = append(regionMaps, maps.Map{
"id": region.Id,
"name": region.Name,
})
}
this.Data["regions"] = regionMaps
// 节点数量
countResp, err := this.RPC().NodeRPC().CountAllNodeRegionInfo(this.AdminContext(), &pb.CountAllNodeRegionInfoRequest{NodeRegionId: params.RegionId})
if err != nil {
this.ErrorPage(err)
return
}
var page = this.NewPage(countResp.Count)
this.Data["page"] = page.AsHTML()
// 节点列表
var hasNodesWithoutRegion = false
nodesResp, err := this.RPC().NodeRPC().ListNodeRegionInfo(this.AdminContext(), &pb.ListNodeRegionInfoRequest{
NodeRegionId: params.RegionId,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var nodeMaps = []maps.Map{}
for _, node := range nodesResp.InfoList {
// region
var regionMap maps.Map
if node.NodeRegion != nil {
regionMap = maps.Map{
"id": node.NodeRegion.Id,
"name": node.NodeRegion.Name,
}
} else {
hasNodesWithoutRegion = true
}
// cluster
var clusterMap maps.Map
if node.NodeCluster != nil {
clusterMap = maps.Map{
"id": node.NodeCluster.Id,
"name": node.NodeCluster.Name,
}
}
nodeMaps = append(nodeMaps, maps.Map{
"id": node.Id,
"name": node.Name,
"region": regionMap,
"cluster": clusterMap,
})
}
this.Data["nodes"] = nodeMaps
this.Data["hasNodesWithoutRegion"] = hasNodesWithoutRegion
this.Show()
}

View File

@@ -1,86 +0,0 @@
package regions
import (
"encoding/json"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions/regionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type PricesAction struct {
actionutils.ParentAction
}
func (this *PricesAction) Init() {
this.Nav("", "", "price")
}
func (this *PricesAction) RunGet(params struct{}) {
// 所有价格项目
itemsResp, err := this.RPC().NodePriceItemRPC().FindAllAvailableNodePriceItems(this.AdminContext(), &pb.FindAllAvailableNodePriceItemsRequest{Type: regionutils.PriceTypeTraffic})
if err != nil {
this.ErrorPage(err)
return
}
itemMaps := []maps.Map{}
for _, item := range itemsResp.NodePriceItems {
itemMaps = append(itemMaps, maps.Map{
"id": item.Id,
"name": item.Name,
"bitsFromString": this.formatBits(item.BitsFrom),
"bitsToString": this.formatBits(item.BitsTo),
})
}
this.Data["items"] = itemMaps
// 所有区域
regionsResp, err := this.RPC().NodeRegionRPC().FindAllEnabledNodeRegions(this.AdminContext(), &pb.FindAllEnabledNodeRegionsRequest{})
if err != nil {
this.ErrorPage(err)
return
}
regionMaps := []maps.Map{}
for _, region := range regionsResp.NodeRegions {
pricesMap := map[string]float32{}
if len(region.PricesJSON) > 0 {
err = json.Unmarshal(region.PricesJSON, &pricesMap)
if err != nil {
this.ErrorPage(err)
return
}
}
regionMaps = append(regionMaps, maps.Map{
"id": region.Id,
"isOn": region.IsOn,
"name": region.Name,
"prices": pricesMap,
})
}
this.Data["regions"] = regionMaps
this.Show()
}
func (this *PricesAction) formatBits(bits int64) string {
sizeHuman := ""
if bits < 1000 {
sizeHuman = numberutils.FormatInt64(bits) + "BPS"
} else if bits < 1_000_000 {
sizeHuman = fmt.Sprintf("%.2fKBPS", float64(bits)/1000)
} else if bits < 1_000_000_000 {
sizeHuman = fmt.Sprintf("%.2fMBPS", float64(bits)/1000/1000)
} else if bits < 1_000_000_000_000 {
sizeHuman = fmt.Sprintf("%.2fGBPS", float64(bits)/1000/1000/1000)
} else if bits < 1_000_000_000_000_000 {
sizeHuman = fmt.Sprintf("%.2fTBPS", float64(bits)/1000/1000/1000/1000)
} else if bits < 1_000_000_000_000_000_000 {
sizeHuman = fmt.Sprintf("%.2fPBPS", float64(bits)/1000/1000/1000/1000/1000)
} else {
sizeHuman = fmt.Sprintf("%.2fEBPS", float64(bits)/1000/1000/1000/1000/1000/1000)
}
return sizeHuman
}

View File

@@ -1,5 +0,0 @@
package regionutils
const (
PriceTypeTraffic = "traffic"
)

View File

@@ -40,12 +40,18 @@ func (this *SelectPopupAction) RunPost(params struct {
Must *actions.Must
CSRF *actionutils.CSRF
}) {
if params.RegionId <= 0 {
this.Data["region"] = nil
this.Success()
return
}
regionResp, err := this.RPC().NodeRegionRPC().FindEnabledNodeRegion(this.AdminContext(), &pb.FindEnabledNodeRegionRequest{NodeRegionId: params.RegionId})
if err != nil {
this.ErrorPage(err)
return
}
region := regionResp.NodeRegion
var region = regionResp.NodeRegion
if region == nil {
this.NotFound("nodeRegion", params.RegionId)
return

View File

@@ -0,0 +1,97 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package regions
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdateNodeRegionPopupAction struct {
actionutils.ParentAction
}
func (this *UpdateNodeRegionPopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdateNodeRegionPopupAction) RunGet(params struct {
NodeId int64
RegionId int64
}) {
// node
nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
var node = nodeResp.Node
if node == nil {
this.NotFound("node", params.NodeId)
return
}
this.Data["node"] = maps.Map{
"id": node.Id,
"name": node.Name,
}
// region
this.Data["region"] = maps.Map{
"id": 0,
"name": "",
}
if params.RegionId > 0 {
regionResp, err := this.RPC().NodeRegionRPC().FindEnabledNodeRegion(this.AdminContext(), &pb.FindEnabledNodeRegionRequest{NodeRegionId: params.RegionId})
if err != nil {
this.ErrorPage(err)
return
}
var region = regionResp.NodeRegion
if region != nil {
this.Data["region"] = maps.Map{
"id": region.Id,
"name": region.Name,
}
}
}
// all regions
regionsResp, err := this.RPC().NodeRegionRPC().FindAllAvailableNodeRegions(this.AdminContext(), &pb.FindAllAvailableNodeRegionsRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var regionMaps = []maps.Map{}
for _, region := range regionsResp.NodeRegions {
regionMaps = append(regionMaps, maps.Map{
"id": region.Id,
"name": region.Name,
})
}
this.Data["regions"] = regionMaps
this.Show()
}
func (this *UpdateNodeRegionPopupAction) RunPost(params struct {
NodeId int64
RegionId int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo("修改节点 %d 区域到 %d", params.RegionId)
_, err := this.RPC().NodeRPC().UpdateNodeRegionInfo(this.AdminContext(), &pb.UpdateNodeRegionInfoRequest{
NodeId: params.NodeId,
NodeRegionId: params.RegionId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -1,94 +0,0 @@
package regions
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdatePricePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePricePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePricePopupAction) RunGet(params struct {
RegionId int64
ItemId int64
}) {
// 区域
regionResp, err := this.RPC().NodeRegionRPC().FindEnabledNodeRegion(this.AdminContext(), &pb.FindEnabledNodeRegionRequest{NodeRegionId: params.RegionId})
if err != nil {
this.ErrorPage(err)
return
}
region := regionResp.NodeRegion
if region == nil {
this.NotFound("nodeRegion", params.RegionId)
return
}
this.Data["region"] = maps.Map{
"id": region.Id,
"isOn": region.IsOn,
"name": region.Name,
}
// 当前价格
pricesMap := map[string]float32{}
if len(region.PricesJSON) > 0 {
err = json.Unmarshal(region.PricesJSON, &pricesMap)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["price"] = pricesMap[numberutils.FormatInt64(params.ItemId)]
// 价格项
itemResp, err := this.RPC().NodePriceItemRPC().FindEnabledNodePriceItem(this.AdminContext(), &pb.FindEnabledNodePriceItemRequest{NodePriceItemId: params.ItemId})
if err != nil {
this.ErrorPage(err)
return
}
item := itemResp.NodePriceItem
if item == nil {
this.NotFound("nodePriceItem", params.ItemId)
return
}
this.Data["item"] = maps.Map{
"id": item.Id,
"name": item.Name,
"bitsFrom": item.BitsFrom,
"bitsTo": item.BitsTo,
}
this.Show()
}
func (this *UpdatePricePopupAction) RunPost(params struct {
RegionId int64
ItemId int64
Price float32
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo("修改区域 %d-价格项 %d 的价格", params.RegionId, params.ItemId)
_, err := this.RPC().NodeRegionRPC().UpdateNodeRegionPrice(this.AdminContext(), &pb.UpdateNodeRegionPriceRequest{
NodeRegionId: params.RegionId,
NodeItemId: params.ItemId,
Price: params.Price,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -3,23 +3,41 @@ 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 isStream = this.Request.ProtoMajor >= 2
this.Data["isStream"] = isStream
this.Data["isDoing"] = resp.ExistTasks
this.Data["hasError"] = resp.ExistError
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
}
// 如果没有数据变化,继续查询
if i < maxTries-1 && params.IsUpdated && resp.ExistTasks == params.IsDoing && resp.ExistError == params.HasError && isStream {
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
}
@@ -76,16 +76,14 @@ func (this *IndexAction) RunPost(params struct{}) {
this.Data["dashboard"] = maps.Map{
"defaultClusterId": resp.DefaultNodeClusterId,
"countServers": resp.CountServers,
"countNodeClusters": resp.CountNodeClusters,
"countNodes": resp.CountNodes,
"countOfflineNodes": resp.CountOfflineNodes,
"countUsers": resp.CountUsers,
"countAPINodes": resp.CountAPINodes,
"countOfflineAPINodes": resp.CountOfflineAPINodes,
"countDBNodes": resp.CountDBNodes,
"countUserNodes": resp.CountUserNodes,
"countOfflineUserNodes": resp.CountOfflineUserNodes,
"countServers": resp.CountServers,
"countNodeClusters": resp.CountNodeClusters,
"countNodes": resp.CountNodes,
"countOfflineNodes": resp.CountOfflineNodes,
"countUsers": resp.CountUsers,
"countAPINodes": resp.CountAPINodes,
"countOfflineAPINodes": resp.CountOfflineAPINodes,
"countDBNodes": resp.CountDBNodes,
"canGoServers": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeServer),
"canGoNodes": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeNode),
@@ -278,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,8 +42,8 @@ func (this *ClusterAction) RunGet(params struct {
return
}
var defaultRoute = dnsResp.DefaultRoute
domainName := ""
dnsMap := maps.Map{
var domainName = ""
var dnsMap = maps.Map{
"dnsName": dnsResp.Name,
"domainId": 0,
"domainName": "",
@@ -70,20 +70,43 @@ 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
}
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 {
// 检查是否已解析
isResolved := false
if cluster.DnsDomainId > 0 && len(cluster.DnsName) > 0 && len(node.IpAddr) > 0 {
recordType := "A"
var isResolved = false
if isInstalled && cluster.DnsDomainId > 0 && len(cluster.DnsName) > 0 && len(node.IpAddr) > 0 {
var recordType = "A"
if utils.IsIPv6(node.IpAddr) {
recordType = "AAAA"
}
@@ -102,22 +125,26 @@ func (this *ClusterAction) RunGet(params struct {
}
nodeMaps = append(nodeMaps, maps.Map{
"id": node.Id,
"name": node.Name,
"ipAddr": node.IpAddr,
"id": node.Id,
"name": node.Name,
"ipAddr": node.IpAddr,
"ipAddrId": node.NodeIPAddressId,
"route": maps.Map{
"name": route.Name,
"code": route.Code,
},
"clusterId": node.NodeClusterId,
"isResolved": isResolved,
"clusterId": node.NodeClusterId,
"isResolved": isResolved,
"isInstalled": isInstalled,
"isBackup": node.IsBackupForCluster || node.IsBackupForGroup,
"isOffline": node.IsOffline,
})
}
} else {
// 默认线路
var isResolved = false
if len(defaultRoute) > 0 {
recordType := "A"
if isInstalled && len(defaultRoute) > 0 {
var recordType = "A"
if utils.IsIPv6(node.IpAddr) {
recordType = "AAAA"
}
@@ -135,15 +162,19 @@ func (this *ClusterAction) RunGet(params struct {
isResolved = checkResp.IsOk
}
nodeMaps = append(nodeMaps, maps.Map{
"id": node.Id,
"name": node.Name,
"ipAddr": node.IpAddr,
"id": node.Id,
"name": node.Name,
"ipAddr": node.IpAddr,
"ipAddrId": node.NodeIPAddressId,
"route": maps.Map{
"name": "",
"code": "",
},
"clusterId": node.NodeClusterId,
"isResolved": isResolved,
"clusterId": node.NodeClusterId,
"isResolved": isResolved,
"isInstalled": isInstalled,
"isBackup": node.IsBackupForCluster || node.IsBackupForGroup,
"isOffline": node.IsOffline,
})
}
}
@@ -155,7 +186,7 @@ func (this *ClusterAction) RunGet(params struct {
this.ErrorPage(err)
return
}
serverMaps := []maps.Map{}
var serverMaps = []maps.Map{}
for _, server := range serversResp.Servers {
// 检查是否已解析
isResolved := false
@@ -198,7 +229,7 @@ func (this *ClusterAction) RunGet(params struct {
this.ErrorPage(err)
return
}
issueMaps := []maps.Map{}
var issueMaps = []maps.Map{}
for _, issue := range issuesResp.Issues {
issueMaps = append(issueMaps, maps.Map{
"target": issue.Target,
@@ -218,7 +249,7 @@ func (this *ClusterAction) RunGet(params struct {
this.ErrorPage(err)
return
}
taskMaps := []maps.Map{}
var taskMaps = []maps.Map{}
for _, task := range resp.DnsTasks {
var clusterMap maps.Map = nil
var nodeMap maps.Map = nil

View File

@@ -4,6 +4,7 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
"sort"
)
// DomainOptionsAction 域名列表选项
@@ -21,10 +22,18 @@ func (this *DomainOptionsAction) RunPost(params struct {
this.ErrorPage(err)
return
}
domainMaps := []maps.Map{}
// 排序
if len(domainsResp.DnsDomains) > 0 {
sort.Slice(domainsResp.DnsDomains, func(i, j int) bool {
return domainsResp.DnsDomains[i].Name < domainsResp.DnsDomains[j].Name
})
}
var domainMaps = []maps.Map{}
for _, domain := range domainsResp.DnsDomains {
// 未开启或者已删除的先跳过
if !domain.IsOn || domain.IsDeleted {
if !domain.IsOn || domain.IsDeleted || !domain.IsUp {
continue
}

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

@@ -16,6 +16,7 @@ func init() {
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeDNS)).
Helper(new(Helper)).
Prefix("/dns").
Data("teaSubMenu", "cluster").
Get("", new(IndexAction)).
GetPost("/updateClusterPopup", new(UpdateClusterPopupAction)).
Post("/providerOptions", new(ProviderOptionsAction)).

View File

@@ -22,12 +22,14 @@ func (this *UpdateNodePopupAction) Init() {
func (this *UpdateNodePopupAction) RunGet(params struct {
ClusterId int64
NodeId int64
IpAddrId int64
}) {
this.Data["nodeId"] = params.NodeId
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{
NodeId: params.NodeId,
NodeClusterId: params.ClusterId,
NodeIPAddrId: params.IpAddrId,
})
if err != nil {
this.ErrorPage(err)
@@ -39,12 +41,13 @@ func (this *UpdateNodePopupAction) RunGet(params struct {
return
}
this.Data["ipAddr"] = dnsInfo.IpAddr
this.Data["ipAddrId"] = dnsInfo.NodeIPAddressId
this.Data["routes"] = domainutils.ConvertRoutesToMaps(dnsInfo)
this.Data["domainId"] = dnsInfo.DnsDomainId
this.Data["domainName"] = dnsInfo.DnsDomainName
// 读取所有线路
allRouteMaps := []maps.Map{}
var allRouteMaps = []maps.Map{}
if dnsInfo.DnsDomainId > 0 {
routesResp, err := this.RPC().DNSDomainRPC().FindAllDNSDomainRoutes(this.AdminContext(), &pb.FindAllDNSDomainRoutesRequest{DnsDomainId: dnsInfo.DnsDomainId})
if err != nil {
@@ -75,6 +78,7 @@ func (this *UpdateNodePopupAction) RunGet(params struct {
func (this *UpdateNodePopupAction) RunPost(params struct {
NodeId int64
IpAddr string
IpAddrId int64
DomainId int64
DnsRoutesJSON []byte
@@ -84,7 +88,7 @@ func (this *UpdateNodePopupAction) RunPost(params struct {
// 操作日志
defer this.CreateLog(oplogs.LevelInfo, "修改节点 %d 的DNS设置", params.NodeId)
routes := []string{}
var routes = []string{}
if len(params.DnsRoutesJSON) > 0 {
err := json.Unmarshal(params.DnsRoutesJSON, &routes)
if err != nil {
@@ -103,10 +107,11 @@ func (this *UpdateNodePopupAction) RunPost(params struct {
// 执行修改
_, err := this.RPC().NodeRPC().UpdateNodeDNS(this.AdminContext(), &pb.UpdateNodeDNSRequest{
NodeId: params.NodeId,
IpAddr: params.IpAddr,
DnsDomainId: params.DomainId,
Routes: routes,
NodeId: params.NodeId,
IpAddr: params.IpAddr,
NodeIPAddressId: params.IpAddrId,
DnsDomainId: params.DomainId,
Routes: routes,
})
if err != nil {
this.ErrorPage(err)

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

@@ -59,7 +59,7 @@ func (this *IndexAction) RunGet(params struct {
}
var providerMaps = []maps.Map{}
for _, provider := range providersResp.DnsProviders {
dataUpdatedTime := ""
var dataUpdatedTime = ""
if provider.DataUpdatedAt > 0 {
dataUpdatedTime = timeutil.FormatTime("Y-m-d H:i:s", provider.DataUpdatedAt)
}
@@ -72,7 +72,7 @@ func (this *IndexAction) RunGet(params struct {
this.ErrorPage(err)
return
}
countDomains := countDomainsResp.Count
var countDomains = countDomainsResp.Count
providerMaps = append(providerMaps, maps.Map{
"id": provider.Id,

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).

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