Compare commits

..

1824 Commits

Author SHA1 Message Date
刘祥超
1135bfafd5 版本号变更为1.3.6 2024-04-22 10:55:55 +08:00
刘祥超
ac9716c355 升级依赖库 2024-04-21 20:56:13 +08:00
刘祥超
f501907264 修改Dockerfile中的版本号为1.3.5 2024-04-21 19:58:05 +08:00
刘祥超
39774542a5 修复otp相关编译问题 2024-04-21 14:30:13 +08:00
刘祥超
783b81bc45 版本号修改为1.3.5 2024-04-20 22:46:48 +08:00
刘祥超
af57cb0fed 优化文字提示 2024-04-20 10:20:10 +08:00
刘祥超
6470fbe9f7 提交components.js 2024-04-20 10:15:39 +08:00
刘祥超
19f3be2895 OTP认证二维码增加下载链接 2024-04-20 10:15:31 +08:00
刘祥超
8404dde243 更新依赖库/修复用户OTP中的产品名称 2024-04-20 10:00:15 +08:00
刘祥超
aa52d695e6 集群设置--网站设置中增加“自动gzip回源”选项 2024-04-18 16:15:40 +08:00
刘祥超
4c83d2cbc3 优化源站列表显示 2024-04-18 15:27:08 +08:00
刘祥超
014973a575 优化源站列表 2024-04-18 15:17:31 +08:00
刘祥超
84c8cb3450 源站地址填写为HTTP时,自动检测HTTPS是否可用 2024-04-18 11:31:47 +08:00
刘祥超
9aba52c276 防盗链功能增加“例外URL“和“限制URL”设置 2024-04-17 15:45:06 +08:00
刘祥超
bbfeb5ec16 UA名单支持批量添加关键词 2024-04-17 15:30:24 +08:00
刘祥超
25f2c68738 UA名单功能增加“例外URL“和“限制URL”设置 2024-04-17 15:10:12 +08:00
刘祥超
cbd2419f63 例外和限制URL中增加常见图片、常见音频、常见视频等规则 2024-04-17 14:46:40 +08:00
刘祥超
b61ffef9e8 内容压缩功能增加“例外URL“和“限制URL”设置 2024-04-17 13:54:55 +08:00
刘祥超
18fcc43326 CC规则默认忽略常用文件 2024-04-16 14:14:31 +08:00
刘祥超
dd63154bf7 版本号修改为1.3.4.4 2024-04-16 13:52:58 +08:00
刘祥超
fd2fce8a8d 取消内容压缩中的级别设置 2024-04-16 11:19:01 +08:00
刘祥超
48c90786e9 优化“预热超时时间”输入框长度 2024-04-15 21:15:33 +08:00
刘祥超
152c679565 版本号修改为1.3.4.3 2024-04-15 09:26:25 +08:00
刘祥超
1e428c6a71 删除一直未实现的Unix协议相关内容 2024-04-14 17:12:42 +08:00
刘祥超
2b26064c7d 限制TCP和UDP网站操作HTTP网站的功能 2024-04-14 16:45:17 +08:00
刘祥超
c25c713ac1 源站增加快速停用/启用功能 2024-04-14 16:29:57 +08:00
刘祥超
141fb7bc61 优化WAF CC规则的文字提示 2024-04-13 19:22:45 +08:00
刘祥超
436ee75709 简化IP名单中创建IP操作/支持IP以CIDR方式显示 2024-04-13 16:45:32 +08:00
刘祥超
939b925dfa 优化修改IP名单中IP信息 2024-04-13 10:07:45 +08:00
刘祥超
9fff1f4d03 版本号修改为1.3.4.2 2024-04-09 10:05:28 +08:00
刘祥超
68d2586120 增加URL通配符示例 2024-04-09 09:46:02 +08:00
刘祥超
6050df45e8 提交components.js 2024-04-08 14:57:17 +08:00
刘祥超
ad664f6d60 将版本号修改为1.3.4.1 2024-04-08 14:56:43 +08:00
刘祥超
0bc2894700 增加"edge-admin security.reset"命令来使用命令行重置安全设置 2024-04-08 11:38:04 +08:00
刘祥超
b0d63c0d5b 系统安全设置中增加“仅从自定义报头中获取IP”选项 2024-04-08 11:21:56 +08:00
刘祥超
3fa1cf5a2c 用户使用反向代理访问系统时主动引导用户设置“自定义客户端IP报头“ 2024-04-08 11:07:51 +08:00
刘祥超
33a5c86beb 使用本地SID二次校验增强管理系统安全性 2024-04-08 10:24:10 +08:00
刘祥超
37441b26f1 优化WAF策略默认设置
* 增加JSCookie动作选项
* 拦截动作增加“失败全局封禁”选项
* 人机识别动作增加“失败全局封禁”选项
* IP名单中的“服务”文字改为“网站”
2024-04-07 14:20:17 +08:00
刘祥超
9f123b9a26 优化IP名单相关文字提示 2024-04-06 19:33:53 +08:00
刘祥超
8b6b6319b5 正在执行中的缓存任务状态从“执行中”改为“部分完成” 2024-04-06 15:27:39 +08:00
刘祥超
5bce566988 IP检查结果也可以显示IPv6范围 2024-04-06 15:14:51 +08:00
刘祥超
ca0c845759 IP名单支持IPv6范围 2024-04-06 14:55:29 +08:00
刘祥超
a60a7879a7 优化批量删除IP名单文字提示 2024-04-06 10:38:21 +08:00
刘祥超
1b131b26ee 更好地支持IPv6 2024-04-06 10:07:53 +08:00
刘祥超
16f4fa6c3f 缓存策略类型从“内存缓存”切换到“文件缓存“时增加默认设置 2024-04-05 11:24:05 +08:00
刘祥超
fa94426133 优化节点CPU线程数文字提示 2024-04-04 20:31:29 +08:00
刘祥超
90484aaa24 集群设置增加自动硬盘TRIM选项 2024-04-04 17:04:28 +08:00
刘祥超
20fae556d7 “磁盘”文字改为“硬盘” 2024-04-04 16:48:49 +08:00
刘祥超
14e0610af8 更新依赖库 2024-04-03 08:14:33 +08:00
刘祥超
8ae708a682 取消MMAP相关选项修改限制 2024-03-30 08:43:38 +08:00
刘祥超
fc27d210bb 使用MMAP提升缓存读取性能 2024-03-29 19:30:12 +08:00
刘祥超
49da63c146 修复glibc版本过低无法启动的问题 2024-03-25 16:50:19 +08:00
刘祥超
e45361d9d3 修复手机下菜单太宽的问题 2024-03-25 11:47:21 +08:00
刘祥超
f7941c2251 更新docker版本 2024-03-24 20:43:02 +08:00
刘祥超
b72eeb29a9 将版本号修改为1.3.4 2024-03-24 20:08:01 +08:00
刘祥超
b4dace1253 修复非gcc下编译错误 2024-03-21 17:38:24 +08:00
刘祥超
4edec8b20b 提交components.js 2024-03-18 15:59:46 +08:00
刘祥超
797abbd4e6 编译脚本支持GCC 2024-03-18 15:59:29 +08:00
刘祥超
bce8176866 管理系统增加XSS和SQL注入攻击防御 2024-03-18 12:42:49 +08:00
刘祥超
005e25c1b8 增强安全性 2024-03-18 11:45:13 +08:00
刘祥超
711e36d0bf 节点SSH密码和私钥均以掩码方式显示 2024-03-18 10:51:14 +08:00
刘祥超
78b52e7b35 DNS服务商中的密钥数据以掩码方式显示 2024-03-18 10:21:15 +08:00
刘祥超
89638a5473 将节点IP地址中的“名称”改为“备注“ 2024-03-18 08:50:14 +08:00
刘祥超
3755a1c970 修改CookieID 2024-03-18 08:20:39 +08:00
刘祥超
21963ac738 恢复默认检查区域 2024-03-18 08:13:47 +08:00
刘祥超
b67ea18471 安全设置中自定义客户端IP报头增加True-Client-IP 2024-03-16 10:03:28 +08:00
刘祥超
6e27530dec 管理系统安全设置CheckClientRegion参数默认改为false 2024-03-16 09:57:55 +08:00
刘祥超
c9d5e38db2 集群设置--网站设置中“处理未绑定域名方式”支持跳转到网址 2024-03-16 08:59:24 +08:00
刘祥超
996adc37ff 智能DNS中国家/地区线路下支持省/州的细分 2024-03-14 20:41:36 +08:00
刘祥超
4f6e0f9c65 检测本地数据库时,主机地址增加尝试localhost、172.20.0.2 2024-03-12 17:07:03 +08:00
刘祥超
5d5d129604 将ACME申请证书功能中部分“服务商”文字改为“证书服务商”/没有DNS服务商时提示用户添加 2024-03-12 10:41:19 +08:00
刘祥超
dcf02f4509 优化安全设置中“自定义客户端IP报头”中默认报头交互 2024-03-10 12:02:41 +08:00
刘祥超
cbaa344467 管理员登录时默认不启用“检查客户端区域”选项,避免因为设置了反向代理导致登录异常 2024-03-10 11:55:59 +08:00
刘祥超
7da5feada0 在缓存任务键值中增加集群信息,以便于调试问题 2024-03-10 11:25:18 +08:00
刘祥超
d901de0063 优化滚动条下的菜单 2024-03-10 08:47:11 +08:00
刘祥超
9b730e2a71 优化CSS代码 2024-03-09 22:57:21 +08:00
刘祥超
facd386cf7 优化自动安装的MySQL服务 2024-03-08 20:02:47 +08:00
刘祥超
f72902cd8d 优化systemd服务配置 2024-03-08 19:00:14 +08:00
刘祥超
70e1ef0fdb 由于密码初始化界面文字提示 2024-01-29 19:02:09 +08:00
刘祥超
74680a83a9 部分doctype改为DOCTYPE 2024-01-29 18:57:46 +08:00
刘祥超
1d9b15ad90 单体应用初始化的时候自动进入修改用户名密码界面 2024-01-29 18:57:33 +08:00
刘祥超
9c9132ad72 版本号修改为1.3.3.1 2024-01-29 17:58:47 +08:00
刘祥超
69ec57f1bf 开源版本网站默认跳入设置页面 2024-01-29 17:55:57 +08:00
刘祥超
d11adf3fe6 删除不需要的代码 2024-01-28 20:05:21 +08:00
刘祥超
faa34bc216 修复创建网站时不启用访问日志导致Websocket、统计等选项失效的问题 2024-01-28 15:03:29 +08:00
刘祥超
76451c9d27 优化编译脚本 2024-01-22 18:48:51 +08:00
刘祥超
7e5802dcd1 优化没有网站时的提示 2024-01-21 21:17:42 +08:00
刘祥超
2b6d1c70a8 集群节点列表页增加停用/启用操作 2024-01-21 17:42:56 +08:00
刘祥超
faaef3b353 Dockerfile版本号修改为1.3.3 2024-01-21 16:58:01 +08:00
刘祥超
6ffa1d4a1d 版本号修改为1.3.3 2024-01-21 16:55:55 +08:00
刘祥超
10f0e0dcca 提交components.js 2024-01-21 16:55:31 +08:00
刘祥超
0afad2e192 增加创建本地节点命令 2024-01-21 16:55:22 +08:00
刘祥超
6a1e7266b9 优化缓存文件句柄缓存的文字提示 2024-01-21 14:31:05 +08:00
刘祥超
6ffc552bd8 WAF允许ALLOW动作增加有效范围 2024-01-20 21:23:59 +08:00
刘祥超
8d8245971a WAF策略增加显示页面动作默认设置 2024-01-20 16:17:28 +08:00
刘祥超
a2f730d57e 修复部分内置页面没有<head>标签的问题 2024-01-20 10:15:05 +08:00
刘祥超
bf282c41fd 优化创建策略规则集提示 2024-01-19 11:24:02 +08:00
刘祥超
3e3f7b2cc6 WAF动作默认设置为“显示网页(page)”,减少因错误规则导致的IP封禁 2024-01-19 11:13:01 +08:00
刘祥超
0580e063be 优化网站列表批量操作按钮,防止误操作 2024-01-19 10:56:31 +08:00
刘祥超
2924aad25e 修改版本号为1.3.2.2 2024-01-16 20:59:07 +08:00
刘祥超
39907299cd 提交components.js 2024-01-16 20:58:58 +08:00
刘祥超
77705895d5 优化添加规则界面 2024-01-16 20:42:06 +08:00
刘祥超
3fb85edf0b 提交components.js 2024-01-15 08:51:21 +08:00
刘祥超
0e7a968cac 版本号修改为1.3.2.1 2024-01-15 08:40:14 +08:00
刘祥超
195a9dc771 优化网站设置和路由设置中的左侧菜单 2024-01-14 20:33:07 +08:00
刘祥超
ca227a846a 优化代码 2024-01-13 19:31:52 +08:00
刘祥超
e1057fc76e 字符编码设置增加“强制替换”选项 2024-01-13 16:28:37 +08:00
刘祥超
b5168c3174 优化文字 2024-01-13 15:46:26 +08:00
刘祥超
4ced00de50 修改API节点时提示用户需要重启edge-api进程 2024-01-12 16:44:02 +08:00
刘祥超
750c929506 实现批量删除网站功能 2024-01-11 18:40:29 +08:00
刘祥超
8b46a5a5af 自定义页面跳转支持使用变量 2024-01-11 15:37:31 +08:00
刘祥超
10d606a101 增加带宽相关组件 2024-01-11 15:17:52 +08:00
刘祥超
45d36dadcb 修复“迁移”功能中无法远程修改API节点访问地址的问题 2024-01-09 10:39:09 +08:00
刘祥超
61484d5cb8 增加用户系统文章相关管理 2024-01-09 10:21:05 +08:00
刘祥超
b7775a99ef 优化添加缓存条件和不缓存条件交互 2023-12-28 15:50:19 +08:00
刘祥超
c45ba38ffa 修复参数匹配不区分大小写选项无法保存的问题 2023-12-28 15:31:40 +08:00
刘祥超
e97d9fb8a0 优化可执行文件程序 2023-12-26 09:09:21 +08:00
刘祥超
bb56ec9ec5 修改Dockerfile版本 2023-12-24 17:35:30 +08:00
刘祥超
6d8f659558 版本号修改为1.3.2 2023-12-24 11:14:31 +08:00
刘祥超
7e9047c8fe 优化IP名单管理 2023-12-24 11:05:38 +08:00
刘祥超
b5af560b67 IP名单中可以批量删除最多10000个IP 2023-12-24 10:53:03 +08:00
刘祥超
5622636870 优化请求脚本相关代码 2023-12-23 20:54:37 +08:00
刘祥超
372c276afc 优化英文菜单样式 2023-12-23 09:41:33 +08:00
刘祥超
250b5fdd10 取消“通用设置”菜单 2023-12-22 16:58:16 +08:00
刘祥超
d1fb0bf8b7 优化数据库节点相关文字提示 2023-12-21 17:18:04 +08:00
刘祥超
490c273d36 改进部分文字 2023-12-20 15:06:19 +08:00
刘祥超
0773dbaa28 在网站设置中显示流量相关限制状态 2023-12-19 14:56:05 +08:00
刘祥超
4d04964740 修复缓存条件简单和复杂切换时可能导致复杂条件失效的问题 2023-12-19 09:26:18 +08:00
刘祥超
537e6c9a5e 修复一处编译问题 2023-12-18 10:12:20 +08:00
刘祥超
082a6721f3 版本号修改为1.3.1.2 2023-12-18 08:51:16 +08:00
刘祥超
d515a20129 提升Go模板安全性 2023-12-14 19:57:02 +08:00
刘祥超
a66d3ef61a 更新相关库 2023-12-14 19:53:43 +08:00
刘祥超
3b05b1a933 优化修改网站域名时套餐相关检查 2023-12-13 19:02:12 +08:00
刘祥超
105479ce4b 网站所属用户没有套餐时提示用户 2023-12-13 18:53:08 +08:00
刘祥超
1a950e3aad 非商业版隐藏网站配置复制图标 2023-12-13 18:46:16 +08:00
刘祥超
79fa76e039 缓存设置中可以设置缓存主域名,用来复用多域名下的缓存 2023-12-13 18:33:48 +08:00
刘祥超
9db9a70a68 优化文字 2023-12-13 11:08:35 +08:00
刘祥超
632862c742 优化多语言相关代码 2023-12-13 10:29:50 +08:00
刘祥超
76d0783f6f 切换语言时保存语言设置 2023-12-12 22:39:42 +08:00
刘祥超
fc6a0e9813 切换语言时保存更长时间 2023-12-12 22:28:17 +08:00
刘祥超
038e289057 更新components.js 2023-12-12 22:28:04 +08:00
刘祥超
4b2a4af1e7 优化极验相关代码 2023-12-12 15:00:38 +08:00
刘祥超
0fb2555bea Update en-us.js 2023-12-12 12:11:13 +08:00
刘祥超
bd8a809065 优化英文版样式 2023-12-12 11:54:20 +08:00
刘祥超
fc6fb7dce2 部分菜单实现中英文切换 2023-12-12 11:47:41 +08:00
刘祥超
bacb94abe6 优化WebP相关提示和代码 2023-12-11 11:07:02 +08:00
刘祥超
eb04421412 WebP转换质量转移到WebP策略配置 2023-12-11 10:00:22 +08:00
刘祥超
2fbd5a785e 改进部分表单文字提示 2023-12-10 10:46:50 +08:00
刘祥超
d8ae1525be 管理系统安全设置增加“自定义客户端IP报头” 2023-12-10 10:46:35 +08:00
刘祥超
daaa165d25 优化集群设置--WAF策略选择窗口尺寸 2023-12-10 08:55:12 +08:00
刘祥超
27ea02be5e 优化WAF动作“显示网页”显示 2023-12-09 15:55:17 +08:00
刘祥超
8eac1ef307 优化文字提示 2023-12-08 16:00:48 +08:00
刘祥超
1181974ea5 WAF规则集列表中参数和操作符增加悬停提示 2023-12-08 12:01:36 +08:00
刘祥超
5f57fa0470 WAF规则集列表中参数显示中文 2023-12-08 11:20:32 +08:00
刘祥超
c0ad74ab7b WAF操作符对应的对比值可以不填写 2023-12-07 20:24:26 +08:00
刘祥超
c27905e1cc 优化WAF多行对比值的文字提示 2023-12-07 11:44:37 +08:00
刘祥超
cadb65a85f 业务域名CNAME检查DNS主机地址增加Google公共DNS 2023-12-06 09:42:15 +08:00
刘祥超
b3c152685a 优化检查域名解析文字提示 2023-12-05 11:33:32 +08:00
刘祥超
16ce0726f8 将部分MB、GB...改成MiB、GiB... 2023-12-03 11:41:04 +08:00
刘祥超
5be306e087 将部分MB、GB...改成MiB、GiB... 2023-12-03 11:30:02 +08:00
刘祥超
0d21fc27ab 增加“极验-行为验”验证码集成支持 2023-11-29 16:57:58 +08:00
刘祥超
7deaa678bc 创建集群时默认生成子域名 2023-11-27 11:28:01 +08:00
刘祥超
0d230a9237 <domain-box>组件自动分割单个域名 2023-11-27 10:31:41 +08:00
刘祥超
8453d754f1 添加域名时自动分割用逗号、顿号连接的域名列表 2023-11-27 10:20:54 +08:00
刘祥超
05f77f7a0d 修复go.mod 2023-11-24 10:21:07 +08:00
刘祥超
4334b6f148 修复若干测试用例 2023-11-24 09:13:18 +08:00
刘祥超
72955759d7 更新go.sum 2023-11-24 08:53:40 +08:00
刘祥超
18dc52996d 更新Dockerfile中版本 2023-11-23 17:40:28 +08:00
刘祥超
0b16d09330 版本号修改为1.3.1 2023-11-23 17:26:24 +08:00
刘祥超
027d81a8cd 优化SSH认证sudo设置 2023-11-23 16:12:41 +08:00
刘祥超
d833158784 DNSPod改名为腾讯云DNSPod/DNSPod 支持腾讯云API密钥 2023-11-23 15:14:34 +08:00
刘祥超
75024c9364 优化X-Cache报头设置文字提示 2023-11-22 19:02:18 +08:00
刘祥超
1dd5db3c42 优化第三方域名解析azure dns提示文字 2023-11-22 10:30:29 +08:00
刘祥超
faba80e315 GRPC增加Keepalive参数 2023-11-20 09:56:35 +08:00
刘祥超
1b7dc8eb96 优化下载升级新版本文字提示 2023-11-20 08:25:34 +08:00
刘祥超
7c0af45dd0 更新Dockerfile中版本号 2023-11-19 17:34:15 +08:00
刘祥超
daea270132 更新components.js 2023-11-19 17:33:56 +08:00
刘祥超
a42e3ac4a6 优化CAPTCHA最大失败次数文字提示 2023-11-19 15:43:37 +08:00
刘祥超
c94c526b43 提交components.js 2023-11-19 09:09:50 +08:00
刘祥超
e531787773 集群WAF策略处于非防御模式时在地区封禁页面提示用户 2023-11-18 15:25:14 +08:00
刘祥超
0eda84a99a 修复修改单个WAF动作时名称变为阻止的问题 2023-11-18 08:51:50 +08:00
刘祥超
89b791448a 优化DNS.LA域名解析提示文字 2023-11-17 18:13:54 +08:00
刘祥超
2239328b2e 优化GoDaddy域名解析提示文字 2023-11-17 18:01:50 +08:00
刘祥超
e440a46080 源站支持404内容自动重试其他源站 2023-11-15 19:05:04 +08:00
刘祥超
4a0fec6e35 优化WAF设置中人机识别验证方式 2023-11-15 17:19:29 +08:00
刘祥超
a9b46987bc 更新components.js 2023-11-15 15:13:09 +08:00
刘祥超
6771afead5 WAF人机识别实现点击验证和滑动解锁验证/单个网站可以设置默认的人机识别方式 2023-11-15 15:12:58 +08:00
刘祥超
553fca2981 版本号修改为1.3.0 2023-11-14 14:47:52 +08:00
刘祥超
8ef1e36b8b 优化界面 2023-11-13 17:47:24 +08:00
刘祥超
9e6c49b67c 调教components.js 2023-11-13 17:00:22 +08:00
刘祥超
170632ddbc 增加用户列表菜单 2023-11-13 11:53:30 +08:00
刘祥超
4a74488cac 自定义页面增加例外URL和限制URL设置 2023-11-13 10:45:13 +08:00
刘祥超
5fd6a08988 第三方域名解析支持微软Azure DNS(商业版) 2023-11-12 20:31:16 +08:00
刘祥超
c86b91ead6 自定义页面增加“跳转URL”功能 2023-11-10 16:36:03 +08:00
刘祥超
aeebb763c1 优化自定义页面组件界面 2023-11-10 14:59:53 +08:00
刘祥超
b05630e3e0 优化集群设置 -- 网站设置界面 2023-11-10 11:46:02 +08:00
刘祥超
51905a0ee3 URL跳转中增加例外域名和仅限域名 2023-11-10 11:05:05 +08:00
刘祥超
869ebd59d1 优化Ln节点域名解析选项提示 2023-11-09 17:02:15 +08:00
刘祥超
8a6aedca16 集群设置--DNS设置中增加”解析记录“页面 2023-11-09 16:24:52 +08:00
刘祥超
cf98c3453a 域名解析支持Amazon Route 53(商业版) 2023-11-08 21:17:32 +08:00
刘祥超
1b6f234f39 第三方域名解析增加火山引擎TrafficRoute 2023-11-06 16:21:19 +08:00
刘祥超
26c7f0f566 优化多行消息显示 2023-11-03 16:39:20 +08:00
刘祥超
617ad9b16e 申请ACME证书时防止重复提交 2023-11-03 16:26:20 +08:00
刘祥超
04a6792dbf 删除不需要的文件 2023-11-03 11:00:26 +08:00
刘祥超
f2bf8f2dbf 删除不需要的文件 2023-11-03 10:48:34 +08:00
刘祥超
3aba0abd24 优化硬盘不足提示算法 2023-11-03 09:06:33 +08:00
刘祥超
b56ccc7cef 优化数据看板 2023-10-30 16:44:01 +08:00
刘祥超
aeebf55e60 优化数据看板 2023-10-30 11:33:00 +08:00
刘祥超
9a9e0d25f3 优化数据看板图表排列 2023-10-30 11:30:51 +08:00
刘祥超
6b3033e240 删除不需要的文件 2023-10-27 09:12:19 +08:00
刘祥超
2859a59340 优化界面 2023-10-26 15:13:35 +08:00
刘祥超
45367dd579 将看板中的“服务”文字改为“网站” 2023-10-26 11:35:31 +08:00
刘祥超
fee3fd743f MySQL安装程序支持8.2 2023-10-26 08:33:35 +08:00
刘祥超
ba4cfea47c 优化文字提示 2023-10-25 15:22:13 +08:00
刘祥超
5c11f07f8e 优化域名解析文字提示 2023-10-17 15:53:58 +08:00
刘祥超
ffac080611 将版本号修改为1.2.11 2023-10-17 13:50:03 +08:00
刘祥超
f7a1e60735 更新架构图 2023-10-15 20:32:57 +08:00
刘祥超
f22651821c 更新架构图 2023-10-15 20:19:59 +08:00
刘祥超
b2a5a9ac9b 更新架构图片 2023-10-15 20:13:52 +08:00
刘祥超
245af2bd77 修改Dockfile中的版本号为1.2.10 2023-10-15 14:42:04 +08:00
刘祥超
9dd2b1d35c 删除不需要的代码 2023-10-15 14:34:37 +08:00
刘祥超
bdbfd3e055 提交components.js 2023-10-15 14:30:27 +08:00
刘祥超
1518c03d2b 将版本号修改为1.2.10 2023-10-15 13:34:44 +08:00
刘祥超
45920d4df2 WAF记录IP动作中IP名单允许留空 2023-10-15 09:34:07 +08:00
刘祥超
1c106ac6eb 优化消息通知相关代码 2023-10-14 17:15:45 +08:00
刘祥超
9f4a32b4ac macOS上检测磁盘是否不足时忽略/Developer/相关分区 2023-10-13 09:22:45 +08:00
刘祥超
2565b10491 优化集群设置DNS TTL设置文字提示 2023-10-12 10:29:48 +08:00
刘祥超
34d632a401 删除看板中不需要的版本判断 2023-10-11 17:52:02 +08:00
刘祥超
a9df27c0e1 删除不需要的文件 2023-10-11 17:49:59 +08:00
刘祥超
313011a168 支持批量复制WAF设置 2023-10-09 19:52:39 +08:00
刘祥超
5c595aad83 提交components.js 2023-10-09 17:30:23 +08:00
刘祥超
2c67eac63c 升级时如果unzip命令不存在,则使用自定义解压程序 2023-10-09 17:30:10 +08:00
刘祥超
d20c20cb33 申请证书任务列表区分管理员和用户 2023-10-09 16:18:27 +08:00
刘祥超
fa76f032be 证书列表区分管理员和用户证书 2023-10-09 15:53:02 +08:00
刘祥超
3f74910640 访问日志列表搜索增加请求来源查询语法:referer:example.com 2023-10-08 17:52:48 +08:00
刘祥超
22bd2a57e9 WAF规则对比值长度限制为4096个字符 2023-10-08 16:14:50 +08:00
刘祥超
e45a1d0367 集群设置中增加“自动调节系统参数”选项 2023-10-08 15:08:11 +08:00
刘祥超
ee7acdc6a0 优化<url-pattern-box>组件对输入问号的提示 2023-09-27 15:24:05 +08:00
刘祥超
32264f2700 优化重写规则中目标URL选项说明 2023-09-25 18:02:35 +08:00
刘祥超
249ea32973 网站访问日志未开启时,在访问日志列表显示提醒文字 2023-09-22 16:26:15 +08:00
刘祥超
9c31b69a58 优化“集群设置--网站设置”页面 2023-09-18 17:58:16 +08:00
刘祥超
6e985cf6cc 将全局设置的TCP相关设置移到“集群设置--网站设置”中 2023-09-18 16:55:40 +08:00
刘祥超
0f35b45704 将全局的通用设置--域名审核设置移到“集群设置--网站设置”中 2023-09-18 16:07:57 +08:00
刘祥超
ba9245e724 优化集群设置--网站设置界面 2023-09-18 10:43:56 +08:00
刘祥超
6e806f2822 优化缓存磁盘用量选项提示文字 2023-09-18 10:35:22 +08:00
刘祥超
7c6842a859 修改Dockfile的版本号 2023-09-18 07:40:17 +08:00
刘祥超
39f9fa0e6b 访客IP设置中支持多个请求报头 2023-09-17 19:14:34 +08:00
刘祥超
0c8ae8960d 优化节点升级提示文字 2023-09-17 09:54:14 +08:00
刘祥超
ffd6a20829 优化界面文字和细节 2023-09-14 09:18:36 +08:00
刘祥超
2ee3e2782c 版本号修改为1.2.9 2023-09-14 09:01:40 +08:00
刘祥超
61324c28c6 在WAF规则集动作中优化已删除IP名单提示 2023-09-13 17:25:03 +08:00
刘祥超
e9f70ecb90 增加删除IP名单任务 2023-09-13 17:14:33 +08:00
刘祥超
cc3b802575 优化文字提示 2023-09-13 11:52:49 +08:00
刘祥超
9b780ff4a3 提交components.js 2023-09-12 15:02:00 +08:00
刘祥超
deb1a33e78 WebP增加图片像素限制提示 2023-09-11 15:48:02 +08:00
刘祥超
5021350aa0 WAF策略中验证码动作页面模板中使用<form></form>包裹${body}时提示警告 2023-09-10 18:05:38 +08:00
刘祥超
41b3dab135 删除不需要的文件 2023-09-08 19:36:48 +08:00
刘祥超
ecf94170c8 优化访客IP地址设置 2023-09-07 18:01:52 +08:00
刘祥超
56acdf3ce3 集群设置 -- 网站设置-- 增加“处理未绑定域名方式”选项 2023-09-07 15:15:35 +08:00
刘祥超
5d6d970d49 优化<digit-input>组件 2023-09-07 11:46:37 +08:00
刘祥超
b501cf3c88 重新实现套餐相关功能 2023-09-06 16:31:05 +08:00
刘祥超
deac2baa18 优化源站选择证书提示 2023-09-02 17:07:01 +08:00
刘祥超
b1e3f54055 优化mysql安装程序 2023-09-01 11:57:28 +08:00
刘祥超
8ebf79ffbe 优化文字提示 2023-08-27 21:37:49 +08:00
刘祥超
8d7307cddf WebP支持的默认格式中去除image/gif 2023-08-25 18:04:38 +08:00
刘祥超
c547837ae3 优化节点DNS线路选择组件 2023-08-25 17:30:21 +08:00
刘祥超
b408996e72 修复单个节点同属多个集群时DNS线路设置自动复制的问题 2023-08-25 17:28:07 +08:00
刘祥超
6b5866524d 有消息提示时页面标题增加点符号提示 2023-08-25 16:08:55 +08:00
刘祥超
4994dfb488 网站设置增加是否支持${serverAddr}选项 2023-08-25 15:31:08 +08:00
刘祥超
5d5040651e 优化几个内置的页面版本,增加连接信息,方便诊断 2023-08-25 15:03:31 +08:00
刘祥超
5252188a68 优化IP列表中区域显示 2023-08-24 12:33:06 +08:00
刘祥超
3f664882d5 使用查询本地IP库代替API查询IP信息 2023-08-24 12:22:16 +08:00
刘祥超
15f2a2d517 修复 安全设置 -- 允许访问的国家和地区 中不能使用中国特殊区域的问题 2023-08-24 11:50:21 +08:00
刘祥超
6637b0fb8f 删除不需要的文件 2023-08-24 11:01:30 +08:00
刘祥超
a06eeb9129 优化文字提示 2023-08-24 11:01:06 +08:00
刘祥超
a24fce2c22 反向代理增加是否重试50X选项,默认为启用 2023-08-20 15:49:09 +08:00
刘祥超
ddbdb64fc4 优化缓存条件界面文字 2023-08-20 11:16:58 +08:00
刘祥超
38c6d545ec 添加域名时移除多余的端口号 2023-08-20 10:27:55 +08:00
刘祥超
fe880abbb0 优化添加域名表单提示 2023-08-20 10:22:35 +08:00
刘祥超
081ed76c39 优化api_admin.yaml生成 2023-08-15 10:34:20 +08:00
刘祥超
613a00686f 修复Dockfile一处注释错误 2023-08-14 14:29:52 +08:00
刘祥超
11739ee671 修改Dockfile中的版本号为1.2.8 2023-08-14 12:21:53 +08:00
刘祥超
d06951e3b5 提交components.js 2023-08-14 12:21:42 +08:00
刘祥超
63bbacd4d8 版本号修改为1.2.8 2023-08-14 12:21:34 +08:00
刘祥超
6ba130b62c 自动生成新的配置文件(api_admin.yaml) 2023-08-14 12:21:00 +08:00
刘祥超
5529d644ac 优化缓存条件;默认缓存有效期从2个小时改为1天 2023-08-14 10:42:36 +08:00
刘祥超
7e5e6fb5b4 请求条件中的“URL前缀”改为“URL目录前缀” 2023-08-14 10:17:06 +08:00
刘祥超
f5fb1911f7 缓存条件中的“URL前缀”改为“URL目录前缀” 2023-08-14 10:02:01 +08:00
刘祥超
e26e129a63 缓存条件中的“条件类型”改为“缓存对象” 2023-08-14 09:59:55 +08:00
刘祥超
280cedad8d Dockerfile指定平台为amd64 2023-08-14 09:25:03 +08:00
刘祥超
7c3d5aa69c 修改docker版本号为1.2.7 2023-08-14 09:09:02 +08:00
刘祥超
7224ba1123 提交components.js 2023-08-13 20:04:51 +08:00
刘祥超
0f7d5057fd 优化缓存策略--“清理“功能 2023-08-13 19:22:48 +08:00
刘祥超
43f2804ced WAF增加通配符匹配/不匹配操作符 2023-08-13 10:38:14 +08:00
刘祥超
5bb83555a4 优化WAF添加规则表单 2023-08-13 10:00:41 +08:00
刘祥超
af4d14364c 修改区域监控的api.yaml到api_reporter.yaml 2023-08-12 19:12:46 +08:00
刘祥超
105e717eb2 将cluster.yaml修改为api_cluster.yaml 2023-08-12 18:52:32 +08:00
刘祥超
697ec6f6e3 优化API配置格式化 2023-08-12 18:16:17 +08:00
刘祥超
e07c99f34d 修复安装界面中安装按钮被部分遮挡的问题 2023-08-12 18:09:25 +08:00
刘祥超
c047bd5896 安装界面中增加MySQL版本号需求说明 2023-08-12 17:59:32 +08:00
刘祥超
d97ac01d79 提交components.js 2023-08-12 17:58:09 +08:00
刘祥超
df19a04ad1 将api.yaml修改为api_admin.yaml 2023-08-12 17:58:00 +08:00
刘祥超
cbce8a9934 优化多个节点安装界面 2023-08-12 15:43:03 +08:00
刘祥超
7739a84828 将节点的api.yaml改为api_node.yaml,并简化配置内容 2023-08-12 15:29:28 +08:00
刘祥超
96f711b43f 优化错误处理相关代码 2023-08-11 16:41:43 +08:00
刘祥超
96b3ae5e7d 安装时API节点端口输入框增加新手提示 2023-08-11 15:36:40 +08:00
刘祥超
d0508e257a 提交components.js 2023-08-11 08:20:10 +08:00
刘祥超
ec8548f133 临时关闭页面内容默认类型从url改为html 2023-08-10 14:07:46 +08:00
刘祥超
f217b4190b 静态分发增加例外URL、限制URL、排除隐藏文件等选项 2023-08-10 11:26:53 +08:00
刘祥超
9a3b9ee8f8 访问日志中的“[服务]”改为“[网站]” 2023-08-10 10:34:48 +08:00
刘祥超
1c299c767b WAF策略可以自定义默认的区域/省份封禁提示 2023-08-10 10:30:38 +08:00
刘祥超
03b46f4c24 WAF策略中的地区/省份封禁也支持自定义提示HTML 2023-08-10 09:52:01 +08:00
刘祥超
0f40b6438a 讲默认的最多检查内容尺寸从1MB改为512K 2023-08-10 09:21:08 +08:00
刘祥超
4549d93e0d 改进文字 2023-08-10 09:12:25 +08:00
刘祥超
844cec1011 节点安装界面显示SSH地址,方便用户校对 2023-08-09 15:41:07 +08:00
刘祥超
0464c6ce43 将版本号修改为1.2.7 2023-08-09 14:24:09 +08:00
刘祥超
0cdaa9bb70 提交components.js 2023-08-09 14:23:57 +08:00
刘祥超
dade2eb4ff Update .golangci.yaml 2023-08-09 08:11:42 +08:00
刘祥超
42e1cd560e 添加golangci-lint配置 2023-08-08 18:32:58 +08:00
刘祥超
5db14ec84a 优化代码 2023-08-08 14:17:16 +08:00
刘祥超
22aca2e80c 集群设置 -- 缓存策略 可以直接点击修改 2023-08-07 17:39:10 +08:00
刘祥超
4f105f9327 优化缓存条件输入框 2023-08-07 17:16:55 +08:00
刘祥超
e2a218dd63 可以在缓存设置中搜索缓存条件 2023-08-07 17:08:38 +08:00
刘祥超
f9c0226fa0 缓存条件默认最大值设置从32MB改为128MB 2023-08-07 11:32:59 +08:00
刘祥超
fcccd69cee 如果用户设置的可缓存最大尺寸超出缓存策略设置,则提示用户 2023-08-07 11:32:36 +08:00
刘祥超
cc60c827da 缓存策略增加“缓存磁盘最小空余空间”选项 2023-08-06 18:09:01 +08:00
刘祥超
3c44e14386 缓存策略增加预热超时时间设置(默认20分钟) 2023-08-06 17:06:28 +08:00
刘祥超
7f765f4267 当网站所在集群没有指定根域名时,增加提示和设置链接 2023-08-06 11:22:52 +08:00
刘祥超
2262e6f3ca 优化网站设置DNS菜单位置 2023-08-06 11:22:02 +08:00
刘祥超
4349d165ac 优化文字 2023-08-06 10:27:55 +08:00
刘祥超
9d396af447 优化文字提示 2023-08-02 19:22:07 +08:00
刘祥超
a665457014 WAF策略增加“最多检查内容尺寸“选项 2023-08-02 16:58:45 +08:00
刘祥超
92ddd04b07 节点详情中显示磁盘预估写入速度 2023-08-02 14:48:23 +08:00
刘祥超
b5f93d6fbc 修复系统服务相关代码可能不执行的问题 2023-08-01 16:18:04 +08:00
刘祥超
374b75b9f1 启动时自动创建相关软链接 2023-08-01 10:46:56 +08:00
刘祥超
47b7d283a3 缓存条件增加“强制Range回源选项” 2023-07-31 17:31:42 +08:00
刘祥超
03a3b8b38f 将“区间”改为“分片” 2023-07-31 17:11:42 +08:00
刘祥超
d2a27f16d7 将“分片”改为“分段”,将“分区”改为“分片” 2023-07-31 16:59:12 +08:00
刘祥超
b9837f526b 缓存条件增加是否允许异步读取源站选项 2023-07-31 15:59:19 +08:00
刘祥超
2393f3a701 修复自定义页面可能无法保存的问题 2023-07-31 09:38:28 +08:00
刘祥超
b910339e6e Update Dockerfile 2023-07-28 09:26:27 +08:00
刘祥超
438445dab8 版本号更改为1.2.6 2023-07-28 09:26:05 +08:00
刘祥超
dc7e62c388 修复分组设置无法打开的问题 2023-07-28 09:25:04 +08:00
刘祥超
3ed8f9ca55 可以修改访问未绑定域名时的提示页面的状态码 2023-07-27 11:23:08 +08:00
刘祥超
0282b5e75f Update Dockerfile 2023-07-27 08:36:27 +08:00
刘祥超
f5f8218940 Update Dockerfile 2023-07-26 15:52:58 +08:00
刘祥超
a634046757 版本号更改为1.2.5 2023-07-26 15:30:19 +08:00
刘祥超
6a4d86e084 将版本号改为1.2.4 2023-07-26 10:15:40 +08:00
刘祥超
9f9b41c63d 更新Dockerfile中版本号 2023-07-26 10:15:32 +08:00
刘祥超
d813f6515b Update Dockerfile 2023-07-25 13:41:24 +08:00
刘祥超
730a445ef6 版本号修改为1.2.3 2023-07-25 13:16:31 +08:00
刘祥超
938947548b 优化节点同步任务界面 2023-07-25 10:21:50 +08:00
刘祥超
2d5085e652 删除TOA功能 2023-07-24 09:51:37 +08:00
刘祥超
e665e299f2 优化缓存策略相关界面 2023-07-20 17:06:45 +08:00
刘祥超
30b9c5eda5 默认自动检查版本更新 2023-07-20 09:38:09 +08:00
刘祥超
fbf29e774a 优化文字提示 2023-07-19 19:40:11 +08:00
刘祥超
5429971553 优化文字提示 2023-07-19 15:14:33 +08:00
刘祥超
df81bde6fd 优化安装界面 2023-07-19 14:22:48 +08:00
刘祥超
cb8b56ceb8 自动安装的mysql版本从8.0改为8.1 2023-07-19 11:18:25 +08:00
刘祥超
1886c9954b Update Dockerfile 2023-07-18 14:35:46 +08:00
刘祥超
c942503351 版本号更改为1.2.2 2023-07-18 14:33:22 +08:00
刘祥超
806fc42379 优化静态分发组件 2023-07-18 14:33:09 +08:00
刘祥超
06a49f0272 优化静态分发组件 2023-07-18 14:32:32 +08:00
刘祥超
a99bcdc437 路由规则找不到的时候提示用户 2023-07-17 15:30:19 +08:00
刘祥超
2d4378423b 优化静态分发组件 2023-07-17 11:54:41 +08:00
刘祥超
6aaf620f18 Update Dockerfile 2023-07-17 09:43:46 +08:00
刘祥超
8e38cb2149 修复无法读取鉴权方法列表的问题 2023-07-17 09:33:19 +08:00
刘祥超
49ea05c890 commit components.js 2023-07-16 19:11:32 +08:00
刘祥超
54b479fb3a 访问鉴权设置帮助中的链接区分管理员和用户 2023-07-16 15:12:55 +08:00
刘祥超
182fbd3e23 WAF-区域封禁增加提示内容设置 2023-07-14 11:00:50 +08:00
刘祥超
6819e33510 修改文字 2023-07-13 19:55:04 +08:00
刘祥超
d547c657a0 集群设置--网站设置增加“服务器旗标”设置 2023-07-12 17:39:04 +08:00
刘祥超
62172db59b 缓存条件扩展名输入框允许输入多个扩展名 2023-07-12 17:13:27 +08:00
刘祥超
fc507b69ab 修改示例域名为example.com 2023-07-12 14:43:52 +08:00
刘祥超
cfde05f5af 优化代码 2023-07-12 14:32:47 +08:00
刘祥超
15618ad03b 自动跳转到HTTPS中域名输入使用新组件 2023-07-12 09:10:52 +08:00
刘祥超
d54fec069c 更新sum和菜单 2023-07-11 19:43:41 +08:00
刘祥超
f3cbb9b9d1 优化部分组件界面 2023-07-10 17:42:00 +08:00
刘祥超
01b510f7f8 提交components.js 2023-07-09 21:27:17 +08:00
刘祥超
2c1463e071 版本号改为1.2.1 2023-07-09 17:37:57 +08:00
刘祥超
9f90ab1b6b 证书列表完善提示 2023-07-09 09:49:24 +08:00
刘祥超
ca5bf930d0 优化文字提示 2023-07-08 20:00:50 +08:00
刘祥超
7eb0ab342a 缓存策略移除“容纳Key数量”选项 2023-07-08 18:50:29 +08:00
刘祥超
f400916351 优化网站列表样式 2023-07-07 18:57:14 +08:00
刘祥超
858cf49966 网站列表增加QPS和攻击QPS信息 2023-07-07 18:52:00 +08:00
刘祥超
a5e97fc425 改进WAF策略的区域封禁 2023-07-07 15:28:29 +08:00
刘祥超
981d1c626b 优化自定义页面设置,页面URL不再支持填写本地文件 2023-07-07 11:48:36 +08:00
刘祥超
ca2b1a6612 优化WAF区域/省份封禁:增加仅允许、增加中国香港澳台、内地等特殊区域 2023-07-07 09:53:00 +08:00
刘祥超
fc749b6ef8 修改某些消息代号 2023-07-06 11:12:49 +08:00
刘祥超
7c4e5b1a57 优化网站设置菜单顺序 2023-07-05 16:51:00 +08:00
刘祥超
4e0e5e955c 优化硬盘空间不足的提示 2023-07-05 16:42:32 +08:00
刘祥超
a07568f412 限制自定义页面最大长度不能超过32K 2023-07-05 16:12:01 +08:00
刘祥超
092735680e 当集群设置不允许记录访问日志时,在网站的访问日志查看页面提醒 2023-07-05 15:55:47 +08:00
刘祥超
5d43125284 “集群设置 -- 网站设置”增加“允许记录访问日志”选项 2023-07-05 15:29:17 +08:00
刘祥超
05d51a5447 优化源站地址输入框 2023-07-05 09:54:21 +08:00
刘祥超
dd6b8b7157 优化HTTP报头界面 2023-07-03 15:21:46 +08:00
刘祥超
3218760f84 将部分“Header”文字改为“报头” 2023-07-03 11:57:07 +08:00
刘祥超
3fa2cdfe44 增加清空节点同步任务、清空DNS同步任务功能 2023-07-02 17:29:00 +08:00
刘祥超
60eef31490 出站规则集中WAF跳转到下一个规则分组可以选择出站规则分组 2023-07-02 16:24:01 +08:00
刘祥超
3e83e89c2b 迁移后确认后,不再自动更新管理系统API信息 2023-07-02 09:05:59 +08:00
刘祥超
949fd9092d 增加部分数据清理周期设置 2023-07-01 17:57:49 +08:00
刘祥超
4276f3436d 优化迁移后确认表单的文字提示 2023-07-01 15:09:19 +08:00
刘祥超
4735aa12a9 优化文字提示 2023-07-01 10:51:52 +08:00
刘祥超
5e38b1fbca 部分中文转换为多语言代号 2023-06-30 18:08:30 +08:00
刘祥超
65555e1fe3 部分中文转换为多语言代号 2023-06-28 19:07:42 +08:00
刘祥超
a5e53df998 部分中文转换为多语言代号 2023-06-28 16:18:52 +08:00
刘祥超
70452428ab 添加多语言最基础代码 2023-06-28 09:14:07 +08:00
刘祥超
a431c57e25 优化界面 2023-06-25 14:58:25 +08:00
刘祥超
a575afdb05 优化集群设置菜单显示 2023-06-24 17:22:08 +08:00
刘祥超
40d6c7f87b 源站支持HTTP/2 2023-06-23 11:43:36 +08:00
刘祥超
8feae0a80f 优化文字提示 2023-06-21 14:34:27 +08:00
刘祥超
0a5b91f0cf 优化部分文字 2023-06-21 11:51:25 +08:00
刘祥超
08aae4df39 Update Dockerfile 2023-06-19 09:33:16 +08:00
刘祥超
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
刘祥超
34bf5028c3 删除不必要的代码 2022-10-01 07:16:37 +08:00
刘祥超
8b727aa939 提交components.js 2022-09-30 18:36:46 +08:00
刘祥超
9432600de6 优化网站服务列表页在海量域名时的加载速度 2022-09-30 13:48:16 +08:00
刘祥超
bb790ec687 文件下载链接找不到对应元素时不提示错误 2022-09-30 09:39:59 +08:00
刘祥超
8bad658d7c 版本调整为v0.5.5 2022-09-28 18:57:47 +08:00
刘祥超
a66cc9c08a 删除不必要的代码 2022-09-28 17:38:11 +08:00
刘祥超
0dc24fb342 systemd服务增加BEGIN INIT INFO 2022-09-28 08:17:32 +08:00
刘祥超
b965ac6232 版本修改为0.5.4 2022-09-27 08:05:50 +08:00
刘祥超
08d61013c8 删除多于的文件 2022-09-27 08:04:35 +08:00
刘祥超
7796f65814 将版本修改为0.5.4 2022-09-26 15:16:53 +08:00
刘祥超
02a3afb9ad 版本修改为0.5.3.1 2022-09-26 13:02:35 +08:00
刘祥超
15a11c384b 优化代码 2022-09-25 20:34:43 +08:00
刘祥超
57b8496d89 更新components.js 2022-09-25 10:34:12 +08:00
刘祥超
7bbe44f5d2 优化代码 2022-09-25 10:33:10 +08:00
刘祥超
ac11ab0431 缓存条件增加”忽略URI参数“选项 2022-09-24 15:13:35 +08:00
刘祥超
12e352964c 默认支持206 Partial Content 2022-09-24 14:40:34 +08:00
刘祥超
7daefc4384 优化代码 2022-09-24 14:06:41 +08:00
刘祥超
29541becd0 添加Header的时候自动去除Header后多余的冒号 2022-09-23 19:00:51 +08:00
刘祥超
ef6dc82f88 优化代码 2022-09-23 11:09:21 +08:00
刘祥超
6d66d93180 智能DNS访问日志增加只记录失败查询选项 2022-09-22 18:41:43 +08:00
刘祥超
3ded17b920 优化防盗链选项说明文字 2022-09-22 17:55:58 +08:00
刘祥超
d35651f570 增加防盗链功能 2022-09-22 16:33:35 +08:00
刘祥超
235300d034 修改反向代理等文字 2022-09-22 15:27:23 +08:00
刘祥超
2f7ebf5166 优化代码 2022-09-21 13:49:02 +08:00
刘祥超
4819cc87c6 删除不必要的文件 2022-09-20 18:22:13 +08:00
刘祥超
c5da383d1e 更新components.js 2022-09-19 09:50:03 +08:00
刘祥超
2da17329fa 创建节点时尝试自动从节点名称中读取IP 2022-09-18 14:57:37 +08:00
刘祥超
70d84acf76 在节点手动安装页显示节点安装文件下载链接 2022-09-18 12:38:32 +08:00
刘祥超
fef99a0023 优化代码/DNS域名增加分页 2022-09-18 10:22:58 +08:00
刘祥超
76d0935e8f 可以设置是否自动安装nftables 2022-09-17 21:04:45 +08:00
刘祥超
42dfe5ada9 缓存的URL类型文字从“前缀”改成“目录” 2022-09-17 19:26:27 +08:00
刘祥超
ef136c25e1 域名解析域名和记录名中支持中文 2022-09-17 16:50:25 +08:00
刘祥超
4251635a8a 集群增加是否远程启动选项 2022-09-17 15:11:40 +08:00
刘祥超
13f00104dd 健康检查设置域名时检查域名是否存在 2022-09-17 11:38:04 +08:00
刘祥超
b0b82b29c1 集群设置--DNS设置页显示DNS账号名 2022-09-17 10:59:18 +08:00
刘祥超
e0d43ad3d9 节点即使离线后仍然在运行状态中显示版本、主程序位置等信息 2022-09-17 10:25:40 +08:00
刘祥超
ae15115af7 创建集群时增加“只允许绑定的域名访问”选项 2022-09-16 19:33:57 +08:00
刘祥超
a2890c6cb0 集群设置中增加服务设置 2022-09-16 18:41:04 +08:00
刘祥超
2b00ece07f 访问日志中增加源站状态码 2022-09-16 10:07:21 +08:00
刘祥超
2e3c34571f 删除不必要的文件 2022-09-15 19:03:30 +08:00
刘祥超
b8b10b5176 集群增加自动同步时钟选项 2022-09-15 15:57:53 +08:00
刘祥超
e6e62bbd24 域名和记录名中可以使用大写 2022-09-14 19:48:23 +08:00
刘祥超
05e9bbf1d6 添加域名窗口中提示可以添加泛域名 2022-09-14 09:24:27 +08:00
刘祥超
bf94c2395f 可以隐藏CDN功能菜单 2022-09-13 19:54:31 +08:00
刘祥超
4a749ca345 优化GRPC参数 2022-09-13 10:48:54 +08:00
刘祥超
80ffe6c9a3 实现DNS域名验证 2022-09-10 16:13:31 +08:00
刘祥超
5414b5a8f9 优化提示文字 2022-09-09 10:33:16 +08:00
刘祥超
e3ba85a326 修改管理界面设置中的时区时同时也会应用到API节点 2022-09-09 10:27:10 +08:00
刘祥超
154fa69dbb 优化安装时跟端口相关的提示 2022-09-08 19:45:23 +08:00
刘祥超
76c44973e9 域名解析增加EdgeDNS API 2022-09-08 19:36:37 +08:00
刘祥超
26fdc82cc1 创建集群的时候可以设置DNS记录的默认TTL 2022-09-08 11:02:19 +08:00
刘祥超
dcc587182c 优化文字提示 2022-09-08 11:00:44 +08:00
刘祥超
7cee1ff5ff 访问日志里以标签的形式显示中文域名 2022-09-07 17:02:40 +08:00
刘祥超
78b8e3bf0b API节点在启动时提示“API节点正在启动,请耐心等待完成” 2022-09-07 15:57:06 +08:00
刘祥超
bd4c014c12 增加edge-admin upgrade命令 2022-09-06 21:19:37 +08:00
刘祥超
daa263cd68 将版本修改为0.5.3 2022-09-06 09:24:02 +08:00
刘祥超
e2965d39af 将版本修改为0.5.2.1 2022-09-05 16:04:51 +08:00
刘祥超
8279d15ebb 将版本修改为0.5.3 2022-09-05 11:02:17 +08:00
刘祥超
9731fa35d8 生成components.js 2022-09-04 09:31:50 +08:00
刘祥超
53be4db22b 缓存条件--简单条件增加参数匹配 2022-09-03 19:25:08 +08:00
刘祥超
0202fa2ca3 优化缓存条件设置交互 2022-09-03 19:15:30 +08:00
刘祥超
d5262b5474 简化缓存条件设置 2022-09-03 18:14:34 +08:00
刘祥超
cb2e1d54c2 优化文字提示 2022-09-02 16:34:37 +08:00
刘祥超
6392297a27 修复因URL中含有搜索引擎关键词而导致页面403的问题 2022-09-02 09:26:50 +08:00
刘祥超
6001d4eba3 更新components.js和一处绿色色值 2022-09-01 09:07:28 +08:00
刘祥超
79588e4bbc DDoS防护增加秒级连接速率限制 2022-08-31 10:00:55 +08:00
刘祥超
2b21c38382 访问控制修改为访问鉴权 2022-08-30 17:04:02 +08:00
刘祥超
481d25845e 修复CSRF可能导致访问控制自动保存失败的问题 2022-08-30 12:08:42 +08:00
刘祥超
e0f8bfe283 优化路由规则中访问控制功能 2022-08-30 11:43:49 +08:00
刘祥超
d2ecb01358 修复服务访问控制页面可能空白的问题 2022-08-30 11:42:40 +08:00
刘祥超
316e793b1e 优化访问控制,将“认证”两字改为“鉴权” 2022-08-30 11:22:54 +08:00
刘祥超
6df6809ab3 优化代码 2022-08-29 09:20:46 +08:00
刘祥超
c71f892601 创建用户的时候,可以设置开通默认功能还是全部功能 2022-08-28 20:21:57 +08:00
刘祥超
d0b5a16ce7 更新components.js 2022-08-28 20:01:29 +08:00
刘祥超
84638d3228 优化代码 2022-08-28 15:51:55 +08:00
刘祥超
bb21a2aa5f 优化代码 2022-08-28 11:07:21 +08:00
刘祥超
c37c948129 优化表头排序图标上文字提示 2022-08-27 18:45:07 +08:00
刘祥超
5155476dd7 对缓存策略中的句柄和sendfile增加设置警告 2022-08-27 18:39:36 +08:00
刘祥超
7ef4f60309 服务列表带宽使用新的算法 2022-08-27 18:37:44 +08:00
刘祥超
b0865cbbdc 可以修改服务的CNAME 2022-08-26 19:51:07 +08:00
刘祥超
990c1070e2 DDoS防护增加单IP TCP新连接速率黑名单 2022-08-26 11:32:00 +08:00
刘祥超
65bdd413eb Ln节点可以指定访问IP 2022-08-25 20:36:51 +08:00
刘祥超
60fa35eb73 集群DNS设置中增加”包含Ln节点“选项 2022-08-25 19:18:34 +08:00
刘祥超
32cfd5c233 节点运行日志可以按照节点ID设置为已读 2022-08-25 18:27:15 +08:00
刘祥超
7852495527 优化代码 2022-08-25 16:02:26 +08:00
刘祥超
3679a78f47 WAF增加Javascript cookie验证 2022-08-25 15:35:16 +08:00
刘祥超
6b21568408 优化文字提示 2022-08-25 12:02:48 +08:00
刘祥超
04a5aa41d7 浏览访问日志时自动用点符号标记有数据的分表 2022-08-25 11:36:18 +08:00
刘祥超
fea1a2199c WAF cc2阈值设置太低时提示用户 2022-08-25 09:26:06 +08:00
刘祥超
9c8492efb9 增加测试用例 2022-08-25 09:25:52 +08:00
刘祥超
20b89a8ddd 相关区域名称可以显示别名 2022-08-23 19:15:44 +08:00
刘祥超
5a8e281fb1 优化代码 2022-08-23 14:55:06 +08:00
刘祥超
a9bb413199 NS节点基本的DDoS防护 2022-08-22 15:12:10 +08:00
刘祥超
428d8ab1b1 折叠访问日志选项 2022-08-22 11:16:01 +08:00
刘祥超
c2675bcdb6 优化代码 2022-08-21 20:47:29 +08:00
刘祥超
1fe228e4c0 连接API时,自动将本地的API节点地址转换为回路地址 2022-08-20 20:33:28 +08:00
刘祥超
c5a6497f10 优化代码 2022-08-20 20:31:00 +08:00
刘祥超
f25a82585f 访问数据看板时自动初始化左侧菜单Badge 2022-08-18 09:33:14 +08:00
刘祥超
01209b66ac 对运行日志和IP名单进行操作时,及时更新左侧菜单Badge 2022-08-18 09:28:09 +08:00
刘祥超
4e19817d6f 版本修改为0.5.2 2022-08-17 18:59:37 +08:00
刘祥超
a48adff8ac 版本修改为0.5.1 2022-08-15 19:38:31 +08:00
刘祥超
ab3b32fda1 优化代码 2022-08-14 20:03:31 +08:00
刘祥超
cafab78ab4 新版IP库管理阶段性提交(未完成) 2022-08-13 23:55:35 +08:00
刘祥超
2d497dee7d 自动纠正API节点相关地址填写的常见错误 2022-08-11 18:33:46 +08:00
刘祥超
280ecd9aea 优化代码 2022-08-11 15:54:09 +08:00
刘祥超
3a980a3bc0 删除不必要的文件 2022-08-11 15:28:24 +08:00
刘祥超
556a5bdd4e 优化代码 2022-08-11 15:25:58 +08:00
刘祥超
eca26a345f generate components.js 2022-08-09 21:04:30 +08:00
刘祥超
eacff7232d 更新components.js 2022-08-08 16:26:12 +08:00
刘祥超
3c071db207 优化文字 2022-08-08 16:16:48 +08:00
刘祥超
6609c56063 页面支持DNS.LA(商业版本可用) 2022-08-07 21:18:21 +08:00
刘祥超
f7ae3de914 修改版本号为0.5.0 2022-08-07 19:02:28 +08:00
刘祥超
d82bc4e77e 缓存条件增加If-None-Match和If-Modified-Since是否回源选项 2022-08-07 16:36:41 +08:00
刘祥超
31e1df0afd 增加刷新管理界面配置函数 2022-08-07 14:56:20 +08:00
刘祥超
700e9236f9 优化代码 2022-08-06 20:28:51 +08:00
刘祥超
ee1e62aff0 优化代码 2022-08-05 21:05:49 +08:00
刘祥超
aa0c38d66f 优化代码 2022-08-05 14:38:56 +08:00
刘祥超
4e4d9e33f0 修复运行日志菜单未读数字可能不会消失的问题 2022-08-04 18:48:50 +08:00
刘祥超
f78daa98bc 删除证书之前检查是否正在被NS集群使用 2022-08-04 16:26:34 +08:00
刘祥超
dad8802fb0 优化代码 2022-08-04 11:51:34 +08:00
刘祥超
529b7041e7 优化代码 2022-08-03 21:15:12 +08:00
刘祥超
5de1eecf92 TCP源站也支持专属域名项 2022-08-03 15:28:55 +08:00
刘祥超
08ee301f89 优化代码 2022-08-03 12:20:24 +08:00
刘祥超
4154904b21 优化代码 2022-08-03 10:44:48 +08:00
刘祥超
79282809e3 优化分页样式 2022-07-31 19:56:32 +08:00
刘祥超
108c2533c2 添加域名时自动将域名转换为小写 2022-07-30 16:17:05 +08:00
刘祥超
fd7309cd17 优化路由规则界面 2022-07-30 10:43:53 +08:00
刘祥超
c5f871edf6 路由规则支持请求限制 2022-07-29 11:38:31 +08:00
刘祥超
3f2d2b238d EdgeDNS:访问日志增加集群和记录类型筛选 2022-07-27 20:20:04 +08:00
刘祥超
e6a30b99d3 优化代码 2022-07-27 16:55:19 +08:00
刘祥超
08ce2b7799 修改版本号为0.4.11 2022-07-26 08:57:04 +08:00
刘祥超
dbe7336f32 改进文字提示 2022-07-26 08:56:37 +08:00
刘祥超
aac953f483 改进文字 2022-07-26 08:00:42 +08:00
刘祥超
49bc469430 优化文字提示 2022-07-24 16:18:13 +08:00
刘祥超
f3ac8a5cc5 用户增加OTP认证设置 2022-07-24 16:14:38 +08:00
刘祥超
847d08a9bb 网站服务列表增加用户筛选 2022-07-24 14:26:14 +08:00
刘祥超
0563a363c2 用户列表中显示实名审核状态 2022-07-24 11:57:46 +08:00
刘祥超
f9dc0d6b54 实现基础的实名认证功能(商业版本专有,开源版本只显示认证状态) 2022-07-24 09:57:26 +08:00
刘祥超
40ef3604aa 增加本地API节点需要升级提示 2022-07-21 19:22:18 +08:00
刘祥超
970604dc73 API节点状态中增加主程序位置信息 2022-07-21 15:24:07 +08:00
刘祥超
d6617f214d 边缘节点详情中包含主程序位置 2022-07-21 15:07:59 +08:00
刘祥超
860fccbd4c API RPC配置增加disableUpdate,可以停用自动更新API节点 2022-07-21 14:13:23 +08:00
刘祥超
b3adb839e0 修改版本为v0.4.10 2022-07-20 18:14:18 +08:00
刘祥超
5e9654c3bc 改进文字提示 2022-07-18 20:40:48 +08:00
刘祥超
43c6bff964 准备发布 2022-07-17 21:19:45 +08:00
刘祥超
aef84189a4 优化代码/自动去除域名中的http://和https://等,防止误填 2022-07-17 17:12:44 +08:00
刘祥超
04f8bfc975 价格增加总价格设定 2022-07-17 10:53:03 +08:00
刘祥超
d139e93160 改进文字提示 2022-07-16 19:21:37 +08:00
刘祥超
1fcc0694ca 改进文字提示 2022-07-16 19:03:56 +08:00
刘祥超
7ba7858076 WAF策略增加记录区域封禁日志选项 2022-07-16 18:45:39 +08:00
刘祥超
b1cbc433ed WAF策略增加记录请求Body选项 2022-07-16 17:04:56 +08:00
刘祥超
8beaf97306 cc2增加忽略常见文件扩展名选项 2022-07-15 12:04:24 +08:00
刘祥超
e626364f45 日志详情中增加源站信息 2022-07-14 11:59:09 +08:00
刘祥超
ebaec51f67 修复全局封锁名单不能创建IP的Bug/创建和修改IP可以直接选择过期时间 2022-07-14 10:19:45 +08:00
刘祥超
36b90451af 集群DNS设置增加允许通过CNAME访问网站服务选项/集群DNS设置可以设置不使用主域名 2022-07-14 09:48:13 +08:00
刘祥超
e0b1c2a6a4 去除开源版本中不必要的菜单 2022-07-12 14:07:25 +08:00
刘祥超
42c6f4264e 删除不必要的文件 2022-07-12 14:05:24 +08:00
刘祥超
78641e7052 删除不必要的文件 2022-07-12 13:57:17 +08:00
刘祥超
6bf0118d20 修复开源版本无法编译的问题 2022-07-12 13:55:00 +08:00
刘祥超
0415cd3719 优化代码 2022-07-11 14:42:38 +08:00
刘祥超
a3412b2f95 域名解析支持DNS.COM(商业版) 2022-07-11 11:52:03 +08:00
刘祥超
6a4b3b026f 修复开源版本无法访问“刷新预热”菜单的Bug 2022-07-10 20:47:48 +08:00
刘祥超
f213976a5d 支持ClouDNS(商业版) 2022-07-10 19:35:13 +08:00
刘祥超
3605f71f70 安全设置中增加禁止搜索引擎、禁止爬虫、允许访问的域名等选项 2022-07-07 19:58:30 +08:00
刘祥超
dc08847a7d 在robots.txt中移除GoEdge标识 2022-07-07 14:42:47 +08:00
刘祥超
a34204e25e 可以在管理界面修改用户平台数据看板相关设置 2022-07-07 12:39:23 +08:00
刘祥超
25d73ac0a2 Go版本号改为从v1.18开始 2022-07-07 09:16:29 +08:00
刘祥超
f67c0c0e75 优化文字 2022-07-05 20:07:37 +08:00
刘祥超
08c8255d59 优化集群设置菜单 2022-07-04 10:31:25 +08:00
刘祥超
157efaa02e 增加UAM(5秒盾)集群设置 2022-07-03 22:09:27 +08:00
刘祥超
a56a29495e 反向代理设置中增加移除回源主机名端口功能 2022-06-30 12:11:17 +08:00
刘祥超
c454cd75b3 实现源站端口跟随功能 2022-06-29 21:56:44 +08:00
刘祥超
633684f576 优化编译脚本 2022-06-29 17:00:05 +08:00
刘祥超
de50b5e0a1 优化编译脚本 2022-06-29 14:50:51 +08:00
刘祥超
855f287e39 DNS自定义线路中增加CIDR、区域以及排除功能 2022-06-28 20:26:06 +08:00
刘祥超
f018dee75e 修复弹窗中没有正确设置favicon的Bug 2022-06-28 20:25:19 +08:00
刘祥超
7d3b218e24 支持ZSTD压缩 2022-06-27 22:40:12 +08:00
刘祥超
536382ce34 改进文字提示 2022-06-27 15:17:54 +08:00
刘祥超
f6f003d524 TLS源站支持填写回源主机名 2022-06-27 15:10:45 +08:00
刘祥超
8126de4048 改进文字 2022-06-25 19:31:23 +08:00
刘祥超
4c41df85a0 优化网站服务菜单 2022-06-20 15:59:55 +08:00
刘祥超
2c9f78bb9e 版本改为v0.4.9 2022-06-20 15:59:26 +08:00
刘祥超
23899e196e 完善一些文字细节 2022-06-19 20:19:58 +08:00
刘祥超
ecbc87265e 优化交互 2022-06-16 19:32:40 +08:00
刘祥超
9f8731d668 删除不必要的文件 2022-06-16 16:33:50 +08:00
刘祥超
12d545138a 删除不必要的文件 2022-06-16 16:26:44 +08:00
刘祥超
0c054581ac 删除不必要的文件 2022-06-16 16:25:37 +08:00
刘祥超
c8c2f83763 删除不必要的文件 2022-06-16 15:35:14 +08:00
刘祥超
9056d591c3 DNS线路可以批量添加IP范围 2022-06-15 20:24:52 +08:00
刘祥超
191f58074e 取消IP库上传入口,防止用户误操作 2022-06-15 19:51:54 +08:00
刘祥超
92d3af3d2b 修改源站时校验端口号 2022-06-15 19:42:03 +08:00
刘祥超
2ddc4d62a2 添加源站时校验端口号 2022-06-15 19:40:07 +08:00
刘祥超
81a2967683 删除不必要的文件 2022-06-15 16:32:11 +08:00
刘祥超
29e3c55df0 优化界面 2022-06-15 15:52:30 +08:00
刘祥超
27e3cdffb1 自动更新缓存任务执行状况/优化缓存相关文字提示 2022-06-15 15:47:43 +08:00
刘祥超
a1ad56aebb 优化缓存任务Key状态显示 2022-06-15 15:17:54 +08:00
刘祥超
2573b3b827 边缘节点时间和API节点时间相差超过3秒时才会在节点详情提示。 2022-06-15 11:51:03 +08:00
刘祥超
bcd9a8fcd2 优化界面提示 2022-06-14 20:00:00 +08:00
刘祥超
8dbc83fd27 增加管理平台所在服务器磁盘空间过小提醒 2022-06-14 19:55:38 +08:00
刘祥超
275280d24e WAF规则中国家/地区、省份、城市、ISP增加候选项检索和选择 2022-06-14 17:38:50 +08:00
刘祥超
c82fa4709d 优化代码 2022-06-12 20:59:55 +08:00
刘祥超
c8e826014b 访问日志查询增加requestPath:/hello、proto:HTTP/1.1、scheme:http等语法 2022-06-12 20:36:05 +08:00
刘祥超
75b2e93678 优化界面 2022-06-09 19:45:09 +08:00
刘祥超
b0573df9d8 优化界面 2022-06-09 12:23:02 +08:00
刘祥超
e6c14590f1 启用服务HTTP/HTTPS设置时如果没有设置端口,则自动添加80/443 2022-06-08 20:11:38 +08:00
刘祥超
248ac43f28 优化代码 2022-06-08 19:55:57 +08:00
刘祥超
9361a27dca API连接错误提示更加详细,以便于快速发现问题 2022-06-08 15:17:00 +08:00
刘祥超
700836903f 安装时如果数据库地址填写的是公网IP,则提示会影响系统运行性能 2022-06-08 11:00:03 +08:00
刘祥超
039ce26f58 在节点详情中提示边缘节点和API节点时间差 2022-06-06 20:13:04 +08:00
刘祥超
95bfb7f0b6 可以设置用户每天执行缓存任务的额度 2022-06-05 21:15:08 +08:00
刘祥超
9a181556ca 发送远程指令时包括从节点 2022-06-05 17:18:26 +08:00
刘祥超
9554b6f3ec 增加刷新、预热缓存任务管理 2022-06-05 17:12:54 +08:00
刘祥超
45089437ef 优化界面 2022-05-25 11:45:56 +08:00
刘祥超
f47a3b0586 优化界面/修改用户集群不影响套餐服务 2022-05-25 11:44:05 +08:00
刘祥超
1a19c7520a 优化界面 2022-05-23 14:54:27 +08:00
刘祥超
6266af66f7 新创建WAF时增加默认选项 2022-05-21 20:00:39 +08:00
刘祥超
d3cdc24ebf WAF策略中增加验证码相关定制设置 2022-05-20 22:07:04 +08:00
刘祥超
f37d2fc4d7 健康检查增加是否记录访问日志选项 2022-05-19 17:14:19 +08:00
刘祥超
6cd182b858 实现基础的DDoS防护 2022-05-18 21:02:47 +08:00
刘祥超
d46bf37726 调整左侧主菜单顺序 2022-05-12 11:04:37 +08:00
刘祥超
363295e0ea 增加edge-admin [dev|prod]两个命令 2022-05-11 21:39:31 +08:00
刘祥超
611d5daf27 升级部分库 2022-05-09 15:51:14 +08:00
刘祥超
0c1e42078e 集群节点列表可以使用“未分组”筛选 2022-05-08 19:32:55 +08:00
刘祥超
c9acafd3ad fix typo 2022-05-08 19:32:05 +08:00
刘祥超
75dd760148 阿里云DNS增加区域ID 2022-05-07 20:59:38 +08:00
刘祥超
28bfbbfa68 DNS服务商增加厂家筛选 2022-05-07 20:41:20 +08:00
刘祥超
36326697d7 创建网站服务时强制填写域名/优化源站未填写时交互 2022-05-07 20:26:40 +08:00
刘祥超
34b7cfec6f 域名设置增加说明文字 2022-05-07 20:01:29 +08:00
刘祥超
f65b725b27 修复缓存条件设置时界面可能不会自动刷新的问题 2022-05-05 11:25:23 +08:00
刘祥超
d7bceb8f2d 路由规则可以单独设置UAM(仅企业版可用) 2022-05-04 20:31:58 +08:00
刘祥超
549f418b69 节点增加DNS解析库类型设置 2022-05-04 16:41:28 +08:00
刘祥超
8a95f49f8f 改进文字提示 2022-04-29 08:28:07 +08:00
刘祥超
2c269a87a6 syn flood封禁时间从4位延长到8位 2022-04-27 16:09:15 +08:00
刘祥超
c17a27b7bf 版本修改为0.4.8 2022-04-25 11:11:52 +08:00
刘祥超
3eaecf2ca0 修复节点列表中百分号显示 2022-04-25 09:24:38 +08:00
刘祥超
07182639f3 优化一个界面显示 2022-04-24 20:13:21 +08:00
刘祥超
046628eda4 管理界面设置中“是否显示底部开源信息”选项变更时即使更新界面底部显示 2022-04-24 18:05:25 +08:00
刘祥超
8f0054b2b0 更新components.js 2022-04-24 15:24:15 +08:00
刘祥超
33f86a7730 健康检查默认只做基础的请求 2022-04-23 13:26:34 +08:00
刘祥超
2487c69136 多个API节点时选择一个作为主节点 2022-04-23 12:32:13 +08:00
刘祥超
492d24f28a 修改一个搜索示例(从http改为https) 2022-04-23 09:09:00 +08:00
刘祥超
9a355da75c 集群概要信息中增加系统服务状态 2022-04-22 22:04:36 +08:00
刘祥超
8bae065572 手动健康检查增加超时时间为60s 2022-04-22 20:56:49 +08:00
刘祥超
efebb5a869 删除不需要的文件 2022-04-22 16:52:17 +08:00
刘祥超
32cc745a85 删除不需要的文件 2022-04-22 16:49:34 +08:00
刘祥超
42bfe3ffac 删除不必要的文件 2022-04-22 16:05:37 +08:00
刘祥超
de3a5f87cf 删除不需要的文件 2022-04-22 15:58:15 +08:00
刘祥超
bd1add6dea 优化代码 2022-04-22 15:58:07 +08:00
刘祥超
d275cc6a8b 修改一处颜色显示 2022-04-22 15:15:24 +08:00
刘祥超
556b5bd2e9 优化代码 2022-04-22 10:38:03 +08:00
刘祥超
e26cf2a19d 优化代码 2022-04-22 09:58:25 +08:00
刘祥超
94f5f1faf6 节点列表页CPU和内容用量增加百分号% 2022-04-22 09:22:15 +08:00
刘祥超
fb3c58bd60 修复IPBox只能显示昨日日志的Bug 2022-04-22 09:15:08 +08:00
刘祥超
e50c918e4d 优化界面显示 2022-04-22 09:14:48 +08:00
刘祥超
eb3ff3369c 增加WAF日志配置/WAF策略中之日志中增加分表查询 2022-04-21 19:45:25 +08:00
刘祥超
a07a08991f 更新components.js 2022-04-21 18:38:47 +08:00
刘祥超
6fcc8f3401 访问日志列表显示WAF相关信息 2022-04-21 16:20:03 +08:00
刘祥超
36b38c6da4 IP列表增加名单类型筛选 2022-04-21 15:09:24 +08:00
刘祥超
7f2f86b7ea 通过IP看访问日志时优先查询IP被加入名单的时间 2022-04-21 09:42:25 +08:00
刘祥超
68514bc7ed 修复访问日志耗时可能显示NaN的Bug 2022-04-21 09:41:59 +08:00
刘祥超
f0dfab536c 修复一个日志提示标签错误 2022-04-19 19:55:03 +08:00
刘祥超
3b75c9999e 自动将端口加入到本地防火墙 2022-04-19 19:51:38 +08:00
刘祥超
084ddf4f5d 更新components.js 2022-04-19 19:51:29 +08:00
刘祥超
6770b1fa72 域名解析增加GoDaddy DNS(目前仅商业版可用) 2022-04-19 16:33:51 +08:00
刘祥超
118d39ed83 修复sendfile功能显示错误 2022-04-19 14:19:06 +08:00
刘祥超
c24df4f876 证书在上传时检查有效期 2022-04-19 11:14:40 +08:00
刘祥超
0ae9c25d6f IP名单增加区域和ISP显示 2022-04-19 10:57:47 +08:00
刘祥超
f99b2a9def 优化界面文字 2022-04-18 21:05:01 +08:00
刘祥超
caf9eeae2e 集群DNS子域名可以随机生成 2022-04-18 18:19:30 +08:00
刘祥超
6afecb5708 单个服务切换集群时可以选择是否保留节点上的配置/总是可以切换集群,不再受所属用户的影响 2022-04-18 17:18:39 +08:00
刘祥超
739d32e2e0 修复访问日志分表为-1时无法切换的问题 2022-04-18 14:32:20 +08:00
刘祥超
4e601298b0 优化界面 2022-04-17 20:49:40 +08:00
刘祥超
74cffced3f 优化访问日志弹窗中的发送字节 2022-04-17 20:22:34 +08:00
刘祥超
f8cc76be35 服务访问日志也支持分表查询 2022-04-17 17:08:39 +08:00
刘祥超
dced2dd418 修复编译脚本无法生成components.js的问题 2022-04-17 16:40:29 +08:00
刘祥超
418fe97c67 Update components.src.js 2022-04-17 16:26:53 +08:00
刘祥超
1440f9b721 优化IPBox访问日志查询 2022-04-17 16:23:52 +08:00
刘祥超
9504c086ea 访问日志可以使用分表查询 2022-04-17 16:18:43 +08:00
刘祥超
3a15d6475c 优化看板界面 2022-04-16 22:23:58 +08:00
刘祥超
18f3a00c0f 修复看板中统计数据可能不显示的问题 2022-04-16 22:22:18 +08:00
刘祥超
5138bbe947 服务列表增加下行带宽 2022-04-15 12:15:09 +08:00
刘祥超
a309feb516 修复服务列表页面中待修复的日志数量显示错误的Bug 2022-04-15 09:58:30 +08:00
刘祥超
e9c172c261 服务列表--选择分组--增加"[未分组]"选项 2022-04-14 16:59:45 +08:00
刘祥超
6ac6842a8d 优化域名设置交互 2022-04-14 16:58:57 +08:00
刘祥超
71dc53ac1f 优化界面/将“反向代理”菜单改为“源站”,将“Web设置”改为“静态分发” 2022-04-14 15:58:39 +08:00
刘祥超
4450fa1379 优化SSH地址自动填充 2022-04-10 16:56:31 +08:00
刘祥超
af0f9489c7 优化本地日志 2022-04-10 15:58:54 +08:00
刘祥超
75b2b2bdb2 修改SSH时自动填入SSH主机地址/节点设置--SSH设置增加连接测试 2022-04-09 21:44:34 +08:00
刘祥超
2d8224fd12 服务设置菜单和路由规则菜单支持拦截函数 2022-04-08 22:09:45 +08:00
刘祥超
c3aea3ba72 支持使用uglifyjs压缩js组件文件 2022-04-08 21:24:54 +08:00
刘祥超
ad1ff29ed2 Update components.js 2022-04-08 20:49:33 +08:00
刘祥超
9887bd99c7 缓存条件增加暂停/恢复功能;缓存条件修改后自动保存 2022-04-08 20:49:31 +08:00
刘祥超
9af9d0192d 判断修改节点级别权限 2022-04-07 21:37:13 +08:00
刘祥超
62933cf637 优化界面 2022-04-07 19:53:10 +08:00
刘祥超
ee0837571d 增加当日统计接口 2022-04-07 19:46:45 +08:00
刘祥超
c2b12419c7 优化代码 2022-04-07 18:53:56 +08:00
刘祥超
991aed7e8c Update components.js 2022-04-07 18:34:34 +08:00
刘祥超
528d5fc5a9 增加节点列表 2022-04-07 18:31:21 +08:00
刘祥超
93569227f3 服务分组开启访问日志设置时,在服务访问日志设置界面有对应提示 2022-04-07 16:19:44 +08:00
刘祥超
46812f9e42 优化界面/删除不需要的文件 2022-04-07 10:21:38 +08:00
刘祥超
61f043319d 对访问日志中的Cookie进行排序显示 2022-04-05 15:58:35 +08:00
刘祥超
77140d01a0 优化访问日志Header显示 2022-04-05 15:43:49 +08:00
刘祥超
beff326001 访问日志中的响应Header和请求Header排序后显示 2022-04-05 15:38:22 +08:00
刘祥超
f2841b7328 优化界面 2022-04-05 15:23:48 +08:00
刘祥超
2ba63b2484 优化界面 2022-04-05 11:33:29 +08:00
刘祥超
f054241a73 优化界面 2022-04-04 19:47:57 +08:00
刘祥超
b5007b9195 缓存文件实现Sendfile 2022-04-04 19:46:12 +08:00
刘祥超
9deea64097 商业版支持L2节点 2022-04-04 12:08:18 +08:00
刘祥超
105777da21 优化界面 2022-04-02 16:33:08 +08:00
刘祥超
e13743dfa8 优化界面 2022-04-02 16:28:22 +08:00
刘祥超
35bdd3a41a 优化界面 2022-04-02 11:09:04 +08:00
刘祥超
464d757800 README增加功能介绍 2022-04-02 10:56:14 +08:00
刘祥超
8076fcd148 集群可以单独设置WebP策略 2022-04-01 16:42:08 +08:00
刘祥超
53b1f07601 优化证书上传和修改界面 2022-04-01 11:12:42 +08:00
刘祥超
2626d78835 只有满足缓存条件的图片内容才会被转换 2022-03-31 16:22:14 +08:00
刘祥超
3442ddfae2 优化代码 2022-03-31 15:21:02 +08:00
刘祥超
5ed863c5ec 特殊页面改为自定义页面 2022-03-31 14:53:07 +08:00
刘祥超
00f6387182 更新README中链接 2022-03-30 22:27:05 +08:00
刘祥超
ca70e8f0c8 全站防护改为5秒盾(商业版可用) 2022-03-30 11:37:55 +08:00
刘祥超
081e95b293 可以用域名搜索DNS账号 2022-03-30 11:15:32 +08:00
刘祥超
1282e610ba DNSPod区域改为中国站和国际站 2022-03-30 10:59:42 +08:00
刘祥超
287c52cef4 IP列表可以使用级别筛选 2022-03-30 09:41:14 +08:00
刘祥超
dd421fec80 支持DNSPod国际版 2022-03-30 09:12:16 +08:00
刘祥超
9b10bdaf0d Update components.js 2022-03-29 21:25:33 +08:00
刘祥超
4b916829a4 商业版增加UAM功能 2022-03-29 21:25:30 +08:00
刘祥超
b33e9d9187 修复服务统计--流量统计--即时的tooltip错误 2022-03-29 10:29:49 +08:00
刘祥超
9d0d323b50 数据库清理界面显示行数 2022-03-28 17:31:37 +08:00
刘祥超
2872569ce0 可以自行设定指标数据保留时间 2022-03-28 09:37:48 +08:00
刘祥超
cd2212b754 优化界面 2022-03-27 17:23:02 +08:00
刘祥超
eb6525b8d9 优化看板打开速度/删除一些不必要的文件 2022-03-27 16:39:20 +08:00
刘祥超
10319a1e3d 优化界面 2022-03-26 22:10:34 +08:00
刘祥超
6bcfea9372 支持路由定义请求脚本 2022-03-26 22:04:26 +08:00
刘祥超
4699d1c2ee 管理界面设置和用户界面设置可以修改时区 2022-03-26 10:23:03 +08:00
刘祥超
68f0b2efc3 反向代理要求必须添加源站 2022-03-25 14:42:28 +08:00
刘祥超
b2af8e196b 优化界面 2022-03-25 14:10:40 +08:00
刘祥超
2e626a915f 优化文字提示 2022-03-25 09:32:46 +08:00
刘祥超
0127050d89 修复无法修复单个日志的Bug 2022-03-25 09:32:35 +08:00
刘祥超
a25938022f 可以修复单页或者全部服务日志 2022-03-23 17:31:53 +08:00
刘祥超
c47b2973b0 优化文字 2022-03-23 16:33:20 +08:00
刘祥超
efd3934470 优化界面 2022-03-23 16:29:56 +08:00
刘祥超
fb7da4e07e 版本号改为0.4.7 2022-03-23 14:45:10 +08:00
刘祥超
d7d209f694 更改版本为0.4.6 2022-03-23 10:03:25 +08:00
刘祥超
83a88299c2 Update components.js 2022-03-21 08:23:34 +08:00
刘祥超
bfae3b86cc Age改为在缓存中的已存活时间 2022-03-20 21:18:51 +08:00
刘祥超
027ee4d336 GRPC通讯支持gzip压缩 2022-03-20 11:28:38 +08:00
刘祥超
b07a65c879 更新相关库 2022-03-20 10:46:09 +08:00
刘祥超
284b2762f5 优化界面 2022-03-18 20:21:00 +08:00
刘祥超
5ba6e3b332 改进文字 2022-03-18 17:02:36 +08:00
刘祥超
8b0e3d960a Update components.js 2022-03-17 19:59:58 +08:00
刘祥超
fba2953974 优化界面 2022-03-17 19:57:41 +08:00
刘祥超
a36b843eff 优化指标统计图表数字展示 2022-03-17 16:01:47 +08:00
刘祥超
0735bc2d8a 源站支持自定义回源主机名 2022-03-17 15:48:08 +08:00
刘祥超
69d6fd645b 修复访问日志按小时搜索无法上翻页的Bug 2022-03-17 12:31:45 +08:00
刘祥超
039b11d434 增加置顶集群功能 2022-03-17 11:12:24 +08:00
刘祥超
485581f680 优化界面 2022-03-17 10:36:30 +08:00
刘祥超
a0c2006e24 Update components.js 2022-03-16 22:48:04 +08:00
刘祥超
7a288c360d IPSet支持IPv6 2022-03-16 20:48:35 +08:00
刘祥超
597d39f651 优化文字 2022-03-16 17:07:46 +08:00
刘祥超
7d27f64b8a 节点可以单独设置缓存目录 2022-03-16 15:24:11 +08:00
刘祥超
bd280bdc53 改进文字 2022-03-14 19:25:19 +08:00
刘祥超
018429866e 优化文字提示 2022-03-14 16:24:09 +08:00
刘祥超
78ad6526b8 缓存策略可以使用存储类型筛选 2022-03-14 15:42:10 +08:00
刘祥超
8c6e960db7 实现回源跟随功能 2022-03-14 15:07:49 +08:00
刘祥超
46d38ff8c0 格式化部分图表中的数字 2022-03-14 12:04:46 +08:00
刘祥超
db6fe469ad 访问日志慢的时候增加指定域名查询建议 2022-03-11 20:35:42 +08:00
刘祥超
5a7630bcd1 增加证书OCSP错误日志管理 2022-03-11 20:27:45 +08:00
刘祥超
099b57169d 上传js 2022-03-10 21:32:56 +08:00
刘祥超
e22dfd3314 优化界面 2022-03-10 15:47:25 +08:00
刘祥超
be71992930 增加OCSP Stapling功能 2022-03-10 11:55:09 +08:00
刘祥超
37433178cb 支持使用小时筛选访问日志 2022-03-09 11:00:49 +08:00
刘祥超
14499a4564 增加对访问日志自动分表配置 2022-03-09 10:01:52 +08:00
刘祥超
0355777f59 优化文字 2022-03-06 19:48:50 +08:00
刘祥超
a55834c78d 优化界面 2022-03-04 17:00:01 +08:00
刘祥超
79bbb47459 更新TeaGo 2022-03-04 15:01:26 +08:00
刘祥超
d3bcf0605b 升级相关依赖库 2022-03-04 12:35:28 +08:00
刘祥超
19a02f3a40 实现基础的区间内容缓存(206 partial content) 2022-03-03 19:32:11 +08:00
刘祥超
db20e9308a 修复选择集群弹窗页面可能只显示前6个集群的Bug 2022-02-28 14:41:55 +08:00
刘祥超
b3f9f28554 优化界面显示 2022-02-27 21:20:49 +08:00
刘祥超
ea5bd3f346 可以在管理界面设置里设置默认每页显示数 2022-02-24 20:52:47 +08:00
刘祥超
5c13797639 缓存可以设置是否使用系统默认设置 2022-02-24 20:39:09 +08:00
刘祥超
f147905532 增加是否同步写入压缩缓存设置 2022-02-24 20:12:15 +08:00
刘祥超
a73314c12a 修改版本为0.4.5 2022-02-24 19:25:12 +08:00
刘祥超
619934a275 修复访问日志XSS漏洞 2022-02-23 17:34:54 +08:00
刘祥超
5fe1384e55 修改版本为0.4.4 2022-02-23 14:48:33 +08:00
刘祥超
9af2c4a18a 修改版本为v0.4.3 2022-02-21 18:32:03 +08:00
刘祥超
8f4c56b24a 更改版本为v0.4.2 2022-02-21 17:52:36 +08:00
刘祥超
5eb0f65934 更新截图 2022-02-21 11:53:51 +08:00
刘祥超
4ed4f165ac URL跳转可以设置是否保留参数 2022-02-20 09:17:30 +08:00
刘祥超
eb8419fda0 Update components.js 2022-02-17 17:24:23 +08:00
刘祥超
b4a7ee0e89 域名支持--(连续的连字符) 2022-02-17 17:24:14 +08:00
刘祥超
83ad1bc529 优化界面 2022-02-15 14:54:45 +08:00
刘祥超
56948d3035 优化菜单中badge显示 2022-02-14 08:58:31 +08:00
刘祥超
e71e5ad57e 支持默认价格设置 2022-01-23 20:16:02 +08:00
刘祥超
e06d1fa5b0 实现带宽计费套餐 2022-01-23 19:16:22 +08:00
刘祥超
460439f6bd 修复服务访问日志不能使用集群、节点筛选的Bug 2022-01-20 16:28:43 +08:00
刘祥超
4b4072a47e 优化界面 2022-01-20 15:53:46 +08:00
刘祥超
65d19d92e9 增加API方法调用耗时统计 2022-01-19 16:53:38 +08:00
刘祥超
79e52d1c6e 优化demo模式进入命令 2022-01-19 15:58:54 +08:00
刘祥超
59963ee7b9 修复检查更新配置不起作用的Bug 2022-01-18 19:29:42 +08:00
刘祥超
88273b1c9b 修改版本为v0.4.1 2022-01-17 10:53:28 +08:00
刘祥超
bc1eb994f9 Update components.js 2022-01-16 20:04:11 +08:00
刘祥超
136f0fd4bd 源站支持客户端证书 2022-01-16 19:51:26 +08:00
刘祥超
021dc13ce9 CAPTCHA增加多个选项 2022-01-16 16:54:20 +08:00
刘祥超
925b71489d Update components.js 2022-01-14 10:46:02 +08:00
刘祥超
580ce9f8f5 可以在IP名单、访问日志中跳到对应的WAF规则集 2022-01-14 10:45:58 +08:00
刘祥超
2b3d8c062d 优化界面 2022-01-13 15:05:57 +08:00
刘祥超
41858e091a Update components.js 2022-01-13 10:03:54 +08:00
刘祥超
5b7eaf08ae 实现open file cache 2022-01-12 21:09:11 +08:00
刘祥超
7362722adb 优化代码 2022-01-11 16:02:27 +08:00
刘祥超
8198ea8819 可以使用集群搜索WAF策略、缓存策略 2022-01-11 15:46:47 +08:00
刘祥超
cb615f4cbc Update components.js 2022-01-11 15:28:50 +08:00
刘祥超
6623fd8362 优化交互 2022-01-11 15:26:43 +08:00
刘祥超
7f8be85116 运行日志可以使用集群、节点筛选 2022-01-11 14:59:19 +08:00
刘祥超
c61381441c 访问日志可以使用集群和节点搜索 2022-01-11 12:04:03 +08:00
刘祥超
b3cecdfea2 实现自动SYN Flood防护 2022-01-10 19:54:29 +08:00
刘祥超
66f582df58 WAF规则增加备注信息/其他界面优化 2022-01-10 10:28:23 +08:00
刘祥超
7f6f7e11ce 生成components.js 2022-01-09 20:13:18 +08:00
刘祥超
0bdda313da WAF策略增加是否使用本地防火墙设置 2022-01-09 17:05:51 +08:00
刘祥超
d5e851cff7 优化界面 2022-01-09 10:47:03 +08:00
刘祥超
92f1ec13f9 优化界面 2022-01-09 10:43:37 +08:00
刘祥超
5376006754 优化文字提示 2022-01-08 16:49:46 +08:00
刘祥超
361979411e IP名单增加未读数、按未读筛选等操作 2022-01-08 16:48:45 +08:00
刘祥超
3981308083 优化代码 2022-01-06 11:13:36 +08:00
刘祥超
facd5e14cc 改进文字 2022-01-05 20:13:22 +08:00
刘祥超
ecce92c528 Update components.js 2022-01-05 15:54:22 +08:00
刘祥超
7696940989 用户列表可以使用待审核、关键词搜索 2022-01-05 11:12:28 +08:00
刘祥超
941ff46c2c 实现用户注册/审核功能 2022-01-05 10:45:28 +08:00
刘祥超
94036073de 优化请求脚本配置交互 2022-01-03 21:48:48 +08:00
刘祥超
13216f481c 尝试自动在firewalld中开放端口 2022-01-03 16:28:27 +08:00
刘祥超
eb35df8720 改进服务访问日志、设置页在手机下的显示 2022-01-03 14:50:11 +08:00
刘祥超
bf500fe1a4 增加格式化数字函数 2022-01-03 12:04:09 +08:00
刘祥超
c173e86e62 创建服务时默认选中统计 2022-01-03 11:27:01 +08:00
刘祥超
d4cb148272 WAF动作中各个超时/有效秒数最大值从10位改成9位 2022-01-03 11:17:33 +08:00
刘祥超
ffc3f8544e 节点运行日志增加标签筛选 2022-01-03 11:08:59 +08:00
刘祥超
b326bfe63a 优化文字 2021-12-31 19:45:14 +08:00
刘祥超
12f3f47ef9 实现初版边缘脚本 2021-12-31 15:20:59 +08:00
刘祥超
45734747c1 修改版本为0.4.0 2021-12-31 15:19:44 +08:00
刘祥超
37933d814c 修改版本号0.3.8 2021-12-31 11:38:55 +08:00
刘祥超
5b26287264 修复路由规则中不能设置响应Header的Bug 2021-12-28 20:12:29 +08:00
刘祥超
bac20b1d1f 自动检查更新被取消时,同时重置已发现的最新版本信息 2021-12-22 10:02:06 +08:00
刘祥超
904c641992 删除不必要的文件 2021-12-22 10:01:35 +08:00
刘祥超
36004bfd94 优化代码 2021-12-21 15:41:43 +08:00
刘祥超
0fcba7e90c 增加自动检查系统更新设置 2021-12-21 15:18:11 +08:00
刘祥超
2e9182933e 修改版本号为0.4.0 2021-12-20 20:01:55 +08:00
刘祥超
e3674fa2c1 优化文字 2021-12-20 09:35:36 +08:00
刘祥超
a14cbe1319 访问日志限制字段 2021-12-20 09:35:25 +08:00
刘祥超
c03c35de88 访问日志限制字段 2021-12-19 20:39:18 +08:00
刘祥超
dc6c649af8 优化文字提示等 2021-12-19 18:56:09 +08:00
刘祥超
b0d2bdb0ba 修复HSTS无法设置有效期的Bug 2021-12-18 17:23:55 +08:00
刘祥超
9239dd9a8b 调整单次通知更新任务的数量为500(原来200) 2021-12-17 14:25:40 +08:00
刘祥超
07a368a0bd 修改相关域名、文字等 2021-12-17 14:17:48 +08:00
刘祥超
b8d6d1c249 访问日志中缓存状态增加STALE 2021-12-17 11:55:12 +08:00
刘祥超
0f1d6a1ad2 实现stale cache配置 2021-12-16 17:27:09 +08:00
刘祥超
37c6928ffc 节点IP显示原始IP(如果已经切换到备用IP的话) 2021-12-16 15:18:21 +08:00
刘祥超
7478b6dbe0 优化代码 2021-12-15 20:46:42 +08:00
刘祥超
e3cd6e1441 增加在线检查最新版本功能 2021-12-15 20:13:10 +08:00
刘祥超
8081d968b6 Update components.js 2021-12-15 09:56:18 +08:00
刘祥超
a226eee6ef HTTP Header:实现请求方法、域名、状态码等限制,实现内容替换功能 2021-12-14 21:26:32 +08:00
刘祥超
8b5a21e593 访问日志查询过慢的时候,提示建议增加新的日志节点 2021-12-14 15:50:21 +08:00
刘祥超
2c71f6c6be 实现访问日志队列 2021-12-14 12:45:09 +08:00
刘祥超
1a50b01edf 优化代码 2021-12-13 10:53:49 +08:00
刘祥超
05f08eeb4c 优化坐标轴单位 2021-12-12 21:13:55 +08:00
刘祥超
fdc3ebb5c5 WAF添加规则:调整界面/增加正则表达式测试功能 2021-12-12 20:56:25 +08:00
刘祥超
8f06bccd48 WAF策略:可以修改分组代号/导入时可以根据名称合并/导出时可以导出停用的分组 2021-12-12 20:24:15 +08:00
刘祥超
16b4eb67d4 Update components.js 2021-12-12 17:14:46 +08:00
刘祥超
ffa545cb41 服务分组可以设置请求限制 2021-12-12 17:07:16 +08:00
刘祥超
f67454d51c 路由规则增加专属域名设置 2021-12-12 16:38:52 +08:00
刘祥超
8ae54c56db 增加排除扩展名条件 2021-12-12 16:14:56 +08:00
刘祥超
9a17adcc6f 请求条件增加不区分大小写选项 2021-12-12 16:11:25 +08:00
刘祥超
4dd6903025 优化界面 2021-12-12 11:58:16 +08:00
刘祥超
085770d0ad 实现请求连接数等限制/容量组件增加EB支持 2021-12-12 11:46:09 +08:00
刘祥超
fbaba7c37d 修复公共黑名单/白名单无法搜索的Bug 2021-12-10 11:12:49 +08:00
刘祥超
d5cea208d2 支持设置单节点最大线程数、单节点TCP最大连接数 2021-12-09 18:49:35 +08:00
刘祥超
36397adca4 修复服务统计图表颜色显示问题/Expires设置增加提示文字 2021-12-08 21:40:01 +08:00
刘祥超
4971e25d44 WAF看板最新拦截记录增加区域信息 2021-12-08 20:28:26 +08:00
刘祥超
150357441d 修复域名为空时点击审核页面为空的Bug 2021-12-08 20:20:10 +08:00
刘祥超
c4ee663285 重新生成components.js 2021-12-08 19:13:54 +08:00
刘祥超
8c95b4a9b9 多处访问日志增加单页显示条数选择 2021-12-08 19:13:34 +08:00
刘祥超
32ba919851 增加部分访问日志条数10->20 2021-12-08 17:49:30 +08:00
刘祥超
eb37345e85 可以在缓存条件里设置Expires Header 2021-12-08 17:41:12 +08:00
刘祥超
681e454917 修复编译时components.js可能没有更新的Bug 2021-12-08 10:03:27 +08:00
刘祥超
7c7b82dee4 优化地图标签 2021-12-08 09:37:46 +08:00
刘祥超
2ae47af8f0 增加批量增加节点IP接口 2021-12-07 18:22:46 +08:00
刘祥超
b72d91d0d4 访问日志实现记录和显示requestBody 2021-12-07 15:03:48 +08:00
刘祥超
56010e7203 缓存默认支持所有请求方法 2021-12-07 10:47:46 +08:00
刘祥超
f658698f7b 缓存支持请求方法设置 2021-12-07 10:45:49 +08:00
刘祥超
b708b9c6df 将缓存默认key改为${scheme}://${host}${requestPath}${isArgs}${args} 2021-12-07 10:04:50 +08:00
刘祥超
c2908e17fa SSH认证支持sudo 2021-12-06 19:24:30 +08:00
刘祥超
302daab824 服务看板增加区域地图 2021-12-06 09:16:23 +08:00
刘祥超
ec49f238d6 优化界面 2021-12-06 08:55:47 +08:00
刘祥超
3e43a5d866 上传components.js/优化地图样式 2021-12-05 20:59:07 +08:00
刘祥超
a99d5e68e9 商业版WAF看板增加地图 2021-12-05 19:38:14 +08:00
刘祥超
28514276ec 商业版首页增加地图/调低各个图表的高度,以便同时可以显示更多的图表 2021-12-05 18:59:20 +08:00
刘祥超
b611427c17 创建新服务时默认开启配置可以选择统计 2021-12-03 14:55:14 +08:00
刘祥超
ce7a4ead04 首页看板显示未审核的服务数、本周流量、昨日流量 2021-12-03 14:54:15 +08:00
刘祥超
5fe15a85fd 优化IPBox交互/优化TOA文字提示 2021-12-03 11:00:20 +08:00
刘祥超
ae412909f6 优化服务设置界面顶部菜单 2021-12-02 17:41:51 +08:00
刘祥超
c6ed579797 IPBox把IP加入黑名单可以选择过期时间/可以从已经添加的名单中删除/已经添加的名单中显示过期时间 2021-12-02 17:11:44 +08:00
刘祥超
b2a525268e WAF规则集中增加是否忽略局域网IP选项 2021-12-02 16:09:15 +08:00
刘祥超
eb78b4881c 多个提示页面增加请求ID 2021-12-02 14:45:51 +08:00
刘祥超
4ce6e5a9f6 访问日志弹窗中加入请求ID/优化Header添加/修改文字提示 2021-12-02 11:49:36 +08:00
刘祥超
15d7e75555 优化URL跳转文字提示 2021-12-02 10:39:22 +08:00
刘祥超
6453cc6ccc 缓存配置增加是否支持Cache-Control: max-age=... 2021-12-02 10:18:22 +08:00
刘祥超
d882a2eb63 缓存配置增加Age Header配置 2021-12-02 09:54:31 +08:00
刘祥超
065ac4aa25 增加generate.sh脚本用来生成文件 2021-12-02 09:46:46 +08:00
刘祥超
dea54fc55e 增加是否记录499选项 2021-12-01 21:13:15 +08:00
刘祥超
8f425bd9c7 当用户提交待审核域名时,给管理员发送消息 2021-12-01 17:19:50 +08:00
刘祥超
9d909d73b8 审核中服务增加提交审核时间/已通过域名标绿 2021-12-01 17:06:05 +08:00
刘祥超
b417d50a28 优化节点日志:可以批量设置服务错误日志为已修复等 2021-11-30 16:43:44 +08:00
刘祥超
aa93a2f702 优化编译脚本 2021-11-30 11:05:58 +08:00
刘祥超
ba7125e773 优化文字 2021-11-30 11:05:24 +08:00
刘祥超
46a7eaa4bb 商业版认证增加申请页面 2021-11-30 10:02:31 +08:00
刘祥超
887439a6fe 优化代码 2021-11-29 20:35:47 +08:00
刘祥超
0e2b07d06d 优化提示文字 2021-11-29 16:41:32 +08:00
刘祥超
b7d4bde11b 修改部分菜单名 2021-11-29 12:00:23 +08:00
刘祥超
9707360948 完善套餐 2021-11-28 20:11:48 +08:00
刘祥超
103a8eb092 修改版本号为0.3.7 2021-11-28 14:28:53 +08:00
刘祥超
dcba4f9376 节点同步不在图标上提示IP名单相关更新 2021-11-27 17:06:36 +08:00
刘祥超
12aaa6fcb1 更新生成的components.js 2021-11-27 17:06:03 +08:00
刘祥超
d8393481a4 修改域名变更时是否需要审核的初始状态 2021-11-25 12:10:23 +08:00
刘祥超
70d3202a2c 服务增加是否合并URL中的多余分隔符选项 2021-11-24 14:49:42 +08:00
刘祥超
c7f1bbc03d 版本号改为0.3.6 2021-11-24 14:03:56 +08:00
刘祥超
61a55cb3f4 优化命名 2021-11-24 11:58:01 +08:00
刘祥超
6a484af775 将版本修改为0.3.5.2 2021-11-24 10:29:59 +08:00
刘祥超
3074d41cf2 改进源站专属域名的文字提示和交互 2021-11-22 18:46:08 +08:00
刘祥超
54199058e3 修复服务无法创建的Bug 2021-11-22 14:34:32 +08:00
刘祥超
87a533791b 版本改为0.3.5.1 2021-11-22 14:34:20 +08:00
刘祥超
c3109bb2c6 编译时生成components.js 2021-11-22 12:08:53 +08:00
刘祥超
031cb836d2 安装时等API节点启动完毕后才进行下一步,避免因为未启动完整而导致的错误 2021-11-21 19:26:05 +08:00
刘祥超
aa0a9134cb 迁移后确认API节点界面可以跳转到安装界面 2021-11-21 19:25:42 +08:00
刘祥超
749eac74fe 当迁移了管理平台后,自动跳转到确认API配置页 2021-11-21 15:57:13 +08:00
刘祥超
ac39908737 优化删除IP时成功消息提示时间 2021-11-21 09:45:11 +08:00
刘祥超
bb8f4bf488 增加批量删除IP名单中的IP的功能 2021-11-21 09:43:14 +08:00
刘祥超
db0d157a74 修复时间输入组件时间戳总是多一秒的Bug 2021-11-21 08:44:03 +08:00
刘祥超
bd8e1bbe71 优化RPC客户端锁 2021-11-20 19:17:52 +08:00
刘祥超
7aba898cf5 优化文字 2021-11-20 19:17:16 +08:00
刘祥超
baf039755f 实现迁移辅助功能(系统设置 -- 高级设置 -- 迁移) 2021-11-20 18:58:58 +08:00
刘祥超
70977f7d80 IP地址“健康检查失败”阈值改为“健康检查结果” 2021-11-18 14:47:48 +08:00
刘祥超
884342d6af 节点IP阈值增加节点健康检查失败 2021-11-18 14:30:46 +08:00
刘祥超
411b0fb4c2 修复看板--事件中无法单条已读操作的Bug 2021-11-18 08:52:29 +08:00
刘祥超
8053fb2399 IP名单中显示已过期标签 2021-11-17 21:18:33 +08:00
刘祥超
ed42dcab9c IP名单增加是否只显示自动拦截名单选项 2021-11-17 20:25:31 +08:00
刘祥超
866b5b0f2f IP名单中增加搜索按钮 2021-11-17 20:15:37 +08:00
刘祥超
5834a1a0fa 增加全局查看、检索IP功能 2021-11-17 19:50:52 +08:00
刘祥超
8c6d845603 将公用IP名单默认改成全局名单,自动应用于所有服务 2021-11-17 16:14:37 +08:00
刘祥超
667f363f3c WAF block动作默认封锁范围为global 2021-11-16 19:17:08 +08:00
刘祥超
e209ff38d9 IP名单中显示IP创建时相关的服务、WAF策略等信息 2021-11-16 16:11:29 +08:00
刘祥超
ea915993b6 节点运行日志中显示相关服务链接 2021-11-15 16:51:39 +08:00
刘祥超
72d0468c6a IP名单中的IP增加添加日期显示 2021-11-15 11:31:54 +08:00
刘祥超
35ae13b1c3 优化缓存配置界面 2021-11-14 16:21:04 +08:00
刘祥超
1d9460f565 优化RPC连接 2021-11-10 22:22:27 +08:00
刘祥超
e9a3ed71b4 将带宽限制改为流量限制 2021-11-09 17:36:38 +08:00
刘祥超
e344e5b7e6 支持套餐相关操作 2021-11-09 15:36:18 +08:00
刘祥超
e814064403 编译时删除.js.map文件 2021-11-09 14:19:42 +08:00
刘祥超
f5aeb5cbcd 支持购买套餐/续费套餐/用户账户操作等 2021-11-08 20:52:02 +08:00
刘祥超
f41164b892 删除不需要的文件 2021-11-07 09:11:30 +08:00
刘祥超
03073c8364 删除不需要的文件 2021-11-07 08:42:11 +08:00
刘祥超
a359bff531 安装时自动检查服务器上安装的MySQL 2021-11-06 18:35:22 +08:00
刘祥超
3789ac6433 域名解析中可以删除和恢复某个域名 2021-11-06 16:23:38 +08:00
刘祥超
9f53f59f18 SSH登录支持Passphrase 2021-11-06 15:31:07 +08:00
刘祥超
521bd746e3 当证书被API节点或者用户节点使用时不允许删除/规范命名 2021-11-05 17:56:17 +08:00
刘祥超
3d8e43a42b 修改文字 2021-11-05 17:13:43 +08:00
刘祥超
9452e1852d 规范API命名 2021-11-05 15:34:48 +08:00
刘祥超
3920d24af6 只有一个可用的API节点时不允许删除 2021-11-05 15:29:37 +08:00
刘祥超
3af11e6ba8 修改版本号为0.3.5 2021-11-05 14:59:10 +08:00
刘祥超
7bcde46d49 修复缓存条件可能无法保存的Bug 2021-11-04 11:23:29 +08:00
刘祥超
0b73041718 支持info指令查询PID、版本号等信息 2021-11-04 11:13:54 +08:00
刘祥超
dcdc0cb8c1 修复无法设置缓存条件的Bug 2021-11-04 11:12:59 +08:00
刘祥超
ad6ac1aad6 优化安装程序 2021-11-01 21:09:52 +08:00
刘祥超
249dc6accd 修改版本为0.3.4 2021-11-01 10:45:41 +08:00
刘祥超
939e5999ca 优化细节 2021-10-30 22:33:56 +08:00
刘祥超
1f91e57d56 增加套餐相关代码 2021-10-29 14:02:01 +08:00
刘祥超
81e749dc60 支持gif转webp 2021-10-29 12:21:52 +08:00
刘祥超
241b2afda8 Update .gitignore 2021-10-26 20:43:59 +08:00
刘祥超
94cc29f227 删除不必要的文件 2021-10-26 20:43:47 +08:00
刘祥超
6d6659eee1 WAF增加显示网页动作 2021-10-25 19:40:28 +08:00
刘祥超
5220be0775 优化代码 2021-10-25 19:01:56 +08:00
刘祥超
07ebbf0863 WAF模板中有新的规则时,可以在界面上收到提醒并点击加入 2021-10-25 12:02:03 +08:00
刘祥超
b60c767fc5 创建网站服务时增加缓存、WAF、从上级代理中读取IP等选项 2021-10-25 09:06:23 +08:00
刘祥超
371c3b78c3 DNS记录名支持下划线 2021-10-25 09:05:26 +08:00
刘祥超
6a3aa219d5 优化HTTP客户端IP配置交互 2021-10-22 14:40:39 +08:00
刘祥超
df586ddfdd 优化代码 2021-10-22 13:57:04 +08:00
刘祥超
f3b2bbfec0 删除不需要的文件 2021-10-22 13:20:02 +08:00
刘祥超
3ea2114798 IP名单列表可以搜索关键词 2021-10-22 12:38:52 +08:00
刘祥超
809cf70e0e 可以在IP名单中搜索IP 2021-10-22 12:19:02 +08:00
刘祥超
390619535f 实现单个服务的带宽限制(商业版) 2021-10-21 17:10:08 +08:00
刘祥超
3392ac1fa8 优化交互 2021-10-20 09:59:08 +08:00
刘祥超
b09d94abbe 网站服务--访问日志增加服务链接 2021-10-19 16:32:08 +08:00
刘祥超
03ac01d21f 健康检查支持UserAgent和是否基础请求设置 2021-10-19 16:31:45 +08:00
刘祥超
12b1d785e5 增加防盗链规则参数 2021-10-19 11:38:56 +08:00
刘祥超
71c58e9d2e WAF阻止动作增加封锁范围 2021-10-18 20:09:06 +08:00
刘祥超
13c2997a52 内容压缩支持对已压缩内容重新压缩 2021-10-18 16:49:19 +08:00
刘祥超
47b840cac9 优化文字提示 2021-10-18 16:49:04 +08:00
刘祥超
3f7f243f50 默认的内容压缩算法从gzip改为brotli 2021-10-18 11:59:50 +08:00
刘祥超
1d1e83b18d 增加PURGE某个URL缓存功能 2021-10-17 20:22:57 +08:00
刘祥超
6f3a602c76 优化分页条数修改/在弹窗下不运行某些任务 2021-10-16 12:45:56 +08:00
刘祥超
afb7a4c6a7 修复选择集群弹窗无法修改分页条数的问题 2021-10-16 12:45:25 +08:00
刘祥超
d0c950d4ca 优化界面 2021-10-16 12:06:55 +08:00
刘祥超
63ee7d5211 支持任意域名通过CNAME访问服务(开启选项后)/可以重新生成服务CNAME 2021-10-16 12:03:21 +08:00
刘祥超
6b0d875745 优化界面显示 2021-10-16 10:29:14 +08:00
刘祥超
a47a9b9c0c 删除CodeMirror中没用的代码 2021-10-16 10:26:14 +08:00
刘祥超
27040a3e5c 节点运行日志增加本页已读 2021-10-15 13:05:02 +08:00
刘祥超
c7a8a40e22 运行日志显示未读的日志数量 2021-10-15 12:54:23 +08:00
刘祥超
408de6af63 优化文字提示 2021-10-15 09:42:55 +08:00
刘祥超
8a324afaa1 数据看板增加事件列表(商业版) 2021-10-14 17:29:30 +08:00
刘祥超
54bc4cede0 修复无法编译amd64以外架构的Bug 2021-10-13 18:06:38 +08:00
刘祥超
4cfbea80b0 支持PROXY Protocol 2021-10-12 20:18:29 +08:00
刘祥超
64cb8286bd 集群非上海时区的在列表里显示时区 2021-10-12 14:39:13 +08:00
刘祥超
3e92e0afc6 优化修改时区交互 2021-10-12 11:49:26 +08:00
刘祥超
8a91308280 可以在集群中指定节点时区 2021-10-12 11:43:53 +08:00
刘祥超
3da861d71e 选择线路的时候关键词可以搜索域名 2021-10-12 08:41:02 +08:00
刘祥超
3566e18e99 WebP压缩支持ico 2021-10-11 14:51:50 +08:00
刘祥超
6e608e627a WebP默认mimeTypes从image/*改为image/png等 2021-10-11 13:56:51 +08:00
刘祥超
65f7fb979b 修改版本为0.3.3 2021-10-11 13:56:20 +08:00
刘祥超
558b5e14f1 优化细节 2021-10-10 20:17:40 +08:00
刘祥超
a5ee2dd03b 页面底部增加GoEdge官网和文档链接 2021-10-10 16:38:45 +08:00
刘祥超
9e9fe78b8d 优化升级提示文字 2021-10-10 16:33:27 +08:00
刘祥超
265e126faf TCP、TLS、UDP支持端口范围 2021-10-10 16:30:21 +08:00
刘祥超
af440e5c5b 服务分组增加特殊页面设置 2021-10-10 10:52:58 +08:00
刘祥超
32b8c91113 特殊页面可以直接使用HTML 2021-10-10 10:35:14 +08:00
刘祥超
dbc60ccca4 证书上传时可以选择输入文本内容 2021-10-09 17:30:05 +08:00
刘祥超
d5b5af5d3a 数据看板-WAF看板增加节点拦截排行和域名拦截排行 2021-10-09 16:01:17 +08:00
刘祥超
8c1bd3bc4e 增加新的界面风格theme4, theme5 2021-10-09 11:50:02 +08:00
刘祥超
8f638186a3 在服务看板中可以切换到附近的服务 2021-10-08 14:36:57 +08:00
刘祥超
0b5a27e674 支持更多的分组全局设置功能 2021-10-07 16:47:14 +08:00
刘祥超
870f1aaaec WAF模式从pass改为bypass 2021-10-07 13:55:00 +08:00
刘祥超
23cb4dcbe5 服务支持自定义访客IP地址获取方式 2021-10-06 11:40:24 +08:00
刘祥超
4f125b4244 添加源站时自动去除专属域名中的末尾斜杠 2021-10-06 09:29:44 +08:00
刘祥超
2b9de7938f 在服务设置里也显示WAF策略的模式 2021-10-06 09:08:09 +08:00
刘祥超
75bb07184f ACME使用EAB申请的账号只能绑定一个用户 2021-10-03 14:43:29 +08:00
刘祥超
1fb491d2e1 ACME证书增加ZeroSSL支持 2021-10-03 13:09:49 +08:00
刘祥超
0bc8bdd841 支持自动转换图像文件为WebP 2021-10-01 16:24:42 +08:00
刘祥超
71d4e2626e 自建DNS改成智能DNS 2021-09-30 13:20:50 +08:00
刘祥超
0b26cbdd01 WAF策略增加观察模式和通过模式 2021-09-30 11:30:36 +08:00
刘祥超
ca72b3c18b 内容压缩支持brotli和deflate 2021-09-29 20:12:27 +08:00
刘祥超
8676f2711b 在WAF规则产生错误时给予提示 2021-09-27 10:11:37 +08:00
刘祥超
788a86bdcf 看板增加离线节点数字 2021-09-27 09:23:48 +08:00
刘祥超
0df6b4b220 优化安装界面--设置管理员账号的交互 2021-09-26 15:08:53 +08:00
刘祥超
f14dcd5c28 缓存条件增加最小内容尺寸配置 2021-09-26 15:01:52 +08:00
刘祥超
f86548e046 版本改为0.3.2 2021-09-26 10:09:51 +08:00
刘祥超
9dea11ab11 节点设置中不显示阈值设置 2021-09-25 19:57:22 +08:00
刘祥超
00749f806c 优化编译脚本 2021-09-25 19:57:10 +08:00
刘祥超
e4e3591413 修改文字 2021-09-24 12:03:31 +08:00
刘祥超
3f847d7c7f 区域监控增加检测时间、结果页增加级别筛选 2021-09-23 10:02:32 +08:00
刘祥超
67a10994a4 提示已经有服务分组配置时加入分组链接 2021-09-22 19:46:22 +08:00
刘祥超
4016296e0b 可以在分组中设置一些全局配置选项 2021-09-22 19:39:38 +08:00
刘祥超
92777366ec 域名解析任务列表中增加集群解析页面链接/在域名解析--集群详情中显示正在执行的任务 2021-09-21 10:56:29 +08:00
刘祥超
beb34f8264 可以设置集群的DNS记录TTL 2021-09-20 20:01:15 +08:00
刘祥超
f6f8c5c858 在集群中可以设置自动加入DNS的CNAME记录 2021-09-20 16:37:41 +08:00
刘祥超
a19c4dffcd 反向代理源站实现使用域名分组 2021-09-20 11:54:28 +08:00
刘祥超
a5754971f6 开源版本也显示域名排行、缓存流量趋势、攻击流量趋势 2021-09-19 16:10:52 +08:00
刘祥超
dbe6435809 实现连通性变化发送通知功能 2021-09-18 14:22:01 +08:00
刘祥超
7e5b980600 优化消息弹窗交互 2021-09-18 11:02:23 +08:00
刘祥超
a6c3b70ee7 修改生成的YAML配置中可能含有tab的Bug 2021-09-16 15:06:37 +08:00
刘祥超
0a23e7951a 增加商业版激活功能 2021-09-16 10:32:58 +08:00
刘祥超
e0e3fc8fef 看板提醒商业版过期日期 2021-09-16 09:21:44 +08:00
刘祥超
a8b947f5ca 修复日期控件初始化格式可能错误的问题 2021-09-15 19:40:42 +08:00
刘祥超
0f28df51f1 优化界面 2021-09-15 19:22:54 +08:00
刘祥超
6e36528f35 IP地址详情中增加区域监控雷达图、数据列表 2021-09-15 17:54:03 +08:00
刘祥超
a885fdbea7 修复IP地址不能修改在线状态的Bug 2021-09-15 11:46:50 +08:00
刘祥超
713de74abb 调整部分命名 2021-09-14 19:39:41 +08:00
刘祥超
8db6a33e15 IP阈值动作增加WebHook 2021-09-14 15:27:54 +08:00
刘祥超
ed2d577be0 IP阈值增加节点分组和集群相关统计项目 2021-09-14 11:36:08 +08:00
刘祥超
084eddecbf 优化节点设置交互 2021-09-13 16:47:34 +08:00
刘祥超
5e09ba302a IP地址支持手动上线和从备用IP中恢复 2021-09-13 13:45:39 +08:00
刘祥超
fa37b40435 实现节点自动切换到备用IP 2021-09-13 10:51:16 +08:00
刘祥超
c95dde5187 实现基础的IP地址阈值 2021-09-12 20:21:32 +08:00
刘祥超
4d5a4d501a 修复当集群没有绑定DNS域名时无法修改节点DNS信息的Bug 2021-09-11 20:12:06 +08:00
刘祥超
ce97f20826 实现基本的监控终端管理 2021-09-08 19:34:51 +08:00
刘祥超
ab9cd13abc 版本修改为0.3.1 2021-09-08 19:34:47 +08:00
刘祥超
7af5e828bb 在选择线路弹窗可以从域名中获取线路 2021-09-06 09:16:19 +08:00
刘祥超
98d53c49a0 修复打开网站服务菜单时无法点击系统设置的Bug 2021-09-06 08:53:51 +08:00
刘祥超
bb32849e47 安装时延长API节点初始化等待时间 2021-09-06 08:53:26 +08:00
刘祥超
13221faee0 实现区域监控节点运行日志和运行结果 2021-09-06 08:53:08 +08:00
刘祥超
abda14f543 实现基本的区域监控终端管理功能 2021-09-05 11:10:07 +08:00
刘祥超
3f0e459181 删除不需要的文件 2021-09-04 10:27:45 +08:00
刘祥超
3df61381a4 删除部分不需要的文件/阶段性提交 2021-09-04 10:17:14 +08:00
刘祥超
daf8ae2192 修复IP地址保存后跳转错误的问题 2021-09-04 08:27:24 +08:00
刘祥超
3bf374ea5a 优化URL跳转交互 2021-09-01 08:49:59 +08:00
刘祥超
94b3559eee 增加独立的IP地址管理功能 2021-08-31 17:24:30 +08:00
刘祥超
1c93b42681 企业认证信息中增加节点数显示 2021-08-30 18:57:35 +08:00
刘祥超
1d9d44ecc3 数据看板和节点看板都改成异步加载 2021-08-30 15:23:31 +08:00
刘祥超
19178f963e 服务看板改成异步 2021-08-30 11:26:23 +08:00
刘祥超
4dc54f95b6 优化数据库节点管理 2021-08-30 10:56:03 +08:00
刘祥超
0790403923 优化交互 2021-08-30 09:52:09 +08:00
刘祥超
c55008d302 看板在健康检查失败离线时提供手动上线按钮 2021-08-29 16:56:37 +08:00
刘祥超
df9df94bb8 节点选择集群时可以根据关键词搜索 2021-08-29 16:41:59 +08:00
刘祥超
502ce4e414 健康检查连续下线次数默认值从1次改为3次 2021-08-29 15:26:56 +08:00
刘祥超
f5f07c0e96 可以在节点列表中直接修改节点所属线路 2021-08-29 14:00:59 +08:00
刘祥超
9549424835 缓存策略里的默认缓存条件增加、修改或者删除后自动保存 2021-08-29 09:40:21 +08:00
刘祥超
062b9baeab 修复缓存条件状态码无法修改的问题 2021-08-29 09:22:02 +08:00
刘祥超
08d1fcaf03 服务 -- 缓存设置中增加清理和预热功能 2021-08-29 09:21:44 +08:00
刘祥超
ad7523a500 修改accessKeys package因为大小写而无法编译的问题 2021-08-26 14:02:53 +08:00
刘祥超
28aa0f3b76 删除不需要的文件 2021-08-26 14:01:48 +08:00
刘祥超
8247843936 添加IP到IP名单时,可以选择批量输入 2021-08-26 10:28:52 +08:00
刘祥超
09ff5a37a9 DNS服务商支持搜索 2021-08-25 18:46:57 +08:00
刘祥超
663ec7710c 集群支持使用域名搜索 2021-08-25 18:39:35 +08:00
刘祥超
0dad0b10e6 选择DNS线路时增加搜索/节点如果没有设置DNS线路就使用默认线路 2021-08-25 17:16:14 +08:00
刘祥超
8c352813c4 优化HTTP Header管理界面交互 2021-08-25 14:09:37 +08:00
刘祥超
745f5d9759 优化集群选择DNS设置交互 2021-08-25 11:55:06 +08:00
刘祥超
6536ace4f7 默认集群没有节点时,在看板提示添加 2021-08-25 11:40:57 +08:00
刘祥超
67e6e06e72 新安装检查数据库权限后删除测试表 2021-08-25 11:27:09 +08:00
刘祥超
d0bf85de17 创建集群时自动创建缓存策略和WAF策略/优化界面 2021-08-25 11:17:24 +08:00
刘祥超
14cb3bdc67 增加忽略相似消息周期设置 2021-08-24 20:45:07 +08:00
刘祥超
e9dadd5571 消息接收人可以设置接收消息时间段 2021-08-24 17:46:00 +08:00
刘祥超
d5eceb05ce 消息发送任务队列可以删除单个任务 2021-08-24 16:36:47 +08:00
刘祥超
b510c88f84 通知媒介可以设置发送频率 2021-08-24 15:46:37 +08:00
刘祥超
16e409e5f5 通知媒介增加任务队列查看功能 2021-08-24 14:22:15 +08:00
刘祥超
ad257fcd0e 优化js 2021-08-24 09:31:14 +08:00
刘祥超
e84087d6ec 修复指标table类型图表的js错误 2021-08-24 09:31:07 +08:00
刘祥超
218c2385f0 在服务日志中增加WAF日志筛选 2021-08-22 16:49:15 +08:00
刘祥超
4ef8c2bc77 全局访问日志增加WAF日志/优化交互/数据看板--WAF--最新拦截记录增加更多链接 2021-08-22 16:34:20 +08:00
刘祥超
d9bc1bdfca Dashboard可以提示API节点升级 2021-08-21 19:44:03 +08:00
刘祥超
a5c287e8d6 自建DNS支持递归查询 2021-08-21 16:46:49 +08:00
刘祥超
1e9f5c9c56 DNS访问日志显示匹配的线路 2021-08-20 11:27:06 +08:00
刘祥超
fbcf439f89 集群界面数据改成异步加载 2021-08-20 10:51:01 +08:00
刘祥超
766c3666d8 添加DNS账号时自动读取DNS服务商下域名 2021-08-19 14:26:25 +08:00
刘祥超
f08289351d IP名单批量导入IP支持CIDR 2021-08-19 10:50:15 +08:00
刘祥超
e426bba8b5 优化界面 2021-08-18 17:09:09 +08:00
刘祥超
4b200762a5 节点IP地址可以设置阈值 2021-08-18 16:19:07 +08:00
刘祥超
fd203ed436 节点IP增加是否启用、是否在线状态 2021-08-18 09:24:03 +08:00
刘祥超
04cea4dbd6 优化界面 2021-08-17 10:48:38 +08:00
刘祥超
a8f1056c3c 调整版本为0.3.0 2021-08-17 09:44:14 +08:00
刘祥超
c2576f4c62 优化界面 2021-08-16 14:44:32 +08:00
刘祥超
1da5d1094f WAF设置处增加文字提示 2021-08-16 10:03:44 +08:00
刘祥超
34e6122407 修复可能edgeTest无法升级的问题 2021-08-16 10:03:14 +08:00
刘祥超
ee61feb581 优化IPBox交互 2021-08-15 15:44:24 +08:00
刘祥超
7f04f1ed62 IPBox增加地域、ISP、添加到黑名单等功能 2021-08-15 15:42:05 +08:00
刘祥超
82646c3576 改进细节 2021-08-15 10:39:22 +08:00
刘祥超
b3f62240c4 节点选择认证时增加推荐 2021-08-14 21:33:48 +08:00
刘祥超
86a5992e8a 优化节点创建流程 2021-08-14 18:06:24 +08:00
刘祥超
82a731ed06 修复在MySQL8下安装提示无法创建edgeTest的问题 2021-08-13 19:37:56 +08:00
刘祥超
e650529efb 可以远程停止和启动DNS节点 2021-08-12 11:47:27 +08:00
刘祥超
1de57e124f DNS节点可以修改SSH登录相关信息 2021-08-12 11:04:28 +08:00
刘祥超
9e72dcc390 实现DNS节点远程安装 2021-08-11 20:59:58 +08:00
刘祥超
80019e2071 修改文字 2021-08-11 17:16:36 +08:00
刘祥超
ab7cdcd1b1 增加全局访问日志 2021-08-11 15:38:49 +08:00
刘祥超
7afe1e0a30 安全设置检查IP时同时也检查直接连接管理平台的上游IP 2021-08-11 10:01:23 +08:00
刘祥超
a39eb80214 安全设置中增加允许记住登录选项 2021-08-11 09:36:16 +08:00
刘祥超
10e2a08cd2 访问日志显示节点信息 2021-08-10 11:14:41 +08:00
刘祥超
6da4949c98 优化自建DNS交互 2021-08-10 10:47:12 +08:00
刘祥超
4d092f329b 自建DNS增加解析测试 2021-08-09 18:42:00 +08:00
刘祥超
69c1d35406 EdgeDNS支持内置线路 2021-08-09 13:56:11 +08:00
刘祥超
a256a7328e DNS节点增加在线状态通知 2021-08-08 10:29:57 +08:00
刘祥超
75c8658366 访问日志搜索增加域名和IP搜索 2021-08-07 22:04:30 +08:00
刘祥超
c3e68915a3 优化代码/支持IP名单的更多格式的导入、导出 2021-08-07 18:26:50 +08:00
刘祥超
d97f4da7fa 修复节点无法修改线路的Bug 2021-08-07 16:11:04 +08:00
刘祥超
06e81dbe37 调整版本为0.2.9 2021-08-07 15:41:21 +08:00
刘祥超
4a46aaa880 自建DNS增加全局配置 2021-08-05 16:08:18 +08:00
刘祥超
e766372a81 域名解析支持华为云解析DNS 2021-08-04 22:14:35 +08:00
刘祥超
c5ee9f095a 调整版本号 2021-08-04 15:36:30 +08:00
刘祥超
9734ed1cf8 调整版本号 2021-08-03 10:40:24 +08:00
刘祥超
548d122c64 修复在自动安装过程中不能填写SSH认证用户名的Bug 2021-08-03 06:29:01 +08:00
刘祥超
c5205ef7a9 优化界面 2021-08-02 14:51:15 +08:00
刘祥超
f71a27960b 优化界面 2021-08-01 14:54:27 +08:00
刘祥超
d747e656dc 优化界面 2021-08-01 11:13:17 +08:00
刘祥超
867215e2af 初步实现多集群共享节点 2021-07-31 22:23:07 +08:00
刘祥超
3614b9f3b7 修复服务设置 -- HTTP/HTTPS页面可能为空的Bug 2021-07-30 13:43:36 +08:00
刘祥超
cbfa4c85c1 优化文字 2021-07-29 17:40:16 +08:00
刘祥超
b9bef5042a 优化界面 2021-07-29 17:33:42 +08:00
刘祥超
46e523d005 实现基本的访问日志策略 2021-07-29 16:51:22 +08:00
刘祥超
fa2930dfc5 调整样式 2021-07-27 15:24:32 +08:00
刘祥超
086ab15e8a 优化交互 2021-07-27 11:49:04 +08:00
刘祥超
c2e3e784a2 优化一处规则显示 2021-07-27 11:26:52 +08:00
刘祥超
9dd01bd36e 网站服务显示服务错误的时候增加节点信息和链接 2021-07-27 10:48:42 +08:00
刘祥超
02d08b0762 图表中攻击流量类型改为Line Area 2021-07-27 09:51:47 +08:00
刘祥超
cbd312e67d 指标图表可以设置忽略空值和其他对象值 2021-07-26 16:44:22 +08:00
刘祥超
5084d9f87e 各个线图改成圆滑曲线 2021-07-26 14:32:24 +08:00
刘祥超
3c3408624d URL跳转模式默认改成匹配前缀 2021-07-26 12:57:41 +08:00
刘祥超
6e8403390b 自动跳转到HTTPS可以设置允许和排除的域名 2021-07-26 11:21:47 +08:00
刘祥超
ef9e5d4bbc 修复一个跳转到详情的链接错误 2021-07-26 10:09:07 +08:00
刘祥超
c0b49948d7 版本改为0.2.6 2021-07-26 08:27:06 +08:00
刘祥超
48b066fb83 改进文字 2021-07-25 20:34:31 +08:00
刘祥超
7d551d59db 缓存目录默认为/opt/cache 2021-07-25 20:19:58 +08:00
刘祥超
5a057857dc 点击访问日志的IP弹出该IP最近访问日志 2021-07-25 19:58:30 +08:00
刘祥超
0a9cea722c 修改部分文字 2021-07-25 19:42:07 +08:00
刘祥超
f7eb7b1ec6 将访问日志保留天数从30天改成14天 2021-07-25 19:41:38 +08:00
刘祥超
0eb57a734e 更新编译脚本 2021-07-25 16:28:48 +08:00
刘祥超
82ff9a2b06 区分社区版和商业版 2021-07-25 15:44:14 +08:00
刘祥超
051a48bc73 DNS支持TSIG 2021-07-25 15:08:11 +08:00
刘祥超
6b9d5cf7a0 DNS服务支持密钥管理/“域名服务”改为“自建DNS” 2021-07-25 09:44:29 +08:00
刘祥超
2e7d64d97b 更新相关库 2021-07-22 18:44:55 +08:00
刘祥超
dd6050ee0b 修复在手机端无法正常浏览图表的Bug 2021-07-22 10:45:36 +08:00
刘祥超
c954910728 修复企业版身份认证可能会导致异常退出的问题 2021-07-22 10:45:23 +08:00
刘祥超
0fe2bc4a54 修复在使用内存缓存时导致节点看板页为空的Bug 2021-07-22 08:25:42 +08:00
刘祥超
9f39cd615f Demo模式下不允许调用Sync方法 2021-07-22 08:25:14 +08:00
刘祥超
07bf21dbb4 修改DEMO链接地址 2021-07-21 22:18:08 +08:00
刘祥超
43e8c65473 增加DEMO连接 2021-07-21 22:14:40 +08:00
刘祥超
8a926eabbe 增加DEMO模式、修复监控节点、用户节点、认证节点无法查看运行日志的Bug 2021-07-21 22:14:33 +08:00
刘祥超
fcbed5f63e 修复DNS看板中Y坐标轴不正确的问题 2021-07-21 09:02:28 +08:00
刘祥超
8d203a1d7c 优化界面 2021-07-21 08:07:46 +08:00
刘祥超
d6efb2dd12 域名记录可以停用/启用 2021-07-20 19:02:45 +08:00
刘祥超
6ea3938f74 自动替换API节点时增加对新节点的测试 2021-07-20 18:10:22 +08:00
刘祥超
63e773ce96 增加恢复模式 2021-07-20 17:15:17 +08:00
刘祥超
60b8e1041c 在各个地方支持IPv6 2021-07-20 10:55:25 +08:00
刘祥超
fb263c926b 创建网站服务后自动开启访问日志 2021-07-20 08:48:55 +08:00
刘祥超
dbbae30f5a 创建网站服务后自动开启Websocket 2021-07-20 08:29:50 +08:00
刘祥超
7b73c86fa4 增加内置的统计指标 2021-07-19 20:38:19 +08:00
刘祥超
c16c6ea6a6 优化指标图表 2021-07-19 18:19:33 +08:00
刘祥超
4ecba0040a Dashboard增加指标图表 2021-07-19 17:58:02 +08:00
刘祥超
f65aabe897 指标中列出使用当前指标的集群 2021-07-19 15:48:36 +08:00
刘祥超
de2337afe7 实现公用的统计指标 2021-07-19 15:23:20 +08:00
刘祥超
3360f2fc08 实现新的CC 2021-07-19 10:48:53 +08:00
刘祥超
7d8f3a9d9b IP名单中可以通过IP查找访问日志 2021-07-18 17:09:31 +08:00
刘祥超
63884bc836 优化WAF动作 2021-07-18 15:52:50 +08:00
刘祥超
9c231a2b8c WAF支持更多动作 2021-07-14 22:45:52 +08:00
刘祥超
6a5c979d2a 修复一个指标图表无法显示时间的Bug 2021-07-14 22:44:13 +08:00
刘祥超
094734ae1f 安装时不检查API地址是否可以绑定 2021-07-14 15:40:23 +08:00
刘祥超
97b4d457fd IP测试时同时也检查绑定的IP名单 2021-07-13 15:50:09 +08:00
刘祥超
9669c0ddda 路径规则改成路由规则 2021-07-13 14:28:06 +08:00
刘祥超
daf8257a1f 优化看板 2021-07-13 11:04:18 +08:00
刘祥超
40f32c7cf9 访问日志增加更容易可视化的时间显示 2021-07-12 18:06:43 +08:00
刘祥超
b8295e5cfc 改进界面 2021-07-12 17:37:10 +08:00
刘祥超
aba489217a 修复<keyword>组件的安全问题 2021-07-12 17:35:33 +08:00
刘祥超
7775689dfa 实现数据看板--WAF 2021-07-12 16:56:56 +08:00
刘祥超
90397b2b6f 修改一处文字 2021-07-12 10:27:35 +08:00
刘祥超
32683a2b59 管理界面可以切换风格 2021-07-12 10:21:17 +08:00
刘祥超
9e36847397 实现数据看板-DNS 2021-07-11 21:44:03 +08:00
刘祥超
4c82804622 实现数据看板--用户 2021-07-11 18:05:49 +08:00
刘祥超
cb911c474e 数据看板使用字节代替比特 2021-07-11 11:09:41 +08:00
刘祥超
ce2537d69d 节点看板磁盘缓存用量增加剩余磁盘空间 2021-07-08 19:47:49 +08:00
刘祥超
38746e83c5 节点看板增加缓存目录用量 2021-07-08 19:43:39 +08:00
刘祥超
be8988d309 优化关键词显示 2021-07-08 16:23:51 +08:00
刘祥超
ed59994db8 实现服务看板(企业版) 2021-07-07 19:55:32 +08:00
刘祥超
11a8d3ba70 优化交互 2021-07-07 19:02:39 +08:00
刘祥超
adf91bf3d0 节点列表增加下行流量,节点列表可以按CPU、内存、下行流量排序 2021-07-07 15:17:41 +08:00
刘祥超
7729b88a00 取消节点的监控图表,使用节点看板代替 2021-07-06 20:09:47 +08:00
刘祥超
e93aecabb4 实现节点看板(仅对企业版开放) 2021-07-06 20:06:20 +08:00
刘祥超
3cf43b2cc1 优化API节点详情页 2021-07-06 15:31:09 +08:00
刘祥超
3cba8cf98d 优化ACME任务修改页面 2021-07-06 15:30:06 +08:00
刘祥超
e2ca94c93b 实现集群看板(暂时只对企业版开放) 2021-07-05 11:38:07 +08:00
刘祥超
1c3e25ba1a 指标增加一些易读的名称等信息 2021-07-03 18:05:55 +08:00
刘祥超
f297f4ec52 实现基本的图表管理 2021-07-03 15:44:49 +08:00
刘祥超
534f10b8a7 指标数据增加占比数据 2021-07-01 10:39:07 +08:00
刘祥超
a7aed3d49e 实现基础的统计指标 2021-06-30 19:59:59 +08:00
刘祥超
9a915a48b5 SSH认证--私钥认证方式增加用户名选项 2021-06-30 14:55:21 +08:00
刘祥超
81a3b299f0 优化交互 2021-06-28 21:09:22 +08:00
刘祥超
a4edae6692 修改版本为0.2.5 2021-06-28 10:31:56 +08:00
刘祥超
047f6c409f API节点相关操作增加一些提示 2021-06-28 09:38:26 +08:00
刘祥超
963be2bc63 阶段性提交 2021-06-27 21:59:06 +08:00
刘祥超
bd34a9bd37 服务列表可以搜索端口号 2021-06-25 11:04:45 +08:00
刘祥超
32b93b2bd9 安装时默认设置访问日志保留30天 2021-06-24 21:06:43 +08:00
刘祥超
e7c4507b0b 集群设置左侧菜单显示TOA设置状态 2021-06-24 18:03:33 +08:00
刘祥超
17ae34c44f ACME申请证书时可以设置回调URL 2021-06-24 09:58:44 +08:00
刘祥超
b466ea2fd1 ACME申请证书时可以设置回调URL 2021-06-24 09:27:11 +08:00
刘祥超
8e4ee54f03 实现公用的IP名单 2021-06-23 13:12:33 +08:00
刘祥超
c99547d9e3 变更版本 2021-06-21 14:44:13 +08:00
刘祥超
1d22125d6e 管理员也支持AccessKey 2021-06-20 19:23:02 +08:00
刘祥超
d4689571cb 优化界面 2021-06-19 22:20:15 +08:00
刘祥超
cb1cb62e30 访问控制支持基本认证和子请求认证 2021-06-19 21:35:38 +08:00
刘祥超
77ad860d35 阶段性提交 2021-06-17 21:17:43 +08:00
刘祥超
5082561dc9 调整版本为0.2.3 2021-06-17 18:46:07 +08:00
刘祥超
7d4c80eedb 调整版本为0.2.2 2021-06-17 18:08:14 +08:00
刘祥超
af4bd950b7 用户增加AccessKey管理 2021-06-16 10:48:45 +08:00
刘祥超
f85f9b0920 调整版本为0.2.1 2021-06-16 08:30:44 +08:00
刘祥超
89ea96c28e Update README.md 2021-06-15 14:32:04 +08:00
刘祥超
6f2e3f5f13 ReadMe增加对Gitee的感谢 2021-06-15 11:15:03 +08:00
刘祥超
ee91e8312f 优化界面 2021-06-15 10:32:40 +08:00
刘祥超
004a29846e 优化界面 2021-06-14 19:53:53 +08:00
刘祥超
01eb22a0fe WAF的出站规则支持请求相关参数 2021-06-12 10:53:59 +08:00
刘祥超
bb2d2c5c86 优化安装过程中的错误提示 2021-06-11 12:00:11 +08:00
刘祥超
e0c36e3c45 优化界面 2021-06-10 19:19:43 +08:00
刘祥超
9fa9f74b90 优化界面 2021-06-10 10:39:05 +08:00
刘祥超
7308748957 去除一处不必要的注释 2021-06-09 22:15:23 +08:00
刘祥超
748105eae1 路径规则、重写规则、URL跳转规则均支持匹配条件;URL跳转规则增加排序、是否启用功能 2021-06-09 21:45:26 +08:00
刘祥超
33f45dfc92 请求条件支持参数匹配 2021-06-09 17:14:31 +08:00
刘祥超
dda1e3054a 优化代码 2021-06-08 22:58:39 +08:00
刘祥超
a8030fa045 优化缓存条件设置,支持不缓存条件设置,支持条件排序等 2021-06-08 22:46:08 +08:00
刘祥超
9de66f080d 优化代码 2021-06-08 15:20:59 +08:00
刘祥超
7a57180ecd 请求统计增加即时、按天 2021-06-08 15:08:38 +08:00
刘祥超
48e1e0b631 增加服务流量统计 2021-06-08 11:22:44 +08:00
刘祥超
d5fe38ba8d 优化界面 2021-06-07 16:23:37 +08:00
刘祥超
cb8035452b 支持UDP代理 2021-06-07 15:43:43 +08:00
刘祥超
c2cab60940 边缘节点IP支持IPV6 2021-06-07 10:00:06 +08:00
刘祥超
58e7e1b283 WAF策略列表支持搜索 2021-06-07 08:58:54 +08:00
刘祥超
5d108b1b6f 优化界面 2021-06-07 08:44:06 +08:00
刘祥超
8fbbbd65c4 缓存策略列表支持搜索 2021-06-07 08:43:57 +08:00
刘祥超
7e0a96857c 创建服务的时候默认增加http和https端口绑定 2021-06-06 14:10:45 +08:00
刘祥超
139b0e037e 日志级别增加success 2021-06-06 13:40:47 +08:00
刘祥超
7cbfa2458b 优化交互 2021-06-06 09:22:59 +08:00
刘祥超
abc5162bca 优化添加端口、添加源站相关交互 2021-06-05 20:27:57 +08:00
刘祥超
d3cf71dad8 改进界面 2021-06-05 16:53:26 +08:00
刘祥超
0909164a06 域名服务集群创建时可以选择开启访问日志 2021-06-05 15:38:57 +08:00
刘祥超
6df50d97e9 改进RPC相关提示 2021-06-05 11:35:36 +08:00
刘祥超
98f8fb3bec 服务访问日志增加关键词搜索 2021-06-04 10:15:31 +08:00
刘祥超
0f5f175f08 DNS访问日志可以使用关键词搜索 2021-06-04 09:09:23 +08:00
刘祥超
16a2a6cb35 修改域名服务界面细节 2021-06-03 22:09:12 +08:00
刘祥超
da38fcaa0c 实现在域名解析中使用EdgeDNS 2021-06-02 18:14:00 +08:00
刘祥超
b6fd9aeb14 域名服务增加访问日志 2021-06-02 11:53:08 +08:00
刘祥超
201c3e2f97 优化域名服务相关功能 2021-06-01 16:42:25 +08:00
刘祥超
d834c2517a [域名服务]实现基本的线路配置 2021-05-30 16:31:12 +08:00
刘祥超
dc98081a92 更新go.mod 2021-05-30 11:48:30 +08:00
刘祥超
30e9753c80 实现基本的域名、记录管理 2021-05-27 17:09:53 +08:00
刘祥超
70cd06dc85 实现最基本的域名服务节点管理 2021-05-26 14:43:29 +08:00
刘祥超
194a919a73 延长缓存相关操作超时时间 2021-05-25 18:24:45 +08:00
刘祥超
b57b242232 优化API命名 2021-05-25 17:48:51 +08:00
刘祥超
cc76172a46 实现域名服务器集群管理 2021-05-25 15:47:40 +08:00
刘祥超
5858f98ec6 改进节点日志显示 2021-05-25 14:14:57 +08:00
刘祥超
941e000142 修改版本号为0.2.0 2021-05-25 12:14:53 +08:00
刘祥超
7dd58fd9dc 优化日志显示 2021-05-25 12:14:21 +08:00
刘祥超
2b560a225e 修改版本号为0.1.2 2021-05-24 16:51:57 +08:00
刘祥超
c768f22059 使用CodeMirror高亮各处的配置信息 2021-05-24 11:23:20 +08:00
刘祥超
f738ba9152 缓存支持默认条件 2021-05-24 09:02:55 +08:00
刘祥超
95c27119ac 缓存策略可以根据Key前缀进行批量删除 2021-05-23 22:49:53 +08:00
刘祥超
2b2b516788 优化集群批量操作结果显示 2021-05-23 22:36:52 +08:00
刘祥超
0bb63b5bb7 SSH认证可搜索 2021-05-23 21:12:52 +08:00
刘祥超
48012072d7 SSL/TLS选择证书增加关键词搜索 2021-05-23 20:54:17 +08:00
刘祥超
0053949bc0 在服务列表中显示需要修复的错误 2021-05-23 20:44:51 +08:00
刘祥超
88e7246366 将节点日志级别筛选中的警告从warn改成warning 2021-05-23 20:03:46 +08:00
刘祥超
fbedf3b605 添加端口时校验端口范围 2021-05-23 20:03:14 +08:00
刘祥超
d5659e627d URL跳转支持正则匹配 2021-05-23 17:01:54 +08:00
刘祥超
709d4dc805 增加Licenses目录 2021-05-20 15:39:21 +08:00
刘祥超
37f052e9a5 节点监控图表流量使用秒来计算 2021-05-20 10:38:46 +08:00
刘祥超
58c765e97f 实现日志消息聚合 2021-05-19 20:52:41 +08:00
刘祥超
da332c2756 增加全局的边缘节点日志 2021-05-19 19:03:03 +08:00
刘祥超
e9d329bf8b 变更版本 2021-05-19 19:02:38 +08:00
刘祥超
775b46cf8f 修改版本 2021-05-13 14:36:22 +08:00
刘祥超
c99eb54811 增加edge-admin reset命令,用来清理对应的配置 2021-05-13 14:27:17 +08:00
刘祥超
7917c68b6f 节点详情中增加缓存用量显示 2021-05-13 11:50:15 +08:00
刘祥超
75ce1490cc 节点可以单独设置缓存的磁盘、内存容量 2021-05-12 21:37:43 +08:00
刘祥超
6f1cd668b6 访问日志中增加缓存状态 2021-05-12 16:31:43 +08:00
刘祥超
5b7cb59997 缓存设置中增加自动添加X-Cache Header 2021-05-12 16:09:40 +08:00
刘祥超
67242ec80e 缓存匹配条件增加“排除URL正则匹配” 2021-05-12 16:05:18 +08:00
刘祥超
2af5e059eb 缓存条件增加多个匹配方式/优化缓存设置界面 2021-05-12 15:07:30 +08:00
刘祥超
1d992b0e97 数据看板显示升级提醒 2021-05-11 22:47:21 +08:00
刘祥超
0c650987d6 服务支持fastcgi;路径规则支持匹配后缀 2021-05-10 21:13:09 +08:00
刘祥超
e8af3960f8 在访问日志中显示ws和wss 2021-05-08 20:52:13 +08:00
刘祥超
1d95087abb Websocket默认不开启 2021-05-08 19:51:05 +08:00
刘祥超
c350fceef3 实现一些阈值设置细节 2021-05-05 19:51:13 +08:00
刘祥超
e6970abcb8 实现基础的阈值设置 2021-05-04 21:02:25 +08:00
刘祥超
9cf5f56e59 修复因为ClusterHelper而导致POST Action可能被阻止的情形 2021-05-03 15:31:59 +08:00
刘祥超
c2ebf81e6c 记录和显示最近常用的服务 2021-05-03 15:15:31 +08:00
刘祥超
19a2af8bfa 记录和显示最近常用的集群 2021-05-03 11:32:59 +08:00
刘祥超
df5c1262d2 增加连接数监控图表 2021-04-29 17:06:40 +08:00
刘祥超
26340110c1 实现基本的监控图表 2021-04-29 16:47:45 +08:00
刘祥超
452f03bb78 修改TB、PB、EB的有些计算错误 2021-04-29 15:04:42 +08:00
刘祥超
609794b2d8 修复TLS证书配置时无法删除证书的问题 2021-04-28 14:56:47 +08:00
刘祥超
554d0daf6d 财务管理只有企业版才默认打开 2021-04-28 14:56:19 +08:00
刘祥超
e4015282a5 缓存设置中增加“支持分片内容”选项,用来支持Chunked内容 2021-04-18 22:16:46 +08:00
刘祥超
a7453a4baa 修复ttlcache的一个bug 2021-04-18 21:50:26 +08:00
刘祥超
913a98b4de 变更版本号 2021-04-18 21:25:01 +08:00
刘祥超
89f391ca22 增加SSH认证连接测试功能 2021-04-18 21:19:50 +08:00
刘祥超
e585149a7e 修改文件缓存策略的文件目录最大容量和内存最大容量值 2021-04-18 19:05:35 +08:00
刘祥超
f9f7cd723a 改进界面 2021-04-18 16:30:58 +08:00
刘祥超
4a813f08cd 改进界面 2021-04-18 15:58:52 +08:00
刘祥超
e75757f2bc 改进安装界面中的小细节 2021-04-18 15:41:47 +08:00
刘祥超
91010325eb 优化集群没有节点时的提示 2021-04-18 15:17:49 +08:00
刘祥超
441c0bd628 优化界面 2021-04-15 17:11:38 +08:00
刘祥超
a91b97ff2b 域名解析增加CloudFlare DNS支持 2021-04-15 17:02:35 +08:00
刘祥超
fb775a0ee9 版本认证中增加公司/组织名 2021-04-14 20:02:08 +08:00
刘祥超
8ed02c8103 增加认证节点管理 2021-04-13 21:23:13 +08:00
刘祥超
42a306ff22 增加企业版认证相关API 2021-04-13 20:01:43 +08:00
刘祥超
a637783249 实现发送消息到媒介 2021-04-12 19:19:59 +08:00
刘祥超
7ffff890a8 URL跳转增加匹配前缀和是否保留RequestURI选项 2021-04-07 11:20:23 +08:00
刘祥超
dbfbdddb3a 实现监控节点管理 2021-04-06 16:32:23 +08:00
刘祥超
f85b3a40ea 实现基础的通知媒介管理 2021-04-05 20:48:13 +08:00
刘祥超
27421bbd46 优化错误提示界面 2021-04-05 08:18:17 +08:00
刘祥超
129d2ccef0 系统用户增加是否允许登录选项 2021-03-30 11:00:06 +08:00
刘祥超
a553bad1ef 优化界面细节 2021-03-29 17:47:10 +08:00
刘祥超
ae7020437b 不在界面上显示SSH认证的密码,提升安全性 2021-03-28 16:21:46 +08:00
刘祥超
74e6c7a87c “系统设置 -- 安全管理”里可以单独添加允许访问的IP 2021-03-28 15:50:28 +08:00
刘祥超
658c5ae6dd 优化左侧菜单显示 2021-03-28 14:47:21 +08:00
刘祥超
7a24a69223 反向代理可以整体设置源站默认超时时间等参数 2021-03-26 22:08:18 +08:00
刘祥超
de90acaae4 反向代理中源站增加最大连接数、连接超时时间等参数 2021-03-25 21:18:50 +08:00
刘祥超
51080f2669 申请证书任务可以筛选和搜索 2021-03-16 18:10:00 +08:00
刘祥超
fa5d97b74c 访问日志默认为关闭 2021-03-16 10:24:22 +08:00
刘祥超
42ba081cd1 修改README中的文档链接地址 2021-03-11 14:42:45 +08:00
刘祥超
404d733ba1 增加编译脚本 2021-03-11 14:41:09 +08:00
刘祥超
2f3398e251 优化界面 2021-03-09 14:45:16 +08:00
刘祥超
5c09e272f3 修改版本号 2021-03-09 14:44:47 +08:00
刘祥超
d045f0526f 文件缓存策略支持二级缓存(内存 | 文件) 2021-03-02 19:38:13 +08:00
刘祥超
0766ec9d5a WAF动作增加显示HTML内容 2021-02-26 16:32:07 +08:00
刘祥超
4511b48382 可以设置管理界面和用户界面的浏览器图标和Logo 2021-02-25 20:54:30 +08:00
刘祥超
151e58fbb9 优化界面 2021-02-25 19:07:58 +08:00
刘祥超
c833ac2e96 因为健康检查下线的节点可以手动恢复上线 2021-02-24 19:27:33 +08:00
刘祥超
99983330a3 集群健康检查URL中可以输入主机名 2021-02-24 16:20:04 +08:00
刘祥超
a14e81a28f 优化界面显示 2021-02-24 15:01:45 +08:00
刘祥超
fc714a15a3 相关统计图表中X坐标显示所有标签 2021-02-24 11:11:41 +08:00
刘祥超
8a8f9d2912 修复重新加载RPC连接时可能死锁的问题 2021-02-24 10:44:53 +08:00
刘祥超
58a0521605 自动更新API节点配置 2021-02-24 09:00:12 +08:00
刘祥超
e129b52f1c 更新版本号 2021-02-24 08:46:05 +08:00
刘祥超
5cefd900a4 终端统计中显示系统和浏览器的版本号 2021-02-07 09:23:55 +08:00
刘祥超
857dc70b4d 修复一个流量格式化错误 2021-02-07 09:16:58 +08:00
刘祥超
e3036025bc 修复反向代理配置组件addHeaders错误 2021-02-07 09:07:18 +08:00
刘祥超
76035dc3c2 创建或修改服务域名时检查域名是否重复 2021-02-06 21:52:00 +08:00
刘祥超
22c55d0b83 修改节点分组的节点数显示错误 2021-02-06 19:50:00 +08:00
刘祥超
9780937a1f 修改版本号 2021-02-06 19:49:36 +08:00
刘祥超
b4eb6e92e0 优化交互 2021-02-06 19:40:29 +08:00
刘祥超
245c4374b4 增加IP级别和WAF动作 2021-02-06 17:37:09 +08:00
刘祥超
ed302370e9 优化系统消息交互 2021-02-03 11:18:41 +08:00
刘祥超
b2feb452e0 可以批量删除同步任务 2021-02-02 20:52:46 +08:00
刘祥超
8b7661d82d 在WAF中增加检查IP状态功能 2021-02-02 19:30:07 +08:00
刘祥超
33c9cd0819 单个服务里也增加国家/地区、省份封禁 2021-02-02 16:23:23 +08:00
刘祥超
a0635a97d5 IP名单新增IPv6和所有IP两种类型 2021-02-02 15:25:11 +08:00
刘祥超
e22a9d061c 修复$HOME未定义问题 2021-02-01 09:25:57 +08:00
刘祥超
1b9d62c7bd 优化界面 2021-02-01 09:25:31 +08:00
刘祥超
2ca31ffcb4 界面显示时增加默认线路值 2021-01-31 16:09:55 +08:00
刘祥超
14636ed82f 可以批量远程安装和升级节点 2021-01-31 16:03:52 +08:00
刘祥超
cc5a34c20e 节点详情中显示更多信息(版本、CPU、负载等) 2021-01-31 11:05:04 +08:00
刘祥超
3ebb47d915 消息提示可以单个标为已读 2021-01-29 10:39:19 +08:00
刘祥超
3eea58c1fc 优化JS加载速度/修改蓝色背景 2021-01-29 10:04:00 +08:00
刘祥超
255bf58abf 优化JS代码加载速度 2021-01-28 17:04:36 +08:00
刘祥超
b5b324bca0 DNS更新任务增加域名更新 2021-01-28 15:39:10 +08:00
刘祥超
74e654446e 优化界面 2021-01-28 11:35:22 +08:00
刘祥超
be01536a09 支持自定义HTTP DNS 2021-01-28 11:29:50 +08:00
刘祥超
4c6bcc7c19 增加DNS同步任务状态显示 2021-01-27 22:59:46 +08:00
刘祥超
98dceb20db 优化界面 2021-01-27 11:53:42 +08:00
刘祥超
25833bc81a 增加管理界面截图 2021-01-27 11:40:32 +08:00
刘祥超
0d5fc7ad85 systemd Service安装失败时直接返回 2021-01-26 21:32:03 +08:00
刘祥超
b9af3d4757 修改README,加入组件源码地址等 2021-01-26 21:31:32 +08:00
刘祥超
f179b0a60e 优化界面 2021-01-26 20:35:29 +08:00
刘祥超
875162cfb6 可以配置是否在反向代理中添加X-Real-IP和X-Forwarded-* 2021-01-26 20:29:29 +08:00
刘祥超
2f21effdbf 实现WAF统计 2021-01-26 18:41:23 +08:00
刘祥超
f7336235a1 缩短右上菜单中同步状态检测时间 2021-01-26 10:29:53 +08:00
刘祥超
6ed0791991 访问日志可以设置只记录WAF相关日志 2021-01-26 10:29:29 +08:00
刘祥超
045a18fb22 删除不必要的文件 2021-01-25 18:40:38 +08:00
刘祥超
dbe598a934 对服务增加基础的数据统计 2021-01-25 16:40:49 +08:00
刘祥超
2812b30b01 优化界面 2021-01-24 14:41:43 +08:00
刘祥超
386d4957e7 修正一处文字提示错误(白名单->黑名单) 2021-01-21 20:46:08 +08:00
刘祥超
ea36e60899 优化Dashboard界面 2021-01-21 19:22:06 +08:00
刘祥超
bbf7e2898f 实现Dashboard 2021-01-21 18:55:53 +08:00
刘祥超
beab50de4c 变更版本号 2021-01-21 11:32:43 +08:00
刘祥超
a3ac7678d9 补充README文档 2021-01-21 11:32:32 +08:00
刘祥超
fc68167f77 WAF策略日志也加入地域信息 2021-01-20 14:19:10 +08:00
刘祥超
0bb379e4cc 创建空的WAF的时候增加serverId 2021-01-20 12:22:44 +08:00
刘祥超
7b350bcc37 显示用户节点运行状态 2021-01-20 10:08:50 +08:00
刘祥超
561b4ba591 可以清理数据库节点的数据表 2021-01-19 22:05:46 +08:00
刘祥超
74896babb5 数据库某些表可以手工清理 2021-01-19 16:14:09 +08:00
刘祥超
7f66c65770 实现自动清理服务访问日志 2021-01-19 12:05:26 +08:00
刘祥超
724622f678 高级设置/数据库/修改:数据库密码中支持特殊字符 2021-01-19 10:44:52 +08:00
刘祥超
b52313434b 优化界面显示 2021-01-19 10:21:53 +08:00
刘祥超
c6a14708df 可以单个服务中的WAF中设置出入站规则、黑白名单等 2021-01-18 20:39:58 +08:00
刘祥超
6468605325 修复安装时数据库环境没有指定的问题 2021-01-18 20:21:55 +08:00
刘祥超
6c3a130afe 管理平台也可以单独管理某个服务的黑白名单 2021-01-18 17:09:35 +08:00
刘祥超
b235b57392 修改版本为0.0.9 2021-01-18 17:09:07 +08:00
2110 changed files with 210316 additions and 13376 deletions

8
.gitignore vendored Normal file
View File

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

74
.golangci.yaml Normal file
View File

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

View File

@@ -1 +1,63 @@
![架构](doc/architect-zh.jpg)
# GoEdge目标
做一款人人用得起的CDN & WAF系统。
![截图](doc/screenshot.png)
## 特性
* `免费` - 开源、免费、自由、开放
* `简单` - 架构简单清晰,安装简单,使用简单,运维简单
* `高扩展性` - 可以自由扩展新的节点,支持亿级数据
## 功能介绍
* 多用户
* 日志审计
* 集群管理
* HTTP/HTTPS/TCP/UDP等协议支持
* WAF
* 缓存
* DNS自动解析
* 多域名绑定
* 免费证书申请
* IP黑白名单
* 访问日志
* 统计
* 内容压缩
* Protocol Proxy协议
* 本地静态文件
* URL跳转
* 路由规则
* 重写规则
* 访问控制
* 字符编码
* 自定义页面
* 自定义HTTP Header
* Websocket
* WebP自动转换
* Fastcgi
* 请求限制
* 流量限制
## 在线演示
* [http://demo.goedge.cn](http://demo.goedge.cn)
## 文档
* [新手指南](https://goedge.cn/docs/QuickStart/Index.md)
* [完整文档](https://goedge.cn/docs)
* [开发者指南](https://goedge.cn/docs/Developer/Build.md)
## 架构
![架构图](doc/architect-zh.png)
其中的组件源码地址如下:
* [边缘节点](https://github.com/TeaOSLab/EdgeNode)
* [API节点](https://github.com/TeaOSLab/EdgeAPI)
* [管理平台](https://github.com/TeaOSLab/EdgeAdmin)
## 联系我们
有什么问题和建议都可以加入 [Telegram群](https://t.me/+5kVCMGxQhZxiODY9)
## 企业版
* [GoEdge企业版](https://goedge.cn/commercial) - 功能更强大的CDN系统
## 感谢
* 感谢 [Gitee](https://gitee.com/) 提供国内源代码托管平台

3
build/.gitignore vendored
View File

@@ -1 +1,2 @@
edge-api/
edge-api/
build-all-test.sh

21
build/build-all.sh Executable file
View File

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

View File

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

View File

@@ -1,66 +1,141 @@
#!/usr/bin/env bash
function build() {
ROOT=$(dirname $0)
ROOT=$(dirname "$0")
JS_ROOT=$ROOT/../web/public/js
NAME="edge-admin"
DIST=$ROOT/"../dist/${NAME}"
OS=${1}
ARCH=${2}
TAG=${3}
if [ -z $OS ]; then
if [ -z "$OS" ]; then
echo "usage: build.sh OS ARCH"
exit
fi
if [ -z $ARCH ]; then
if [ -z "$ARCH" ]; then
echo "usage: build.sh OS ARCH"
exit
fi
if [ -z "$TAG" ]; then
TAG="community"
fi
VERSION=$(lookup-version $ROOT/../internal/const/const.go)
ZIP="${NAME}-${OS}-${ARCH}-v${VERSION}.zip"
# checking environment
echo "checking required commands ..."
commands=("zip" "unzip" "go" "find" "sed")
for cmd in "${commands[@]}"; do
if [ "$(which "${cmd}")" ]; then
echo "checking ${cmd}: ok"
else
echo "checking ${cmd}: not found"
return
fi
done
# check edge-api
APINodeVersion=$(lookup-version $ROOT"/../../EdgeAPI/internal/const/const.go")
VERSION=$(lookup-version "$ROOT"/../internal/const/const.go)
ZIP="${NAME}-${OS}-${ARCH}-${TAG}-v${VERSION}.zip"
# build edge-api
APINodeVersion=$(lookup-version "$ROOT""/../../EdgeAPI/internal/const/const.go")
echo "building edge-api v${APINodeVersion} ..."
EDGE_API_BUILD_SCRIPT=$ROOT"/../../EdgeAPI/build/build.sh"
if [ ! -f $EDGE_API_BUILD_SCRIPT ]; then
if [ ! -f "$EDGE_API_BUILD_SCRIPT" ]; then
echo "unable to find edge-api build script 'EdgeAPI/build/build.sh'"
exit
fi
cd $ROOT"/../../EdgeAPI/build"
cd "$ROOT""/../../EdgeAPI/build" || exit
echo "=============================="
./build.sh $OS $ARCH
./build.sh "$OS" "$ARCH" $TAG
echo "=============================="
cd -
cd - || exit
# generate files
echo "generating files ..."
env CGO_ENABLED=0 go run -tags $TAG "$ROOT"/../cmd/edge-admin/main.go generate
if [ "$(which uglifyjs)" ]; then
echo "compress to component.js ..."
uglifyjs --compress --mangle -- "${JS_ROOT}"/components.src.js > "${JS_ROOT}"/components.js
uglifyjs --compress --mangle -- "${JS_ROOT}"/utils.js > "${JS_ROOT}"/utils.min.js
else
echo "copy to component.js ..."
cp "${JS_ROOT}"/components.src.js "${JS_ROOT}"/components.js
cp "${JS_ROOT}"/utils.js "${JS_ROOT}"/utils.min.js
fi
# create dir & copy files
echo "copying ..."
if [ ! -d $DIST ]; then
mkdir $DIST
mkdir $DIST/bin
mkdir $DIST/configs
mkdir $DIST/logs
if [ ! -d "$DIST" ]; then
mkdir "$DIST"
mkdir "$DIST"/bin
mkdir "$DIST"/configs
mkdir "$DIST"/logs
fi
cp -R $ROOT/../web $DIST/
rm -f $DIST/web/tmp/*
cp $ROOT/configs/server.template.yaml $DIST/configs/
cp -R "$ROOT"/../web "$DIST"/
rm -f "$DIST"/web/tmp/*
rm -rf "$DIST"/web/public/js/components
rm -f "$DIST"/web/public/js/components.src.js
cp "$ROOT"/configs/server.template.yaml "$DIST"/configs/
EDGE_API_ZIP_FILE=$ROOT"/../../EdgeAPI/dist/edge-api-${OS}-${ARCH}-v${APINodeVersion}.zip"
cp $EDGE_API_ZIP_FILE $DIST/
cd $DIST/
unzip -q $(basename $EDGE_API_ZIP_FILE)
rm -f $(basename $EDGE_API_ZIP_FILE)
cd -
# change _plus.[ext] to .[ext]
if [ "${TAG}" = "plus" ]; then
echo "converting filenames ..."
exts=("html" "js" "css")
for ext in "${exts[@]}"; do
pattern="*_plus."${ext}
find "$DIST"/web/views -type f -name "$pattern" | \
while read filename; do
mv "${filename}" "${filename/_plus."${ext}"/."${ext}"}"
done
done
fi
EDGE_API_ZIP_FILE=$ROOT"/../../EdgeAPI/dist/edge-api-${OS}-${ARCH}-${TAG}-v${APINodeVersion}.zip"
cp "$EDGE_API_ZIP_FILE" "$DIST"/
cd "$DIST"/ || exit
unzip -q "$(basename "$EDGE_API_ZIP_FILE")"
rm -f "$(basename "$EDGE_API_ZIP_FILE")"
cd - || exit
# find gcc
GCC_DIR=""
CC_PATH=""
CXX_PATH=""
if [ "${ARCH}" == "amd64" ]; then
GCC_DIR="/usr/local/gcc/x86_64-unknown-linux-gnu/bin"
CC_PATH="x86_64-unknown-linux-gnu-gcc"
CXX_PATH="x86_64-unknown-linux-gnu-g++"
fi
if [ "${ARCH}" == "arm64" ]; then
GCC_DIR="/usr/local/gcc/aarch64-unknown-linux-gnu/bin"
CC_PATH="aarch64-unknown-linux-gnu-gcc"
CXX_PATH="aarch64-unknown-linux-gnu-g++"
fi
# build
echo "building "${NAME}" ..."
env GOOS=$OS GOARCH=$GOARCH go build -ldflags="-s -w" -o $DIST/bin/${NAME} $ROOT/../cmd/edge-admin/main.go
echo "building ${NAME} ..."
if [ -f "${GCC_DIR}/${CC_PATH}" ]; then
echo " building ${NAME} with gcc ..."
env CC="${GCC_DIR}/${CC_PATH}" \
CXX="${GCC_DIR}/${CXX_PATH}" \
CGO_ENABLED=1 \
GOOS="$OS" GOARCH="$ARCH" go build -trimpath -tags "${TAG} gcc" -ldflags="-linkmode external -extldflags -static -s -w" -o "$DIST"/bin/${NAME} "$ROOT"/../cmd/edge-admin/main.go
else
GOOS="$OS" GOARCH="$ARCH" go build -trimpath -tags $TAG -ldflags="-s -w" -o "$DIST"/bin/${NAME} "$ROOT"/../cmd/edge-admin/main.go
fi
if [ ! -f "${DIST}/bin/${NAME}" ]; then
echo "build '${NAME}' failed!"
exit
fi
# delete hidden files
find $DIST -name ".DS_Store" -delete
find $DIST -name ".gitignore" -delete
find "$DIST" -name ".DS_Store" -delete
find "$DIST" -name ".gitignore" -delete
find "$DIST" -name "*.less" -delete
#find "$DIST" -name "*.css.map" -delete
#find "$DIST" -name "*.js.map" -delete
# zip
echo "zip files ..."
@@ -77,15 +152,15 @@ function build() {
function lookup-version() {
FILE=$1
VERSION_DATA=$(cat $FILE)
VERSION_DATA=$(cat "$FILE")
re="Version[ ]+=[ ]+\"([0-9.]+)\""
if [[ $VERSION_DATA =~ $re ]]; then
VERSION=${BASH_REMATCH[1]}
echo $VERSION
echo "$VERSION"
else
echo "could not match version"
exit
fi
}
build $1 $2
build "$1" "$2" "$3"

View File

@@ -1,4 +1,6 @@
api.yaml
api_admin.yaml
server.yaml
api_db.yaml
api-123.yaml
*.pem
*.pem
*.cache.json

View File

@@ -1,4 +0,0 @@
rpc:
endpoints: [ "http://127.0.0.1:8003" ]
nodeId: ""
secret: ""

View File

@@ -1,6 +0,0 @@
rpc:
endpoints:
- http://192.168.2.40:8003
nodeId: H6sjDf779jimnVPnBFSgZxvr6Ca0wQ0z
secret: hMHjmEng0SIcT3yiA3HIoUjogwAC9cur

View File

@@ -0,0 +1,3 @@
rpc.endpoints: [ "http://127.0.0.1:8003" ]
nodeId: ""
secret: ""

22
build/generate.sh Executable file
View File

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

1
build/licenses/README.md Normal file
View File

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

View File

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

View File

@@ -1,19 +1,48 @@
package main
import (
"bytes"
"flag"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/apps"
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/gen"
"github.com/TeaOSLab/EdgeAdmin/internal/nodes"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web"
_ "github.com/TeaOSLab/EdgeCommon/pkg/langs/messages"
"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"
)
func main() {
app := apps.NewAppCmd().
var app = apps.NewAppCmd().
Version(teaconst.Version).
Product(teaconst.ProductName).
Usage(teaconst.ProcessName + " [-v|start|stop|restart|service|daemon]")
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").
Option("start", "start the service").
Option("stop", "stop the service").
Option("restart", "restart the service").
Option("service", "register service into systemd").
Option("daemon", "start the service with daemon").
Option("reset", "reset configs").
Option("recover", "enter recovery mode").
Option("demo", "switch to demo mode").
Option("dev", "switch to 'dev' mode").
Option("prod", "switch to 'prod' mode").
Option("upgrade [--url=URL]", "upgrade from official site or an url").
Option("install-local-node", "install a local node").
Option("security.reset", "reset security config")
app.On("daemon", func() {
nodes.NewAdminNode().Daemon()
@@ -22,11 +51,148 @@ func main() {
err := nodes.NewAdminNode().InstallSystemService()
if err != nil {
fmt.Println("[ERROR]install failed: " + err.Error())
return
}
fmt.Println("done")
})
app.On("reset", func() {
err := configs.ResetAPIConfig()
if err != nil {
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() {
var sock = gosock.NewTmpSock(teaconst.ProcessName)
if !sock.IsListening() {
fmt.Println("[ERROR]the service not started yet, you should start the service first")
return
}
_, err := sock.Send(&gosock.Command{Code: "recover"})
if err != nil {
fmt.Println("[ERROR]enter recovery mode failed: " + err.Error())
return
}
fmt.Println("enter recovery mode successfully")
})
app.On("demo", func() {
var sock = gosock.NewTmpSock(teaconst.ProcessName)
if !sock.IsListening() {
fmt.Println("[ERROR]the service not started yet, you should start the service first")
return
}
reply, err := sock.Send(&gosock.Command{Code: "demo"})
if err != nil {
fmt.Println("[ERROR]change demo mode failed: " + err.Error())
return
}
var isDemo = maps.NewMap(reply.Params).GetBool("isDemo")
if isDemo {
fmt.Println("change demo mode to: on")
} else {
fmt.Println("change demo mode to: off")
}
})
app.On("generate", func() {
err := gen.Generate()
if err != nil {
fmt.Println("generate failed: " + err.Error())
return
}
})
app.On("dev", func() {
var env = "dev"
var sock = gosock.NewTmpSock(teaconst.ProcessName)
_, err := sock.Send(&gosock.Command{
Code: env,
Params: nil,
})
if err != nil {
fmt.Println("failed to switch to '" + env + "': " + err.Error())
} else {
fmt.Println("switch to '" + env + "' ok")
}
})
app.On("prod", func() {
var env = "prod"
var sock = gosock.NewTmpSock(teaconst.ProcessName)
_, err := sock.Send(&gosock.Command{
Code: env,
Params: nil,
})
if err != nil {
fmt.Println("failed to switch to '" + env + "': " + err.Error())
} else {
fmt.Println("switch to '" + env + "' ok")
}
})
app.On("upgrade", func() {
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() {
var lastProgress float32 = 0
var isStarted = false
for range ticker.C {
if manager.IsDownloading() {
if !isStarted {
log.Println("start downloading v" + manager.NewVersion() + " ...")
isStarted = true
}
var progress = manager.Progress()
if progress >= 0 {
if progress == 0 || progress == 1 || progress-lastProgress >= 0.1 {
lastProgress = progress
log.Printf("%.2f%%", manager.Progress()*100)
}
}
} else {
break
}
}
}()
err := manager.Start()
if err != nil {
log.Println("upgrade failed: " + err.Error())
return
}
log.Println("finished!")
log.Println("restarting ...")
app.RunRestart()
})
app.On("security.reset", func() {
var sock = gosock.NewTmpSock(teaconst.ProcessName)
if !sock.IsListening() {
fmt.Println("[ERROR]the service not started yet, you should start the service first")
return
}
_, _ = sock.Send(&gosock.Command{
Code: "security.reset",
})
fmt.Println("ok")
})
app.Run(func() {
adminNode := nodes.NewAdminNode()
var adminNode = nodes.NewAdminNode()
adminNode.Run()
})
}

2
dist/.gitignore vendored
View File

@@ -1,2 +1,2 @@
*.zip
shield-admin
edge-admin

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

BIN
doc/architect-zh.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

BIN
doc/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

1
docker/.gitignore vendored Normal file
View File

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

41
docker/Dockerfile Normal file
View File

@@ -0,0 +1,41 @@
FROM --platform=linux/amd64 alpine:latest
LABEL maintainer="goedge.cdn@gmail.com"
ENV TZ "Asia/Shanghai"
ENV VERSION 1.3.5
ENV ROOT_DIR /usr/local/goedge
ENV TAR_FILE edge-admin-linux-amd64-plus-v${VERSION}.zip
# remote official repository
ENV TAR_URL "https://dl.goedge.cn/edge/v${VERSION}/edge-admin-linux-amd64-plus-v${VERSION}.zip"
# your local repository
#ENV TAR_URL "http://192.168.2.61:8080/edge-admin-linux-amd64-plus-v${VERSION}.zip"
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}

49
go.mod
View File

@@ -1,21 +1,50 @@
module github.com/TeaOSLab/EdgeAdmin
go 1.15
go 1.21
replace github.com/TeaOSLab/EdgeCommon => ../EdgeCommon
require (
github.com/TeaOSLab/EdgeCommon v0.0.0-00010101000000-000000000000
github.com/cespare/xxhash v1.1.0
github.com/cespare/xxhash/v2 v2.2.0
github.com/go-sql-driver/mysql v1.5.0
github.com/go-yaml/yaml v2.1.0+incompatible
github.com/iwind/TeaGo v0.0.0-20201206115018-cdd967bfb13d
github.com/miekg/dns v1.1.35
github.com/iwind/TeaGo v0.0.0-20240312020455-6f20b5121caf
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4
github.com/miekg/dns v1.1.43
github.com/quic-go/quic-go v0.42.0
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-20200724161237-0e2f3a69832c // indirect
google.golang.org/grpc v1.32.0
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
github.com/xlzd/gotp v0.1.0
golang.org/x/crypto v0.22.0
golang.org/x/sys v0.19.0
google.golang.org/grpc v1.63.2
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/frankban/quicktest v1.11.3 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/google/btree v1.0.0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20240402174815-29b9bb013b0f // indirect
github.com/kr/pretty v0.2.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/onsi/ginkgo/v2 v2.17.1 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/quic-go/qpack v0.4.0 // 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
go.uber.org/mock v0.4.0 // indirect
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.19.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
)

263
go.sum
View File

@@ -1,206 +1,131 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
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=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/frankban/quicktest v1.5.0 h1:Tb4jWdSpdjKzTUicPnY61PZxKbDoGa7ABbrReT3gQVY=
github.com/frankban/quicktest v1.5.0/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-redis/redis/v8 v8.0.0-beta.7/go.mod h1:FGJAWDWFht1sQ4qxyJHZZbVyvnVcKQN0E3u5/5lRz+g=
github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iwind/TeaGo v0.0.0-20200923021120-f5d76441fe9e/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/TeaGo v0.0.0-20201206115018-cdd967bfb13d h1:J4ohNUwOqCQkY62LFwaygfGtHJj+87pnzr+RJxEYSBo=
github.com/iwind/TeaGo v0.0.0-20201206115018-cdd967bfb13d/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20240402174815-29b9bb013b0f h1:f00RU+zOX+B3rLAmMMkzHUF2h1z4DeYR9tTCvEq2REY=
github.com/google/pprof v0.0.0-20240402174815-29b9bb013b0f/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/iwind/TeaGo v0.0.0-20240312020455-6f20b5121caf h1:WA9qgiynESu/DDTnLH6npRI5AK6UL9qwJ2YZ5qhJX5E=
github.com/iwind/TeaGo v0.0.0-20240312020455-6f20b5121caf/go.mod h1:SfqVbWyIPdVflyA6lMgicZzsoGS8pyeLiTRe8/CIpGI=
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/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=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs=
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/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.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
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/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8=
github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
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=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM=
github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa h1:2cO3RojjYl3hVTbEvJVqrMaFmORhL6O06qdW42toftk=
github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa/go.mod h1:Yjr3bdWaVWyME1kha7X0jsz3k2DgXNa1Pj3XGyUAbx8=
github.com/shirou/gopsutil/v3 v3.22.5 h1:atX36I/IXgFiB81687vSiBI5zrMsxcIBkP9cQMJQoJA=
github.com/shirou/gopsutil/v3 v3.22.5/go.mod h1:so9G9VzeHt/hsd0YwqprnjHnfARAUktauykSbr+y2gA=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/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=
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119 h1:YyPWX3jLOtYKulBR6AScGIs74lLrJcgeKRwcbAuQOG4=
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119/go.mod h1:/nuTSlK+okRfR/vnIPqR89fFKonnWPiZymN5ydRJkX8=
go.opentelemetry.io/otel v0.7.0/go.mod h1:aZMyHG5TqDOXEgH2tyLiXSUKly1jT3yqE9PmrzIeCdo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
github.com/xlzd/gotp v0.1.0 h1:37blvlKCh38s+fkem+fFh7sMnceltoIEBYTVXyoa5Po=
github.com/xlzd/gotp v0.1.0/go.mod h1:ndLJ3JKzi3xLmUProq4LLxCuECL93dG9WASNLpHz8qg=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0=
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
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=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1,30 +1,39 @@
package apps
import (
"errors"
"fmt"
"github.com/iwind/TeaGo/Tea"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"github.com/iwind/gosock/pkg/gosock"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"syscall"
"strings"
"time"
)
// App命令帮助
// AppCmd App命令帮助
type AppCmd struct {
product string
version string
usage string
usages []string
options []*CommandHelpOption
appendStrings []string
directives []*Directive
sock *gosock.Sock
}
func NewAppCmd() *AppCmd {
return &AppCmd{}
return &AppCmd{
sock: gosock.NewTmpSock(teaconst.ProcessName),
}
}
type CommandHelpOption struct {
@@ -32,25 +41,25 @@ type CommandHelpOption struct {
Description string
}
// 产品
// Product 产品
func (this *AppCmd) Product(product string) *AppCmd {
this.product = product
return this
}
// 版本
// Version 版本
func (this *AppCmd) Version(version string) *AppCmd {
this.version = version
return this
}
// 使用方法
// Usage 使用方法
func (this *AppCmd) Usage(usage string) *AppCmd {
this.usage = usage
this.usages = append(this.usages, usage)
return this
}
// 选项
// Option 选项
func (this *AppCmd) Option(code string, description string) *AppCmd {
this.options = append(this.options, &CommandHelpOption{
Code: code,
@@ -59,18 +68,20 @@ func (this *AppCmd) Option(code string, description string) *AppCmd {
return this
}
// 附加内容
// Append 附加内容
func (this *AppCmd) Append(appendString string) *AppCmd {
this.appendStrings = append(this.appendStrings, appendString)
return this
}
// 打印
// Print 打印
func (this *AppCmd) Print() {
fmt.Println(this.product + " v" + this.version)
usage := this.usage
fmt.Println("Usage:", "\n "+usage)
fmt.Println("Usage:")
for _, usage := range this.usages {
fmt.Println(" " + usage)
}
if len(this.options) > 0 {
fmt.Println("")
@@ -104,7 +115,7 @@ func (this *AppCmd) Print() {
}
}
// 添加指令
// On 添加指令
func (this *AppCmd) On(arg string, callback func()) {
this.directives = append(this.directives, &Directive{
Arg: arg,
@@ -112,10 +123,10 @@ func (this *AppCmd) On(arg string, callback func()) {
})
}
// 运行
// Run 运行
func (this *AppCmd) Run(main func()) {
// 获取参数
args := os.Args[1:]
var args = os.Args[1:]
if len(args) > 0 {
switch args[0] {
case "-v", "version", "-version", "--version":
@@ -131,7 +142,7 @@ func (this *AppCmd) Run(main func()) {
this.runStop()
return
case "restart":
this.runRestart()
this.RunRestart()
return
case "status":
this.runStatus()
@@ -151,11 +162,8 @@ func (this *AppCmd) Run(main func()) {
return
}
// 记录PID
_ = this.writePid()
// 日志
writer := new(LogWriter)
var writer = new(LogWriter)
writer.Init()
logs.SetWriter(writer)
@@ -165,7 +173,7 @@ func (this *AppCmd) Run(main func()) {
// 版本号
func (this *AppCmd) runVersion() {
fmt.Println(this.product+" v"+this.version, "(build: "+runtime.Version(), runtime.GOOS, runtime.GOARCH+")")
fmt.Println(this.product+" v"+this.version, "(build: "+runtime.Version(), runtime.GOOS, runtime.GOARCH, teaconst.Tag+")")
}
// 帮助
@@ -175,40 +183,40 @@ func (this *AppCmd) runHelp() {
// 启动
func (this *AppCmd) runStart() {
proc := this.checkPid()
if proc != nil {
fmt.Println(this.product+" already started, pid:", proc.Pid)
var pid = this.getPID()
if pid > 0 {
fmt.Println(this.product+" already started, pid:", pid)
return
}
cmd := exec.Command(os.Args[0])
var cmd = exec.Command(this.exe())
err := cmd.Start()
if err != nil {
fmt.Println(this.product+" start failed:", err.Error())
return
}
// create symbolic links
_ = this.createSymLinks()
fmt.Println(this.product+" started ok, pid:", cmd.Process.Pid)
}
// 停止
func (this *AppCmd) runStop() {
proc := this.checkPid()
if proc == nil {
var pid = this.getPID()
if pid == 0 {
fmt.Println(this.product + " not started yet")
return
}
// 停止进程
_ = proc.Signal(syscall.SIGQUIT)
_, _ = this.sock.Send(&gosock.Command{Code: "stop"})
// 在Windows上经常不能及时释放资源
_ = DeletePid(Tea.Root + "/bin/pid")
fmt.Println(this.product+" stopped ok, pid:", proc.Pid)
fmt.Println(this.product+" stopped ok, pid:", types.String(pid))
}
// 重启
func (this *AppCmd) runRestart() {
// RunRestart 重启
func (this *AppCmd) RunRestart() {
this.runStop()
time.Sleep(1 * time.Second)
this.runStart()
@@ -216,20 +224,79 @@ func (this *AppCmd) runRestart() {
// 状态
func (this *AppCmd) runStatus() {
proc := this.checkPid()
if proc == nil {
var pid = this.getPID()
if pid == 0 {
fmt.Println(this.product + " not started yet")
} else {
fmt.Println(this.product + " is running, pid: " + fmt.Sprintf("%d", proc.Pid))
return
}
fmt.Println(this.product + " is running, pid: " + types.String(pid))
}
// 检查PID
func (this *AppCmd) checkPid() *os.Process {
return CheckPid(Tea.Root + "/bin/pid")
// 获取当前的PID
func (this *AppCmd) getPID() int {
if !this.sock.IsListening() {
return 0
}
reply, err := this.sock.Send(&gosock.Command{Code: "pid"})
if err != nil {
return 0
}
return maps.NewMap(reply.Params).GetInt("pid")
}
// 写入PID
func (this *AppCmd) writePid() error {
return WritePid(Tea.Root + "/bin/pid")
func (this *AppCmd) exe() string {
var exe, _ = os.Executable()
if len(exe) == 0 {
exe = os.Args[0]
}
return exe
}
// 创建软链接
func (this *AppCmd) createSymLinks() error {
if runtime.GOOS != "linux" {
return nil
}
var exe, _ = os.Executable()
if len(exe) == 0 {
return nil
}
var errorList = []string{}
// bin
{
var target = "/usr/bin/" + teaconst.ProcessName
old, _ := filepath.EvalSymlinks(target)
if old != exe {
_ = os.Remove(target)
err := os.Symlink(exe, target)
if err != nil {
errorList = append(errorList, err.Error())
}
}
}
// log
{
var realPath = filepath.Dir(filepath.Dir(exe)) + "/logs/run.log"
var target = "/var/log/" + teaconst.ProcessName + ".log"
old, _ := filepath.EvalSymlinks(target)
if old != realPath {
_ = os.Remove(target)
err := os.Symlink(realPath, target)
if err != nil {
errorList = append(errorList, err.Error())
}
}
}
if len(errorList) > 0 {
return errors.New(strings.Join(errorList, "\n"))
}
return nil
}

View File

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

View File

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

View File

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

View File

@@ -1,113 +0,0 @@
package apps
import (
"fmt"
"github.com/iwind/TeaGo/types"
"io/ioutil"
"os"
"runtime"
)
var pidFileList = []*os.File{}
// 检查Pid
func CheckPid(path string) *os.Process {
// windows上打开的文件是不能删除的
if runtime.GOOS == "windows" {
if os.Remove(path) == nil {
return nil
}
}
file, err := os.Open(path)
if err != nil {
return nil
}
defer func() {
_ = file.Close()
}()
// 是否能取得Lock
err = LockFile(file)
if err == nil {
_ = UnlockFile(file)
return nil
}
pidBytes, err := ioutil.ReadAll(file)
if err != nil {
return nil
}
pid := types.Int(string(pidBytes))
if pid <= 0 {
return nil
}
proc, _ := os.FindProcess(pid)
return proc
}
// 写入Pid
func WritePid(path string) error {
fp, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_RDONLY, 0666)
if err != nil {
return err
}
if runtime.GOOS != "windows" {
err = LockFile(fp)
if err != nil {
return err
}
}
pidFileList = append(pidFileList, fp) // hold the file pointers
_, err = fp.WriteString(fmt.Sprintf("%d", os.Getpid()))
if err != nil {
return err
}
return nil
}
// 写入Ppid
func WritePpid(path string) error {
fp, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_RDONLY, 0666)
if err != nil {
return err
}
if runtime.GOOS != "windows" {
err = LockFile(fp)
if err != nil {
return err
}
}
pidFileList = append(pidFileList, fp) // hold the file pointers
_, err = fp.WriteString(fmt.Sprintf("%d", os.Getppid()))
if err != nil {
return err
}
return nil
}
// 删除Pid
func DeletePid(path string) error {
_, err := os.Stat(path)
if err != nil {
if !os.IsNotExist(err) {
return nil
}
return err
}
for _, fp := range pidFileList {
_ = UnlockFile(fp)
_ = fp.Close()
}
return os.Remove(path)
}

View File

@@ -1,24 +1,32 @@
package configloaders
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type AdminModuleCode = string
const (
AdminModuleCodeServer AdminModuleCode = "server" // 网站
AdminModuleCodeNode AdminModuleCode = "node" // 节点
AdminModuleCodeDNS AdminModuleCode = "dns" // DNS
AdminModuleCodeAdmin AdminModuleCode = "admin" // 系统用户
AdminModuleCodeUser AdminModuleCode = "user" // 平台用户
AdminModuleCodeFinance AdminModuleCode = "finance" // 财务
AdminModuleCodeLog AdminModuleCode = "log" // 日志
AdminModuleCodeSetting AdminModuleCode = "setting" // 设置
AdminModuleCodeCommon AdminModuleCode = "common" // 只要登录就可以访问的模块
AdminModuleCodeDashboard AdminModuleCode = "dashboard" // 看板
AdminModuleCodeServer AdminModuleCode = "server" // 网站
AdminModuleCodeNode AdminModuleCode = "node" // 节点
AdminModuleCodeDNS AdminModuleCode = "dns" // DNS
AdminModuleCodeNS AdminModuleCode = "ns" // 域名服务
AdminModuleCodeAdmin AdminModuleCode = "admin" // 系统用户
AdminModuleCodeUser AdminModuleCode = "user" // 平台用户
AdminModuleCodeFinance AdminModuleCode = "finance" // 财务
AdminModuleCodePlan AdminModuleCode = "plan" // 套餐
AdminModuleCodeLog AdminModuleCode = "log" // 日志
AdminModuleCodeSetting AdminModuleCode = "setting" // 设置
AdminModuleCodeTicket AdminModuleCode = "ticket" // 工单
AdminModuleCodeCommon AdminModuleCode = "common" // 只要登录就可以访问的模块
)
var sharedAdminModuleMapping = map[int64]*AdminModuleList{} // adminId => AdminModuleList
@@ -41,6 +49,8 @@ func loadAdminModuleMapping() (map[int64]*AdminModuleList, error) {
list := &AdminModuleList{
IsSuper: m.IsSuper,
Fullname: m.Fullname,
Theme: m.Theme,
Lang: m.Lang,
}
for _, pbModule := range m.Modules {
@@ -67,7 +77,7 @@ func NotifyAdminModuleMappingChange() error {
return err
}
// 检查用户是否存在
// CheckAdmin 检查用户是否存在
func CheckAdmin(adminId int64) bool {
locker.Lock()
defer locker.Unlock()
@@ -81,7 +91,7 @@ func CheckAdmin(adminId int64) bool {
return ok
}
// 检查模块是否允许访问
// AllowModule 检查模块是否允许访问
func AllowModule(adminId int64, module string) bool {
locker.Lock()
defer locker.Unlock()
@@ -102,14 +112,14 @@ func AllowModule(adminId int64, module string) bool {
return false
}
// 获取管理员第一个可访问模块
// FindFirstAdminModule 获取管理员第一个可访问模块
func FindFirstAdminModule(adminId int64) (module AdminModuleCode, ok bool) {
locker.Lock()
defer locker.Unlock()
list, ok2 := sharedAdminModuleMapping[adminId]
if ok2 {
if list.IsSuper {
return AdminModuleCodeServer, true
return AdminModuleCodeDashboard, true
} else if len(list.Modules) > 0 {
return list.Modules[0].Code, true
}
@@ -117,7 +127,7 @@ func FindFirstAdminModule(adminId int64) (module AdminModuleCode, ok bool) {
return
}
// 查找某个管理员名称
// FindAdminFullname 查找某个管理员名称
func FindAdminFullname(adminId int64) string {
locker.Lock()
defer locker.Unlock()
@@ -129,48 +139,145 @@ func FindAdminFullname(adminId int64) string {
return ""
}
// 所有权限列表
func AllModuleMaps() []maps.Map {
return []maps.Map{
// FindAdminTheme 查找某个管理员选择的风格
func FindAdminTheme(adminId int64) string {
locker.Lock()
defer locker.Unlock()
list, ok := sharedAdminModuleMapping[adminId]
if ok {
return list.Theme
}
return ""
}
// UpdateAdminTheme 设置某个管理员的风格
func UpdateAdminTheme(adminId int64, theme string) {
locker.Lock()
defer locker.Unlock()
list, ok := sharedAdminModuleMapping[adminId]
if ok {
list.Theme = theme
}
}
// FindAdminLang 查找某个管理员选择的语言
func FindAdminLang(adminId int64) string {
locker.Lock()
defer locker.Unlock()
list, ok := sharedAdminModuleMapping[adminId]
if ok {
return list.Lang
}
return ""
}
// UpdateAdminLang 修改某个管理员选择的语言
func UpdateAdminLang(adminId int64, langCode string) {
locker.Lock()
defer locker.Unlock()
list, ok := sharedAdminModuleMapping[adminId]
if ok {
list.Lang = langCode
}
}
func FindAdminLangForAction(actionPtr actions.ActionWrapper) (langCode langs.LangCode) {
locker.Lock()
defer locker.Unlock()
var adminId = actionPtr.Object().Session().GetInt64(teaconst.SessionAdminId)
list, ok := sharedAdminModuleMapping[adminId]
var result = ""
if ok {
result = list.Lang
}
if len(result) == 0 {
result = langs.ParseLangFromAction(actionPtr)
}
return result
}
// AllModuleMaps 所有权限列表
func AllModuleMaps(langCode string) []maps.Map {
var m = []maps.Map{
{
"name": "网站服务",
"name": langs.Message(langCode, codes.AdminMenu_Dashboard),
"code": AdminModuleCodeDashboard,
"url": "/dashboard",
},
{
"name": langs.Message(langCode, codes.AdminMenu_Servers),
"code": AdminModuleCodeServer,
"url": "/servers",
},
{
"name": "边缘节点",
"name": langs.Message(langCode, codes.AdminMenu_Nodes),
"code": AdminModuleCodeNode,
"url": "/clusters",
},
{
"name": "域名解析",
"name": langs.Message(langCode, codes.AdminMenu_DNS),
"code": AdminModuleCodeDNS,
"url": "/dns",
},
}
if teaconst.IsPlus {
m = append(m, maps.Map{
"name": langs.Message(langCode, codes.AdminMenu_NS),
"code": AdminModuleCodeNS,
"url": "/ns",
})
}
m = append(m, []maps.Map{
{
"name": "平台用户",
"name": langs.Message(langCode, codes.AdminMenu_Users),
"code": AdminModuleCodeUser,
"url": "/users",
},
{
"name": "系统用户",
"name": langs.Message(langCode, codes.AdminMenu_Admins),
"code": AdminModuleCodeAdmin,
"url": "/admins",
},
{
"name": "财务管理",
"name": langs.Message(langCode, codes.AdminMenu_Finance),
"code": AdminModuleCodeFinance,
"url": "/finance",
},
}...)
if teaconst.IsPlus {
m = append(m, []maps.Map{
{
"name": langs.Message(langCode, codes.AdminMenu_Plans),
"code": AdminModuleCodePlan,
"url": "/plans",
},
{
"name": langs.Message(langCode, codes.AdminMenu_Tickets),
"code": AdminModuleCodeTicket,
"url": "/tickets",
},
}...)
}
m = append(m, []maps.Map{
{
"name": "日志审计",
"name": langs.Message(langCode, codes.AdminMenu_Logs),
"code": AdminModuleCodeLog,
"url": "/log",
},
{
"name": "系统设置",
"name": langs.Message(langCode, codes.AdminMenu_Settings),
"code": AdminModuleCodeSetting,
"url": "/settings",
},
}
}...)
return m
}

View File

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

View File

@@ -6,7 +6,7 @@ import (
)
func TestLoadAdminModuleMapping(t *testing.T) {
m, err := LoadAdminModuleMapping()
m, err := loadAdminModuleMapping()
if err != nil {
t.Fatal(err)
}

View File

@@ -3,18 +3,18 @@ package configloaders
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/iwind/TeaGo/logs"
"reflect"
"time"
)
var sharedAdminUIConfig *systemconfigs.AdminUIConfig = nil
const (
AdminUISettingName = "adminUIConfig"
)
func LoadAdminUIConfig() (*systemconfigs.AdminUIConfig, error) {
locker.Lock()
defer locker.Unlock()
@@ -28,6 +28,15 @@ func LoadAdminUIConfig() (*systemconfigs.AdminUIConfig, error) {
return &v, nil
}
func ReloadAdminUIConfig() error {
locker.Lock()
defer locker.Unlock()
sharedAdminUIConfig = nil
_, err := loadAdminUIConfig()
return err
}
func UpdateAdminUIConfig(uiConfig *systemconfigs.AdminUIConfig) error {
locker.Lock()
defer locker.Unlock()
@@ -41,7 +50,7 @@ func UpdateAdminUIConfig(uiConfig *systemconfigs.AdminUIConfig) error {
return err
}
_, err = rpcClient.SysSettingRPC().UpdateSysSetting(rpcClient.Context(0), &pb.UpdateSysSettingRequest{
Code: AdminUISettingName,
Code: systemconfigs.SettingCodeAdminUIConfig,
ValueJSON: valueJSON,
})
if err != nil {
@@ -49,10 +58,13 @@ func UpdateAdminUIConfig(uiConfig *systemconfigs.AdminUIConfig) error {
}
sharedAdminUIConfig = uiConfig
// timezone
updateTimeZone(uiConfig)
return nil
}
// 是否显示财务信息
// ShowFinance 是否显示财务信息
func ShowFinance() bool {
config, _ := LoadAdminUIConfig()
if config != nil && !config.ShowFinance {
@@ -70,7 +82,7 @@ func loadAdminUIConfig() (*systemconfigs.AdminUIConfig, error) {
return nil, err
}
resp, err := rpcClient.SysSettingRPC().ReadSysSetting(rpcClient.Context(0), &pb.ReadSysSettingRequest{
Code: AdminUISettingName,
Code: systemconfigs.SettingCodeAdminUIConfig,
})
if err != nil {
return nil, err
@@ -80,23 +92,39 @@ func loadAdminUIConfig() (*systemconfigs.AdminUIConfig, error) {
return sharedAdminUIConfig, nil
}
config := &systemconfigs.AdminUIConfig{}
var config = &systemconfigs.AdminUIConfig{}
err = json.Unmarshal(resp.ValueJSON, config)
if err != nil {
logs.Println("[UI_MANAGER]" + err.Error())
sharedAdminUIConfig = defaultAdminUIConfig()
return sharedAdminUIConfig, nil
}
// timezone
updateTimeZone(config)
sharedAdminUIConfig = config
return sharedAdminUIConfig, nil
}
func defaultAdminUIConfig() *systemconfigs.AdminUIConfig {
return &systemconfigs.AdminUIConfig{
ProductName: "GoEdge",
AdminSystemName: "GoEdge管理员系统",
ProductName: langs.DefaultMessage(codes.AdminUI_DefaultProductName),
AdminSystemName: langs.DefaultMessage(codes.AdminUI_DefaultSystemName),
ShowOpenSourceInfo: true,
ShowVersion: true,
ShowFinance: true,
DefaultPageSize: 10,
TimeZone: nodeconfigs.DefaultTimeZoneLocation,
}
}
// 修改时区
func updateTimeZone(config *systemconfigs.AdminUIConfig) {
if len(config.TimeZone) > 0 {
location, err := time.LoadLocation(config.TimeZone)
if err == nil && time.Local != location {
time.Local = location
}
}
}

View File

@@ -9,7 +9,7 @@ import (
func TestLoadUIConfig(t *testing.T) {
for i := 0; i < 10; i++ {
before := time.Now()
config, err := LoadUIConfig()
config, err := LoadAdminUIConfig()
if err != nil {
t.Fatal(err)
}
@@ -20,7 +20,7 @@ func TestLoadUIConfig(t *testing.T) {
func TestLoadUIConfig2(t *testing.T) {
for i := 0; i < 10; i++ {
config, err := LoadUIConfig()
config, err := LoadAdminUIConfig()
if err != nil {
t.Fatal(err)
}

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
}
@@ -52,6 +52,10 @@ func UpdateSecurityConfig(securityConfig *systemconfigs.SecurityConfig) error {
if err != nil {
return err
}
err = securityConfig.Init()
if err != nil {
return err
}
sharedSecurityConfig = securityConfig
// 通知更新
@@ -75,24 +79,40 @@ func loadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
return nil, err
}
if len(resp.ValueJSON) == 0 {
sharedSecurityConfig = defaultSecurityConfig()
sharedSecurityConfig = NewSecurityConfig()
return sharedSecurityConfig, nil
}
config := &systemconfigs.SecurityConfig{}
var config = &systemconfigs.SecurityConfig{
Frame: FrameSameOrigin,
AllowLocal: true,
CheckClientFingerprint: false,
CheckClientRegion: true,
DenySearchEngines: true,
DenySpiders: true,
}
err = json.Unmarshal(resp.ValueJSON, config)
if err != nil {
logs.Println("[SECURITY_MANAGER]" + err.Error())
sharedSecurityConfig = defaultSecurityConfig()
sharedSecurityConfig = NewSecurityConfig()
return sharedSecurityConfig, nil
}
err = config.Init()
if err != nil {
return nil, err
}
sharedSecurityConfig = config
return sharedSecurityConfig, nil
}
func defaultSecurityConfig() *systemconfigs.SecurityConfig {
// NewSecurityConfig create new security config
func NewSecurityConfig() *systemconfigs.SecurityConfig {
return &systemconfigs.SecurityConfig{
Frame: FrameSameOrigin,
AllowLocal: true,
Frame: FrameSameOrigin,
AllowLocal: true,
CheckClientFingerprint: false,
CheckClientRegion: true,
DenySearchEngines: true,
DenySpiders: true,
}
}

View File

@@ -1,93 +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,
}
}

View File

@@ -1,42 +1,57 @@
package configs
import (
"errors"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/go-yaml/yaml"
"github.com/iwind/TeaGo/Tea"
"io/ioutil"
"gopkg.in/yaml.v3"
"os"
"path/filepath"
)
// API配置
const ConfigFileName = "api_admin.yaml"
const oldConfigFileName = "api.yaml"
// APIConfig API配置
type APIConfig struct {
RPC struct {
Endpoints []string `yaml:"endpoints"`
} `yaml:"rpc"`
OldRPC struct {
Endpoints []string `yaml:"endpoints"`
DisableUpdate bool `yaml:"disableUpdate"`
} `yaml:"rpc,omitempty"`
RPCEndpoints []string `yaml:"rpc.endpoints,flow" json:"rpc.endpoints"`
RPCDisableUpdate bool `yaml:"rpc.disableUpdate" json:"rpc.disableUpdate"`
NodeId string `yaml:"nodeId"`
Secret string `yaml:"secret"`
}
// 加载API配置
// LoadAPIConfig 加载API配置
func LoadAPIConfig() (*APIConfig, error) {
// 候选文件
localFile := Tea.ConfigFile("api.yaml")
isFromLocal := false
paths := []string{localFile}
homeDir, err := os.UserHomeDir()
if err == nil {
paths = append(paths, homeDir+"/."+teaconst.ProcessName+"/api.yaml")
var realFile = Tea.ConfigFile(ConfigFileName)
var oldRealFile = Tea.ConfigFile(oldConfigFileName)
var isFromLocal = false
var paths = []string{realFile, oldRealFile}
homeDir, homeErr := os.UserHomeDir()
if homeErr == nil {
paths = append(paths, homeDir+"/."+teaconst.ProcessName+"/"+ConfigFileName)
}
paths = append(paths, "/etc/"+teaconst.ProcessName+"/api.yaml")
paths = append(paths, "/etc/"+teaconst.ProcessName+"/"+ConfigFileName)
var data []byte
var err error
var isFromOld = false
for _, path := range paths {
data, err = ioutil.ReadFile(path)
data, err = os.ReadFile(path)
if err == nil {
if path == localFile {
if path == realFile || path == oldRealFile {
isFromLocal = true
}
// 自动生成新的配置文件
isFromOld = path == oldRealFile
break
}
}
@@ -44,58 +59,168 @@ func LoadAPIConfig() (*APIConfig, error) {
return nil, err
}
config := &APIConfig{}
var config = &APIConfig{}
err = yaml.Unmarshal(data, config)
if err != nil {
return nil, err
}
err = config.Init()
if err != nil {
return nil, errors.New("init error: " + err.Error())
}
if !isFromLocal {
// 恢复文件
_ = ioutil.WriteFile(localFile, data, 0666)
_ = os.WriteFile(realFile, data, 0666)
}
// 自动生成新配置文件
if isFromOld {
config.OldRPC.Endpoints = nil
_ = config.WriteFile(Tea.ConfigFile(ConfigFileName))
}
return config, nil
}
// 写入API配置
// ResetAPIConfig 重置配置
func ResetAPIConfig() error {
var filename = ConfigFileName
// 重置 configs/api_admin.yaml
{
var configFile = Tea.ConfigFile(filename)
stat, err := os.Stat(configFile)
if err == nil && !stat.IsDir() {
err = os.Remove(configFile)
if err != nil {
return err
}
}
}
// 重置 ~/.edge-admin/api_admin.yaml
homeDir, homeErr := os.UserHomeDir()
if homeErr == nil {
var configFile = homeDir + "/." + teaconst.ProcessName + "/" + filename
stat, err := os.Stat(configFile)
if err == nil && !stat.IsDir() {
err = os.Remove(configFile)
if err != nil {
return err
}
}
}
// 重置 /etc/edge-admin/api_admin.yaml
{
var configFile = "/etc/" + teaconst.ProcessName + "/" + filename
stat, err := os.Stat(configFile)
if err == nil && !stat.IsDir() {
err = os.Remove(configFile)
if err != nil {
return err
}
}
}
return nil
}
func IsNewInstalled() bool {
homeDir, err := os.UserHomeDir()
if err != nil {
return false
}
for _, filename := range []string{ConfigFileName, oldConfigFileName} {
_, err = os.Stat(homeDir + "/." + teaconst.ProcessName + "/" + filename)
if err == nil {
return false
}
}
return true
}
// WriteFile 写入API配置
func (this *APIConfig) WriteFile(path string) error {
data, err := yaml.Marshal(this)
if err != nil {
return err
}
err = ioutil.WriteFile(path, data, 0666)
err = os.WriteFile(path, data, 0666)
if err != nil {
return err
}
// 写入 ~/ 和 /etc/ 目录,因为是备份需要,所以不需要提示错误信息
// 写入 ~/.edge-admin/
filename := filepath.Base(path)
homeDir, err := os.UserHomeDir()
if err == nil {
// 这个用来判断用户是否为重装,所以比较重要
var filename = filepath.Base(path)
homeDir, homeErr := os.UserHomeDir()
if homeErr == nil {
dir := homeDir + "/." + teaconst.ProcessName
stat, err := os.Stat(dir)
if err == nil && stat.IsDir() {
_ = ioutil.WriteFile(dir+"/"+filename, data, 0666)
err = os.WriteFile(dir+"/"+filename, data, 0666)
if err != nil {
return err
}
} else if err != nil && os.IsNotExist(err) {
err = os.Mkdir(dir, 0777)
if err == nil {
_ = ioutil.WriteFile(dir+"/"+filename, data, 0666)
err = os.WriteFile(dir+"/"+filename, data, 0666)
if err != nil {
return err
}
}
}
}
// 写入 /etc/.edge-admin
// 写入 /etc/edge-admin
{
dir := "/etc/" + teaconst.ProcessName
var dir = "/etc/" + teaconst.ProcessName
stat, err := os.Stat(dir)
if err == nil && stat.IsDir() {
_ = ioutil.WriteFile(dir+"/"+filename, data, 0666)
_ = os.WriteFile(dir+"/"+filename, data, 0666)
} else if err != nil && os.IsNotExist(err) {
err = os.Mkdir(dir, 0777)
if err == nil {
_ = ioutil.WriteFile(dir+"/"+filename, data, 0666)
_ = os.WriteFile(dir+"/"+filename, data, 0666)
}
}
}
return err
return nil
}
// Clone 克隆当前配置
func (this *APIConfig) Clone() *APIConfig {
return &APIConfig{
NodeId: this.NodeId,
Secret: this.Secret,
}
}
func (this *APIConfig) Init() error {
// compatible with old
if len(this.RPCEndpoints) == 0 && len(this.OldRPC.Endpoints) > 0 {
this.RPCEndpoints = this.OldRPC.Endpoints
this.RPCDisableUpdate = this.OldRPC.DisableUpdate
}
if len(this.RPCEndpoints) == 0 {
return errors.New("no valid 'rpc.endpoints'")
}
if len(this.NodeId) == 0 {
return errors.New("'nodeId' required")
}
if len(this.Secret) == 0 {
return errors.New("'secret' required")
}
return nil
}

View File

@@ -2,6 +2,7 @@ package configs
import (
_ "github.com/iwind/TeaGo/bootstrap"
"gopkg.in/yaml.v3"
"testing"
)
@@ -11,16 +12,16 @@ func TestLoadAPIConfig(t *testing.T) {
t.Fatal(err)
}
t.Log(config)
configData, err := yaml.Marshal(config)
if err != nil {
t.Fatal(err)
}
t.Log(string(configData))
}
func TestAPIConfig_WriteFile(t *testing.T) {
config := &APIConfig{
RPC: struct {
Endpoints []string `yaml:"endpoints"`
}{},
NodeId: "1",
Secret: "2",
}
var config = &APIConfig{}
err := config.WriteFile("/tmp/api_config.yaml")
if err != nil {
t.Fatal(err)

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

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

View File

@@ -1,7 +1,9 @@
package teaconst
const (
Version = "0.0.8"
Version = "1.3.6"
APINodeVersion = "1.3.6"
ProductName = "Edge Admin"
ProcessName = "edge-admin"
@@ -12,8 +14,9 @@ const (
EncryptKey = "8f983f4d69b83aaa0d74b21a212f6967"
EncryptMethod = "aes-256-cfb"
ErrServer = "服务器出了点小问题,请联系技术人员处理。"
CookieSID = "edgesid"
CookieSID = "geadsid"
SessionAdminId = "adminId"
SystemdServiceName = "edge-admin"
UpdatesURL = "https://goedge.cn/api/boot/versions?os=${os}&arch=${arch}&version=${version}"
)

View File

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

View File

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

6
internal/const/plus.go Normal file
View File

@@ -0,0 +1,6 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build !plus
package teaconst
var IsPlus = false

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

@@ -0,0 +1,32 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package teaconst
import (
"os"
"strings"
)
var (
IsRecoverMode = false
IsDemoMode = false
ErrorDemoOperation = "DEMO模式下无法进行创建、修改、删除等操作"
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

@@ -10,7 +10,7 @@ import (
"time"
)
// 生成Token
// Generate 生成Token
func Generate() string {
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
@@ -23,7 +23,7 @@ func Generate() string {
return token
}
// 校验Token
// Validate 校验Token
func Validate(token string) (b bool) {
if len(token) == 0 {
return

View File

@@ -19,7 +19,6 @@ func TestAES128CFBMethod_Encrypt(t *testing.T) {
dst = dst[:len(src)]
t.Log("dst:", string(dst))
src = make([]byte, len(src))
src, err = method.Decrypt(dst)
if err != nil {
t.Fatal(err)
@@ -64,7 +63,6 @@ func TestAES128CFBMethod_Encrypt2(t *testing.T) {
for _, dst := range sources {
dst2 := append([]byte{}, dst...)
src2 := make([]byte, len(dst2))
src2, err := method.Decrypt(dst2)
if err != nil {
t.Fatal(err)

View File

@@ -5,22 +5,22 @@ import "sync"
var eventsMap = map[string][]func(){} // event => []callbacks
var locker = sync.Mutex{}
// 增加事件回调
// On 增加事件回调
func On(event string, callback func()) {
locker.Lock()
defer locker.Unlock()
callbacks, _ := eventsMap[event]
var callbacks = eventsMap[event]
callbacks = append(callbacks, callback)
eventsMap[event] = callbacks
}
// 通知事件
// Notify 通知事件
func Notify(event string) {
locker.Lock()
callbacks, _ := eventsMap[event]
var callbacks = eventsMap[event]
locker.Unlock()
for _, callback := range callbacks {
callback()
}

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

@@ -0,0 +1,169 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package gen
import (
"bytes"
"encoding/json"
"fmt"
"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"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
"io"
"os"
"path/filepath"
)
func Generate() error {
err := generateComponentsJSFile()
if err != nil {
return fmt.Errorf("generate 'components.src.js' failed: %w", err)
}
return nil
}
// 生成Javascript文件
func generateComponentsJSFile() error {
var buffer = bytes.NewBuffer([]byte{})
var webRoot string
if Tea.IsTesting() {
webRoot = Tea.Root + "/../web/public/js/components/"
} else {
webRoot = Tea.Root + "/web/public/js/components/"
}
f := files.NewFile(webRoot)
f.Range(func(file *files.File) {
if !file.IsFile() {
return
}
if file.Ext() != ".js" {
return
}
data, err := file.ReadAll()
if err != nil {
logs.Error(err)
return
}
buffer.Write(data)
buffer.Write([]byte{'\n', '\n'})
})
// 条件组件
typesJSON, err := json.Marshal(condutils.ReadAllAvailableCondTypes())
if err != nil {
logs.Println("ComponentsAction marshal request cond types failed: " + err.Error())
} else {
buffer.WriteString("window.REQUEST_COND_COMPONENTS = ")
buffer.Write(typesJSON)
buffer.Write([]byte{';', '\n', '\n'})
}
// 条件操作符
requestOperatorsJSON, err := json.Marshal(shared.AllRequestOperators())
if err != nil {
logs.Println("ComponentsAction marshal request operators failed: " + err.Error())
} else {
buffer.WriteString("window.REQUEST_COND_OPERATORS = ")
buffer.Write(requestOperatorsJSON)
buffer.Write([]byte{';', '\n', '\n'})
}
// 请求变量
requestVariablesJSON, err := json.Marshal(shared.DefaultRequestVariables())
if err != nil {
logs.Println("ComponentsAction marshal request variables failed: " + err.Error())
} else {
buffer.WriteString("window.REQUEST_VARIABLES = ")
buffer.Write(requestVariablesJSON)
buffer.Write([]byte{';', '\n', '\n'})
}
// 指标
metricHTTPKeysJSON, err := json.Marshal(serverconfigs.FindAllMetricKeyDefinitions(serverconfigs.MetricItemCategoryHTTP))
if err != nil {
logs.Println("ComponentsAction marshal metric http keys failed: " + err.Error())
} else {
buffer.WriteString("window.METRIC_HTTP_KEYS = ")
buffer.Write(metricHTTPKeysJSON)
buffer.Write([]byte{';', '\n', '\n'})
}
// IP地址阈值项目
ipAddrThresholdItemsJSON, err := json.Marshal(nodeconfigs.FindAllIPAddressThresholdItems())
if err != nil {
logs.Println("ComponentsAction marshal ip addr threshold items failed: " + err.Error())
} else {
buffer.WriteString("window.IP_ADDR_THRESHOLD_ITEMS = ")
buffer.Write(ipAddrThresholdItemsJSON)
buffer.Write([]byte{';', '\n', '\n'})
}
// IP地址阈值动作
ipAddrThresholdActionsJSON, err := json.Marshal(nodeconfigs.FindAllIPAddressThresholdActions())
if err != nil {
logs.Println("ComponentsAction marshal ip addr threshold actions failed: " + err.Error())
} else {
buffer.WriteString("window.IP_ADDR_THRESHOLD_ACTIONS = ")
buffer.Write(ipAddrThresholdActionsJSON)
buffer.Write([]byte{';', '\n', '\n'})
}
// WAF checkpoints
var wafCheckpointsMaps = []maps.Map{}
for _, checkpoint := range firewallconfigs.AllCheckpoints {
wafCheckpointsMaps = append(wafCheckpointsMaps, maps.Map{
"name": checkpoint.Name,
"prefix": checkpoint.Prefix,
"description": checkpoint.Description,
})
}
wafCheckpointsJSON, err := json.Marshal(wafCheckpointsMaps)
if err != nil {
logs.Println("ComponentsAction marshal waf rule checkpoints failed: " + err.Error())
} else {
buffer.WriteString("window.WAF_RULE_CHECKPOINTS = ")
buffer.Write(wafCheckpointsJSON)
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'})
}
// WAF验证码类型
captchaTypesJSON, err := json.Marshal(firewallconfigs.FindAllCaptchaTypes())
if err != nil {
logs.Println("ComponentsAction marshal captcha types failed: " + err.Error())
} else {
buffer.WriteString("window.WAF_CAPTCHA_TYPES = ")
buffer.Write(captchaTypesJSON)
buffer.Write([]byte{';', '\n', '\n'})
}
fp, err := os.OpenFile(filepath.Clean(Tea.PublicFile("/js/components.src.js")), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0777)
if err != nil {
return err
}
_, err = io.Copy(fp, buffer)
if err != nil {
return err
}
return nil
}

View File

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

View File

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

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

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

View File

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

View File

@@ -1,23 +1,29 @@
package nodes
import (
"errors"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/errors"
"github.com/TeaOSLab/EdgeAdmin/internal/events"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/iwind/TeaGo"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/sessions"
"io/ioutil"
"github.com/iwind/TeaGo/types"
"github.com/iwind/gosock/pkg/gosock"
"gopkg.in/yaml.v3"
"log"
"net"
"os"
"os/exec"
"os/signal"
"path/filepath"
"strings"
"syscall"
"time"
)
@@ -25,6 +31,7 @@ import (
var SharedAdminNode *AdminNode = nil
type AdminNode struct {
sock *gosock.Sock
subPIDs []int
}
@@ -36,13 +43,13 @@ func (this *AdminNode) Run() {
SharedAdminNode = this
// 启动管理界面
secret := this.genSecret()
var secret = this.genSecret()
configs.Secret = secret
// 本地Sock
err := this.listenSock()
if err != nil {
logs.Println("NODE" + err.Error())
logs.Println("[NODE]", err.Error())
return
}
@@ -56,8 +63,11 @@ func (this *AdminNode) Run() {
return
}
// 添加端口到防火墙
this.addPortsToFirewall()
// 监听信号
sigQueue := make(chan os.Signal)
var sigQueue = make(chan os.Signal, 8)
signal.Notify(sigQueue, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL, syscall.SIGQUIT)
go func() {
for range sigQueue {
@@ -77,23 +87,30 @@ 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(600 * time.Second).
ReadTimeout(1200 * time.Second).
Start()
}
// 实现守护进程
// Daemon 实现守护进程
func (this *AdminNode) Daemon() {
path := os.TempDir() + "/edge-admin.sock"
isDebug := lists.ContainsString(os.Args, "debug")
isDebug = true
var sock = gosock.NewTmpSock(teaconst.ProcessName)
var isDebug = lists.ContainsString(os.Args, "debug")
for {
conn, err := net.DialTimeout("unix", path, 1*time.Second)
conn, err := sock.Dial()
if err != nil {
if isDebug {
log.Println("[DAEMON]starting ...")
@@ -132,7 +149,7 @@ func (this *AdminNode) Daemon() {
}
}
// 安装系统服务
// InstallSystemService 安装系统服务
func (this *AdminNode) InstallSystemService() error {
shortName := teaconst.SystemdServiceName
@@ -149,7 +166,7 @@ func (this *AdminNode) InstallSystemService() error {
return nil
}
// 添加子PID
// AddSubPID 添加子PID
func (this *AdminNode) AddSubPID(pid int) {
this.subPIDs = append(this.subPIDs, pid)
}
@@ -165,11 +182,11 @@ func (this *AdminNode) checkServer() error {
if os.IsNotExist(err) {
// 创建文件
templateFile := Tea.ConfigFile("server.template.yaml")
data, err := ioutil.ReadFile(templateFile)
data, err := os.ReadFile(templateFile)
if err == nil {
err = ioutil.WriteFile(configFile, data, 0666)
err = os.WriteFile(configFile, data, 0666)
if err != nil {
return errors.New("create config file failed: " + err.Error())
return fmt.Errorf("create config file failed: %w", err)
}
} else {
templateYAML := `# environment code
@@ -187,21 +204,59 @@ https:
cert: ""
key: ""
`
err = ioutil.WriteFile(configFile, []byte(templateYAML), 0666)
err = os.WriteFile(configFile, []byte(templateYAML), 0666)
if err != nil {
return errors.New("create config file failed: " + err.Error())
return fmt.Errorf("create config file failed: %w", err)
}
}
} else {
return errors.New("can not read config from 'configs/server.yaml': " + err.Error())
return fmt.Errorf("can not read config from 'configs/server.yaml': %w", err)
}
return nil
}
// 添加端口到防火墙
func (this *AdminNode) addPortsToFirewall() {
var configFile = Tea.ConfigFile("server.yaml")
data, err := os.ReadFile(configFile)
if err != nil {
return
}
var config = &TeaGo.ServerConfig{}
err = yaml.Unmarshal(data, config)
if err != nil {
return
}
var ports = []int{}
if config.Http.On {
for _, listen := range config.Http.Listen {
_, portString, _ := net.SplitHostPort(listen)
var port = types.Int(portString)
if port > 0 && !lists.ContainsInt(ports, port) {
ports = append(ports, port)
}
}
}
if config.Https.On {
for _, listen := range config.Https.Listen {
_, portString, _ := net.SplitHostPort(listen)
var port = types.Int(portString)
if port > 0 && !lists.ContainsInt(ports, port) {
ports = append(ports, port)
}
}
}
utils.AddPortsToFirewall(ports)
}
// 启动API节点
func (this *AdminNode) startAPINode() {
configPath := Tea.Root + "/edge-api/configs/api.yaml"
var configPath = Tea.Root + "/edge-api/configs/api.yaml"
_, err := os.Stat(configPath)
canStart := false
if err == nil {
@@ -217,9 +272,9 @@ func (this *AdminNode) startAPINode() {
for _, path := range paths {
_, err = os.Stat(path)
if err == nil {
data, err := ioutil.ReadFile(path)
data, err := os.ReadFile(path)
if err == nil {
err = ioutil.WriteFile(configPath, data, 0666)
err = os.WriteFile(configPath, data, 0666)
if err == nil {
logs.Println("[NODE]recover 'edge-api/configs/api.yaml' from '" + path + "'")
canStart = true
@@ -243,9 +298,9 @@ func (this *AdminNode) startAPINode() {
for _, path := range paths {
_, err = os.Stat(path)
if err == nil {
data, err := ioutil.ReadFile(path)
data, err := os.ReadFile(path)
if err == nil {
err = ioutil.WriteFile(dbPath, data, 0666)
err = os.WriteFile(dbPath, data, 0666)
if err == nil {
logs.Println("[NODE]recover 'edge-api/configs/db.yaml' from '" + path + "'")
break
@@ -270,48 +325,105 @@ func (this *AdminNode) startAPINode() {
// 生成Secret
func (this *AdminNode) genSecret() string {
tmpFile := os.TempDir() + "/edge-admin-secret.tmp"
data, err := ioutil.ReadFile(tmpFile)
data, err := os.ReadFile(tmpFile)
if err == nil && len(data) == 32 {
return string(data)
}
secret := rands.String(32)
_ = ioutil.WriteFile(tmpFile, []byte(secret), 0666)
_ = os.WriteFile(tmpFile, []byte(secret), 0666)
return secret
}
// 监听本地sock
func (this *AdminNode) listenSock() error {
path := os.TempDir() + "/edge-admin.sock"
this.sock = gosock.NewTmpSock(teaconst.ProcessName)
// 检查是否已经存
_, err := os.Stat(path)
if err == nil {
conn, err := net.Dial("unix", path)
if err != nil {
_ = os.Remove(path)
// 检查是否在运行
if this.sock.IsListening() {
reply, err := this.sock.Send(&gosock.Command{Code: "pid"})
if err == nil {
return errors.New("error: the process is already running, pid: " + maps.NewMap(reply.Params).GetString("pid"))
} else {
_ = conn.Close()
return errors.New("error: the process is already running")
}
}
// 新的监听任务
listener, err := net.Listen("unix", path)
if err != nil {
return err
}
events.On(events.EventQuit, func() {
logs.Println("NODE", "quit unix sock")
_ = listener.Close()
})
// 启动监听
go func() {
for {
_, err := listener.Accept()
if err != nil {
return
this.sock.OnCommand(func(cmd *gosock.Command) {
switch cmd.Code {
case "pid":
_ = cmd.Reply(&gosock.Command{
Code: "pid",
Params: map[string]interface{}{
"pid": os.Getpid(),
},
})
case "stop":
_ = cmd.ReplyOk()
// 关闭子进程
for _, pid := range this.subPIDs {
p, err := os.FindProcess(pid)
if err == nil && p != nil {
_ = p.Kill()
}
}
// 停止当前目录下的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)
case "recover":
teaconst.IsRecoverMode = true
_ = cmd.ReplyOk()
case "demo":
teaconst.IsDemoMode = !teaconst.IsDemoMode
_ = cmd.Reply(&gosock.Command{
Params: map[string]interface{}{"isDemo": teaconst.IsDemoMode},
})
case "info":
exePath, _ := os.Executable()
_ = cmd.Reply(&gosock.Command{
Code: "info",
Params: map[string]interface{}{
"pid": os.Getpid(),
"version": teaconst.Version,
"path": exePath,
},
})
case "dev": // 切换到dev
Tea.Env = Tea.EnvDev
_ = cmd.ReplyOk()
case "prod": // 切换到prod
Tea.Env = Tea.EnvProd
_ = cmd.ReplyOk()
case "security.reset":
var newConfig = configloaders.NewSecurityConfig()
_ = configloaders.UpdateSecurityConfig(newConfig)
_ = cmd.ReplyOk()
}
})
err := this.sock.Listen()
if err != nil {
logs.Println("NODE", err.Error())
}
}()
events.On(events.EventQuit, func() {
logs.Println("NODE", "quit unix sock")
_ = this.sock.Close()
})
return nil
}

View File

@@ -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,118 @@
// 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/EdgeAdmin/internal/ttlcache"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/logs"
"strings"
"time"
)
// 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{}
var cacheKey = "SESSION@" + sid
var item = ttlcache.DefaultCache.Read(cacheKey)
if item != nil && item.Value != nil {
itemMap, ok := item.Value.(map[string]string)
if ok {
return itemMap
}
}
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())
}
// Write to cache
ttlcache.DefaultCache.Write(cacheKey, result, time.Now().Unix()+300 /** must not be too long **/)
return result
}
func (this *SessionManager) WriteItem(sid string, key string, value string) bool {
// 删除缓存
defer ttlcache.DefaultCache.Delete( "SESSION@" + sid)
// 忽略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 {
// 删除缓存
defer ttlcache.DefaultCache.Delete( "SESSION@" + sid)
// 忽略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

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

View File

@@ -5,38 +5,45 @@ import (
"crypto/tls"
"encoding/base64"
"errors"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/encrypt"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
"google.golang.org/grpc"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/encoding/gzip"
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/metadata"
"net"
"net/url"
"strings"
"sync"
"time"
)
// RPC客户端
// RPCClient RPC客户端
type RPCClient struct {
apiConfig *configs.APIConfig
conns []*grpc.ClientConn
locker sync.Mutex
locker sync.RWMutex
}
// 构造新的RPC客户端
func NewRPCClient(apiConfig *configs.APIConfig) (*RPCClient, error) {
// NewRPCClient 构造新的RPC客户端
func NewRPCClient(apiConfig *configs.APIConfig, isPrimary bool) (*RPCClient, error) {
if apiConfig == nil {
return nil, errors.New("api config should not be nil")
}
client := &RPCClient{
var client = &RPCClient{
apiConfig: apiConfig,
}
@@ -46,11 +53,17 @@ func NewRPCClient(apiConfig *configs.APIConfig) (*RPCClient, error) {
}
// 设置RPC
dao.SetRPC(client)
if isPrimary {
dao.SetRPC(client)
}
return client, nil
}
func (this *RPCClient) APITokenRPC() pb.APITokenServiceClient {
return pb.NewAPITokenServiceClient(this.pickConn())
}
func (this *RPCClient) AdminRPC() pb.AdminServiceClient {
return pb.NewAdminServiceClient(this.pickConn())
}
@@ -67,10 +80,18 @@ func (this *RPCClient) NodeGrantRPC() pb.NodeGrantServiceClient {
return pb.NewNodeGrantServiceClient(this.pickConn())
}
func (this *RPCClient) NodeLoginRPC() pb.NodeLoginServiceClient {
return pb.NewNodeLoginServiceClient(this.pickConn())
}
func (this *RPCClient) NodeClusterRPC() pb.NodeClusterServiceClient {
return pb.NewNodeClusterServiceClient(this.pickConn())
}
func (this *RPCClient) NodeClusterFirewallActionRPC() pb.NodeClusterFirewallActionServiceClient {
return pb.NewNodeClusterFirewallActionServiceClient(this.pickConn())
}
func (this *RPCClient) NodeGroupRPC() pb.NodeGroupServiceClient {
return pb.NewNodeGroupServiceClient(this.pickConn())
}
@@ -79,18 +100,66 @@ 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())
}
func (this *RPCClient) NodeIPAddressLogRPC() pb.NodeIPAddressLogServiceClient {
return pb.NewNodeIPAddressLogServiceClient(this.pickConn())
}
func (this *RPCClient) NodeIPAddressThresholdRPC() pb.NodeIPAddressThresholdServiceClient {
return pb.NewNodeIPAddressThresholdServiceClient(this.pickConn())
}
func (this *RPCClient) NodeValueRPC() pb.NodeValueServiceClient {
return pb.NewNodeValueServiceClient(this.pickConn())
}
func (this *RPCClient) NodeThresholdRPC() pb.NodeThresholdServiceClient {
return pb.NewNodeThresholdServiceClient(this.pickConn())
}
func (this *RPCClient) ServerRPC() pb.ServerServiceClient {
return pb.NewServerServiceClient(this.pickConn())
}
func (this *RPCClient) ServerBandwidthStatRPC() pb.ServerBandwidthStatServiceClient {
return pb.NewServerBandwidthStatServiceClient(this.pickConn())
}
func (this *RPCClient) ServerClientSystemMonthlyStatRPC() pb.ServerClientSystemMonthlyStatServiceClient {
return pb.NewServerClientSystemMonthlyStatServiceClient(this.pickConn())
}
func (this *RPCClient) ServerClientBrowserMonthlyStatRPC() pb.ServerClientBrowserMonthlyStatServiceClient {
return pb.NewServerClientBrowserMonthlyStatServiceClient(this.pickConn())
}
func (this *RPCClient) ServerRegionCountryMonthlyStatRPC() pb.ServerRegionCountryMonthlyStatServiceClient {
return pb.NewServerRegionCountryMonthlyStatServiceClient(this.pickConn())
}
func (this *RPCClient) ServerRegionProvinceMonthlyStatRPC() pb.ServerRegionProvinceMonthlyStatServiceClient {
return pb.NewServerRegionProvinceMonthlyStatServiceClient(this.pickConn())
}
func (this *RPCClient) ServerRegionCityMonthlyStatRPC() pb.ServerRegionCityMonthlyStatServiceClient {
return pb.NewServerRegionCityMonthlyStatServiceClient(this.pickConn())
}
func (this *RPCClient) ServerRegionProviderMonthlyStatRPC() pb.ServerRegionProviderMonthlyStatServiceClient {
return pb.NewServerRegionProviderMonthlyStatServiceClient(this.pickConn())
}
func (this *RPCClient) ServerHTTPFirewallDailyStatRPC() pb.ServerHTTPFirewallDailyStatServiceClient {
return pb.NewServerHTTPFirewallDailyStatServiceClient(this.pickConn())
}
func (this *RPCClient) ServerDailyStatRPC() pb.ServerDailyStatServiceClient {
return pb.NewServerDailyStatServiceClient(this.pickConn())
}
func (this *RPCClient) ServerGroupRPC() pb.ServerGroupServiceClient {
return pb.NewServerGroupServiceClient(this.pickConn())
}
@@ -99,14 +168,18 @@ func (this *RPCClient) APINodeRPC() pb.APINodeServiceClient {
return pb.NewAPINodeServiceClient(this.pickConn())
}
func (this *RPCClient) UserNodeRPC() pb.UserNodeServiceClient {
return pb.NewUserNodeServiceClient(this.pickConn())
func (this *RPCClient) APIMethodStatRPC() pb.APIMethodStatServiceClient {
return pb.NewAPIMethodStatServiceClient(this.pickConn())
}
func (this *RPCClient) DBNodeRPC() pb.DBNodeServiceClient {
return pb.NewDBNodeServiceClient(this.pickConn())
}
func (this *RPCClient) DBRPC() pb.DBServiceClient {
return pb.NewDBServiceClient(this.pickConn())
}
func (this *RPCClient) OriginRPC() pb.OriginServiceClient {
return pb.NewOriginServiceClient(this.pickConn())
}
@@ -143,6 +216,14 @@ func (this *RPCClient) HTTPCachePolicyRPC() pb.HTTPCachePolicyServiceClient {
return pb.NewHTTPCachePolicyServiceClient(this.pickConn())
}
func (this *RPCClient) HTTPCacheTaskRPC() pb.HTTPCacheTaskServiceClient {
return pb.NewHTTPCacheTaskServiceClient(this.pickConn())
}
func (this *RPCClient) HTTPCacheTaskKeyRPC() pb.HTTPCacheTaskKeyServiceClient {
return pb.NewHTTPCacheTaskKeyServiceClient(this.pickConn())
}
func (this *RPCClient) HTTPFirewallPolicyRPC() pb.HTTPFirewallPolicyServiceClient {
return pb.NewHTTPFirewallPolicyServiceClient(this.pickConn())
}
@@ -155,6 +236,10 @@ func (this *RPCClient) HTTPFirewallRuleSetRPC() pb.HTTPFirewallRuleSetServiceCli
return pb.NewHTTPFirewallRuleSetServiceClient(this.pickConn())
}
func (this *RPCClient) FirewallRPC() pb.FirewallServiceClient {
return pb.NewFirewallServiceClient(this.pickConn())
}
func (this *RPCClient) HTTPLocationRPC() pb.HTTPLocationServiceClient {
return pb.NewHTTPLocationServiceClient(this.pickConn())
}
@@ -167,11 +252,19 @@ func (this *RPCClient) HTTPRewriteRuleRPC() pb.HTTPRewriteRuleServiceClient {
return pb.NewHTTPRewriteRuleServiceClient(this.pickConn())
}
// 访问日志
// HTTPAccessLogRPC 访问日志
func (this *RPCClient) HTTPAccessLogRPC() pb.HTTPAccessLogServiceClient {
return pb.NewHTTPAccessLogServiceClient(this.pickConn())
}
func (this *RPCClient) HTTPFastcgiRPC() pb.HTTPFastcgiServiceClient {
return pb.NewHTTPFastcgiServiceClient(this.pickConn())
}
func (this *RPCClient) HTTPAuthPolicyRPC() pb.HTTPAuthPolicyServiceClient {
return pb.NewHTTPAuthPolicyServiceClient(this.pickConn())
}
func (this *RPCClient) SSLCertRPC() pb.SSLCertServiceClient {
return pb.NewSSLCertServiceClient(this.pickConn())
}
@@ -192,6 +285,14 @@ func (this *RPCClient) IPLibraryRPC() pb.IPLibraryServiceClient {
return pb.NewIPLibraryServiceClient(this.pickConn())
}
func (this *RPCClient) IPLibraryFileRPC() pb.IPLibraryFileServiceClient {
return pb.NewIPLibraryFileServiceClient(this.pickConn())
}
func (this *RPCClient) IPLibraryArtifactRPC() pb.IPLibraryArtifactServiceClient {
return pb.NewIPLibraryArtifactServiceClient(this.pickConn())
}
func (this *RPCClient) IPListRPC() pb.IPListServiceClient {
return pb.NewIPListServiceClient(this.pickConn())
}
@@ -216,6 +317,18 @@ func (this *RPCClient) RegionProvinceRPC() pb.RegionProvinceServiceClient {
return pb.NewRegionProvinceServiceClient(this.pickConn())
}
func (this *RPCClient) RegionCityRPC() pb.RegionCityServiceClient {
return pb.NewRegionCityServiceClient(this.pickConn())
}
func (this *RPCClient) RegionTownRPC() pb.RegionTownServiceClient {
return pb.NewRegionTownServiceClient(this.pickConn())
}
func (this *RPCClient) RegionProviderRPC() pb.RegionProviderServiceClient {
return pb.NewRegionProviderServiceClient(this.pickConn())
}
func (this *RPCClient) LogRPC() pb.LogServiceClient {
return pb.NewLogServiceClient(this.pickConn())
}
@@ -232,6 +345,10 @@ func (this *RPCClient) DNSRPC() pb.DNSServiceClient {
return pb.NewDNSServiceClient(this.pickConn())
}
func (this *RPCClient) DNSTaskRPC() pb.DNSTaskServiceClient {
return pb.NewDNSTaskServiceClient(this.pickConn())
}
func (this *RPCClient) ACMEUserRPC() pb.ACMEUserServiceClient {
return pb.NewACMEUserServiceClient(this.pickConn())
}
@@ -240,26 +357,78 @@ func (this *RPCClient) ACMETaskRPC() pb.ACMETaskServiceClient {
return pb.NewACMETaskServiceClient(this.pickConn())
}
func (this *RPCClient) ACMEProviderRPC() pb.ACMEProviderServiceClient {
return pb.NewACMEProviderServiceClient(this.pickConn())
}
func (this *RPCClient) ACMEProviderAccountRPC() pb.ACMEProviderAccountServiceClient {
return pb.NewACMEProviderAccountServiceClient(this.pickConn())
}
func (this *RPCClient) UserRPC() pb.UserServiceClient {
return pb.NewUserServiceClient(this.pickConn())
}
func (this *RPCClient) UserBillRPC() pb.UserBillServiceClient {
return pb.NewUserBillServiceClient(this.pickConn())
func (this *RPCClient) UserAccessKeyRPC() pb.UserAccessKeyServiceClient {
return pb.NewUserAccessKeyServiceClient(this.pickConn())
}
func (this *RPCClient) UserIdentityRPC() pb.UserIdentityServiceClient {
return pb.NewUserIdentityServiceClient(this.pickConn())
}
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())
}
// 构造Admin上下文
func (this *RPCClient) LatestItemRPC() pb.LatestItemServiceClient {
return pb.NewLatestItemServiceClient(this.pickConn())
}
func (this *RPCClient) MetricItemRPC() pb.MetricItemServiceClient {
return pb.NewMetricItemServiceClient(this.pickConn())
}
func (this *RPCClient) MetricStatRPC() pb.MetricStatServiceClient {
return pb.NewMetricStatServiceClient(this.pickConn())
}
func (this *RPCClient) MetricChartRPC() pb.MetricChartServiceClient {
return pb.NewMetricChartServiceClient(this.pickConn())
}
func (this *RPCClient) NodeClusterMetricItemRPC() pb.NodeClusterMetricItemServiceClient {
return pb.NewNodeClusterMetricItemServiceClient(this.pickConn())
}
func (this *RPCClient) ServerStatBoardRPC() pb.ServerStatBoardServiceClient {
return pb.NewServerStatBoardServiceClient(this.pickConn())
}
func (this *RPCClient) ServerDomainHourlyStatRPC() pb.ServerDomainHourlyStatServiceClient {
return pb.NewServerDomainHourlyStatServiceClient(this.pickConn())
}
func (this *RPCClient) ServerStatBoardChartRPC() pb.ServerStatBoardChartServiceClient {
return pb.NewServerStatBoardChartServiceClient(this.pickConn())
}
func (this *RPCClient) TrafficDailyStatRPC() pb.TrafficDailyStatServiceClient {
return pb.NewTrafficDailyStatServiceClient(this.pickConn())
}
// Context 构造Admin上下文
func (this *RPCClient) Context(adminId int64) context.Context {
ctx := context.Background()
m := maps.Map{
var ctx = context.Background()
var m = maps.Map{
"timestamp": time.Now().Unix(),
"type": "admin",
"userId": adminId,
@@ -274,15 +443,15 @@ func (this *RPCClient) Context(adminId int64) context.Context {
utils.PrintError(err)
return context.Background()
}
token := base64.StdEncoding.EncodeToString(data)
var token = base64.StdEncoding.EncodeToString(data)
ctx = metadata.AppendToOutgoingContext(ctx, "nodeId", this.apiConfig.NodeId, "token", token)
return ctx
}
// 构造API上下文
// APIContext 构造API上下文
func (this *RPCClient) APIContext(apiNodeId int64) context.Context {
ctx := context.Background()
m := maps.Map{
var ctx = context.Background()
var m = maps.Map{
"timestamp": time.Now().Unix(),
"type": "api",
"userId": apiNodeId,
@@ -297,27 +466,64 @@ func (this *RPCClient) APIContext(apiNodeId int64) context.Context {
utils.PrintError(err)
return context.Background()
}
token := base64.StdEncoding.EncodeToString(data)
var token = base64.StdEncoding.EncodeToString(data)
ctx = metadata.AppendToOutgoingContext(ctx, "nodeId", this.apiConfig.NodeId, "token", token)
return ctx
}
// UpdateConfig 修改配置
func (this *RPCClient) UpdateConfig(config *configs.APIConfig) error {
this.apiConfig = config
this.locker.Lock()
err := this.init()
this.locker.Unlock()
return err
}
// 初始化
func (this *RPCClient) init() error {
// 当前的IP地址
var localIPAddrs = this.localIPAddrs()
// 重新连接
conns := []*grpc.ClientConn{}
for _, endpoint := range this.apiConfig.RPC.Endpoints {
var conns = []*grpc.ClientConn{}
for _, endpoint := range this.apiConfig.RPCEndpoints {
u, err := url.Parse(endpoint)
if err != nil {
return errors.New("parse endpoint failed: " + err.Error())
return fmt.Errorf("parse endpoint failed: %w", err)
}
var apiHost = u.Host
// 如果本机,则将地址修改为回路地址
if lists.ContainsString(localIPAddrs, u.Hostname()) {
if strings.Contains(apiHost, "[") { // IPv6 [host]:port
apiHost = "[::1]"
} else {
apiHost = "127.0.0.1"
}
var port = u.Port()
if len(port) > 0 {
apiHost += ":" + port
}
}
var conn *grpc.ClientConn
var callOptions = grpc.WithDefaultCallOptions(
grpc.MaxCallRecvMsgSize(128<<20),
grpc.MaxCallSendMsgSize(128<<20),
grpc.UseCompressor(gzip.Name),
)
var keepaliveParams = grpc.WithKeepaliveParams(keepalive.ClientParameters{
Time: 30 * time.Second,
})
if u.Scheme == "http" {
conn, err = grpc.Dial(u.Host, grpc.WithInsecure(), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(128*1024*1024)))
conn, err = grpc.Dial(apiHost, grpc.WithTransportCredentials(insecure.NewCredentials()), callOptions, keepaliveParams)
} else if u.Scheme == "https" {
conn, err = grpc.Dial(u.Host, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
conn, err = grpc.Dial(apiHost, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
InsecureSkipVerify: true,
})), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(128*1024*1024)))
})), callOptions, keepaliveParams)
} else {
return errors.New("parse endpoint failed: invalid scheme '" + u.Scheme + "'")
}
@@ -329,6 +535,8 @@ func (this *RPCClient) init() error {
if len(conns) == 0 {
return errors.New("[RPC]no available endpoints")
}
// 这里不需要加锁因为会和pickConn冲突
this.conns = conns
return nil
}
@@ -339,39 +547,71 @@ func (this *RPCClient) pickConn() *grpc.ClientConn {
defer this.locker.Unlock()
// 检查连接状态
if len(this.conns) > 0 {
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 关闭
func (this *RPCClient) Close() error {
this.locker.Lock()
defer this.locker.Unlock()
var lastErr error
for _, conn := range this.conns {
var err = conn.Close()
if err != nil {
lastErr = err
continue
}
}
return lastErr
}
func (this *RPCClient) localIPAddrs() []string {
localInterfaceAddrs, err := net.InterfaceAddrs()
var localIPAddrs = []string{}
if err == nil {
for _, addr := range localInterfaceAddrs {
var addrString = addr.String()
var index = strings.Index(addrString, "/")
if index > 0 {
localIPAddrs = append(localIPAddrs, addrString[:index])
}
}
}
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

@@ -18,7 +18,7 @@ func TestRPCClient_NodeRPC(t *testing.T) {
if err != nil {
t.Fatal(err)
}
rpc, err := NewRPCClient(config)
rpc, err := NewRPCClient(config, true)
if err != nil {
t.Fatal(err)
}
@@ -34,14 +34,10 @@ func TestRPCClient_NodeRPC(t *testing.T) {
func TestRPC_Dial_HTTP(t *testing.T) {
client, err := NewRPCClient(&configs.APIConfig{
RPC: struct {
Endpoints []string `yaml:"endpoints"`
}{
Endpoints: []string{"127.0.0.1:8003"},
},
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
})
RPCEndpoints: []string{"https://127.0.0.1:8003"},
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
}, true)
if err != nil {
t.Fatal(err)
}
@@ -55,14 +51,10 @@ func TestRPC_Dial_HTTP(t *testing.T) {
func TestRPC_Dial_HTTP_2(t *testing.T) {
client, err := NewRPCClient(&configs.APIConfig{
RPC: struct {
Endpoints []string `yaml:"endpoints"`
}{
Endpoints: []string{"http://127.0.0.1:8003"},
},
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
})
RPCEndpoints: []string{"https://127.0.0.1:8003"},
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
}, true)
if err != nil {
t.Fatal(err)
}
@@ -76,14 +68,10 @@ func TestRPC_Dial_HTTP_2(t *testing.T) {
func TestRPC_Dial_HTTPS(t *testing.T) {
client, err := NewRPCClient(&configs.APIConfig{
RPC: struct {
Endpoints []string `yaml:"endpoints"`
}{
Endpoints: []string{"https://127.0.0.1:8004"},
},
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
})
RPCEndpoints: []string{"https://127.0.0.1:8004"},
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
}, true)
if err != nil {
t.Fatal(err)
}
@@ -94,3 +82,53 @@ func TestRPC_Dial_HTTPS(t *testing.T) {
}
t.Log(resp.Node)
}
func BenchmarkNewRPCClient(b *testing.B) {
config, err := configs.LoadAPIConfig()
if err != nil {
b.Fatal(err)
}
rpc, err := NewRPCClient(config, true)
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
resp, err := rpc.AdminRPC().LoginAdmin(rpc.Context(0), &pb.LoginAdminRequest{
Username: "admin",
Password: stringutil.Md5("123456"),
})
if err != nil {
b.Fatal(err)
}
_ = resp
}
}
func BenchmarkNewRPCClient_2(b *testing.B) {
config, err := configs.LoadAPIConfig()
if err != nil {
b.Fatal(err)
}
rpc, err := NewRPCClient(config, true)
if err != nil {
b.Fatal(err)
}
var conn = rpc.AdminRPC()
b.ResetTimer()
for i := 0; i < b.N; i++ {
resp, err := conn.LoginAdmin(rpc.Context(0), &pb.LoginAdminRequest{
Username: "admin",
Password: stringutil.Md5("123456"),
})
if err != nil {
b.Fatal(err)
}
_ = resp
}
}

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"
)
@@ -20,7 +23,7 @@ func SharedRPC() (*RPCClient, error) {
if err != nil {
return nil, err
}
client, err := NewRPCClient(config)
client, err := NewRPCClient(config, true)
if err != nil {
return nil, err
}
@@ -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

@@ -6,7 +6,7 @@ import (
var isConfigured bool
// 判断系统是否已经配置过
// IsConfigured 判断系统是否已经配置过
func IsConfigured() bool {
if isConfigured {
return true
@@ -16,3 +16,8 @@ func IsConfigured() bool {
isConfigured = err == nil
return isConfigured
}
// IsNewInstalled IsNew 检查是否新安装
func IsNewInstalled() bool {
return configs.IsNewInstalled()
}

View File

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

View File

@@ -0,0 +1,166 @@
package tasks
import (
"context"
"crypto/tls"
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/events"
"github.com/TeaOSLab/EdgeAdmin/internal/goman"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeAdmin/internal/setup"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/logs"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"net/url"
"sort"
"strings"
"sync"
"time"
)
func init() {
events.On(events.EventStart, func() {
task := NewSyncAPINodesTask()
goman.New(func() {
task.Start()
})
})
}
// SyncAPINodesTask API节点同步任务
type SyncAPINodesTask struct {
}
func NewSyncAPINodesTask() *SyncAPINodesTask {
return &SyncAPINodesTask{}
}
func (this *SyncAPINodesTask) Start() {
ticker := time.NewTicker(5 * time.Minute)
if Tea.IsTesting() {
// 快速测试
ticker = time.NewTicker(1 * time.Minute)
}
for range ticker.C {
err := this.Loop()
if err != nil {
logs.Println("[TASK][SYNC_API_NODES]" + err.Error())
}
}
}
func (this *SyncAPINodesTask) Loop() error {
// 如果还没有安装直接返回
if !setup.IsConfigured() || teaconst.IsRecoverMode {
return nil
}
config, err := configs.LoadAPIConfig()
if err != nil {
return err
}
// 是否禁止自动升级
if config.RPCDisableUpdate {
return nil
}
// 获取所有可用的节点
rpcClient, err := rpc.SharedRPC()
if err != nil {
return err
}
resp, err := rpcClient.APINodeRPC().FindAllEnabledAPINodes(rpcClient.Context(0), &pb.FindAllEnabledAPINodesRequest{})
if err != nil {
return err
}
var newEndpoints = []string{}
for _, node := range resp.ApiNodes {
if !node.IsOn {
continue
}
newEndpoints = append(newEndpoints, node.AccessAddrs...)
}
// 和现有的对比
if this.isSame(newEndpoints, config.RPCEndpoints) {
return nil
}
// 测试是否有API节点可用
hasOk := this.testEndpoints(newEndpoints)
if !hasOk {
return nil
}
// 修改RPC对象配置
config.RPCEndpoints = newEndpoints
err = rpcClient.UpdateConfig(config)
if err != nil {
return err
}
// 保存到文件
err = config.WriteFile(Tea.ConfigFile(configs.ConfigFileName))
if err != nil {
return err
}
return nil
}
func (this *SyncAPINodesTask) isSame(endpoints1 []string, endpoints2 []string) bool {
sort.Strings(endpoints1)
sort.Strings(endpoints2)
return strings.Join(endpoints1, "&") == strings.Join(endpoints2, "&")
}
func (this *SyncAPINodesTask) testEndpoints(endpoints []string) bool {
if len(endpoints) == 0 {
return false
}
var wg = sync.WaitGroup{}
wg.Add(len(endpoints))
var ok = false
for _, endpoint := range endpoints {
go func(endpoint string) {
defer wg.Done()
u, err := url.Parse(endpoint)
if err != nil {
return
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer func() {
cancel()
}()
var conn *grpc.ClientConn
if u.Scheme == "http" {
conn, err = grpc.DialContext(ctx, u.Host, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())
} else if u.Scheme == "https" {
conn, err = grpc.DialContext(ctx, u.Host, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
InsecureSkipVerify: true,
})), grpc.WithBlock())
}
if err != nil {
return
}
_ = conn.Close()
ok = true
}(endpoint)
}
wg.Wait()
return ok
}

View File

@@ -0,0 +1,15 @@
package tasks
import (
_ "github.com/iwind/TeaGo/bootstrap"
"testing"
)
func TestSyncAPINodesTask_Loop(t *testing.T) {
task := NewSyncAPINodesTask()
err := task.Loop()
if err != nil {
t.Fatal(err)
}
t.Log("ok")
}

View File

@@ -1,7 +1,9 @@
package tasks
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/events"
"github.com/TeaOSLab/EdgeAdmin/internal/goman"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeAdmin/internal/setup"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/nodeutils"
@@ -16,11 +18,13 @@ import (
func init() {
events.On(events.EventStart, func() {
task := NewSyncClusterTask()
go task.Start()
goman.New(func() {
task.Start()
})
})
}
// 自动同步集群任务
// SyncClusterTask 自动同步集群任务
type SyncClusterTask struct {
}
@@ -40,7 +44,7 @@ func (this *SyncClusterTask) Start() {
func (this *SyncClusterTask) loop() error {
// 如果还没有安装直接返回
if !setup.IsConfigured() {
if !setup.IsConfigured() || teaconst.IsRecoverMode {
return nil
}
@@ -50,7 +54,7 @@ func (this *SyncClusterTask) loop() error {
}
ctx := rpcClient.Context(0)
tasksResp, err := rpcClient.NodeTaskRPC().FindNotifyingNodeTasks(ctx, &pb.FindNotifyingNodeTasksRequest{Size: 100})
tasksResp, err := rpcClient.NodeTaskRPC().FindNotifyingNodeTasks(ctx, &pb.FindNotifyingNodeTasksRequest{Size: 500})
if err != nil {
return err
}

View File

@@ -10,8 +10,10 @@ var DefaultCache = NewCache()
// TTL缓存
// 最大的缓存时间为30 * 86400
// Piece数据结构
// Piece1 | Piece2 | Piece3 | ...
// [ Item1, Item2, ... | ...
//
// Piece1 | Piece2 | Piece3 | ...
// [ Item1, Item2, ... | ...
//
// KeyMap列表数据结构
// { timestamp1 => [key1, key2, ...] }, ...
type Cache struct {
@@ -109,19 +111,11 @@ func (this *Cache) Read(key string) (item *Item) {
return this.pieces[uint64Key%this.countPieces].Read(uint64Key)
}
func (this *Cache) readIntKey(key uint64) (value *Item) {
return this.pieces[key%this.countPieces].Read(key)
}
func (this *Cache) Delete(key string) {
uint64Key := HashKey([]byte(key))
this.pieces[uint64Key%this.countPieces].Delete(uint64Key)
}
func (this *Cache) deleteIntKey(key uint64) {
this.pieces[key%this.countPieces].Delete(key)
}
func (this *Cache) Count() (count int) {
for _, piece := range this.pieces {
count += piece.Count()

View File

@@ -30,7 +30,7 @@ func (this *Piece) IncreaseInt64(key uint64, delta int64, expiredAt int64) (resu
this.locker.Lock()
item, ok := this.m[key]
if ok {
result := types.Int64(item.Value) + delta
result = types.Int64(item.Value) + delta
item.Value = result
item.expiredAt = expiredAt
} else {

View File

@@ -1,6 +1,6 @@
package ttlcache
import "github.com/cespare/xxhash"
import "github.com/cespare/xxhash/v2"
func HashKey(key []byte) uint64 {
return xxhash.Sum64(key)

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.RPCEndpoints = 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 fmt.Errorf("upgrade api node failed: %w", err)
}
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 fmt.Errorf("lookup version failed: %w", err)
}
// 检查要升级的文件
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 fmt.Errorf("upload deploy file '%s' failed: %w", filepath.Base(deployFile.Path), err)
}
}
}
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 fmt.Errorf("upload deploy file '%s' failed: %w", filepath.Base(deployFile.Path), err)
}
}
}
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:]
}

12
internal/utils/email.go Normal file
View File

@@ -0,0 +1,12 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package utils
import "regexp"
var emailReg = regexp.MustCompile(`(?i)^[a-z\d]+([._+-]*[a-z\d]+)*@([a-z\d]+[a-z\d-]*[a-z\d]+\.)+[a-z\d]+$`)
// ValidateEmail 校验电子邮箱格式
func ValidateEmail(email string) bool {
return emailReg.MatchString(email)
}

View File

@@ -0,0 +1,22 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package utils_test
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/iwind/TeaGo/assert"
"testing"
)
func TestValidateEmail(t *testing.T) {
var a = assert.NewAssertion(t)
a.IsTrue(utils.ValidateEmail("aaaa@gmail.com"))
a.IsTrue(utils.ValidateEmail("a.b@gmail.com"))
a.IsTrue(utils.ValidateEmail("a.b.c.d@gmail.com"))
a.IsTrue(utils.ValidateEmail("aaaa@gmail.com.cn"))
a.IsTrue(utils.ValidateEmail("hello.world.123@gmail.123.com"))
a.IsTrue(utils.ValidateEmail("10000@qq.com"))
a.IsFalse(utils.ValidateEmail("aaaa.@gmail.com"))
a.IsFalse(utils.ValidateEmail("aaaa@gmail"))
a.IsFalse(utils.ValidateEmail("aaaa@123"))
}

162
internal/utils/exec/cmd.go Normal file
View File

@@ -0,0 +1,162 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package executils
import (
"bytes"
"context"
"os"
"os/exec"
"strings"
"time"
)
type Cmd struct {
name string
args []string
env []string
dir string
ctx context.Context
timeout time.Duration
cancelFunc func()
captureStdout bool
captureStderr bool
stdout *bytes.Buffer
stderr *bytes.Buffer
rawCmd *exec.Cmd
}
func NewCmd(name string, args ...string) *Cmd {
return &Cmd{
name: name,
args: args,
}
}
func NewTimeoutCmd(timeout time.Duration, name string, args ...string) *Cmd {
return (&Cmd{
name: name,
args: args,
}).WithTimeout(timeout)
}
func (this *Cmd) WithTimeout(timeout time.Duration) *Cmd {
this.timeout = timeout
ctx, cancelFunc := context.WithTimeout(context.Background(), timeout)
this.ctx = ctx
this.cancelFunc = cancelFunc
return this
}
func (this *Cmd) WithStdout() *Cmd {
this.captureStdout = true
return this
}
func (this *Cmd) WithStderr() *Cmd {
this.captureStderr = true
return this
}
func (this *Cmd) WithEnv(env []string) *Cmd {
this.env = env
return this
}
func (this *Cmd) WithDir(dir string) *Cmd {
this.dir = dir
return this
}
func (this *Cmd) Start() error {
var cmd = this.compose()
return cmd.Start()
}
func (this *Cmd) Wait() error {
var cmd = this.compose()
return cmd.Wait()
}
func (this *Cmd) Run() error {
if this.cancelFunc != nil {
defer this.cancelFunc()
}
var cmd = this.compose()
return cmd.Run()
}
func (this *Cmd) RawStdout() string {
if this.stdout != nil {
return this.stdout.String()
}
return ""
}
func (this *Cmd) Stdout() string {
return strings.TrimSpace(this.RawStdout())
}
func (this *Cmd) RawStderr() string {
if this.stderr != nil {
return this.stderr.String()
}
return ""
}
func (this *Cmd) Stderr() string {
return strings.TrimSpace(this.RawStderr())
}
func (this *Cmd) String() string {
if this.rawCmd != nil {
return this.rawCmd.String()
}
var newCmd = exec.Command(this.name, this.args...)
return newCmd.String()
}
func (this *Cmd) Process() *os.Process {
if this.rawCmd != nil {
return this.rawCmd.Process
}
return nil
}
func (this *Cmd) compose() *exec.Cmd {
if this.rawCmd != nil {
return this.rawCmd
}
if this.ctx != nil {
this.rawCmd = exec.CommandContext(this.ctx, this.name, this.args...)
} else {
this.rawCmd = exec.Command(this.name, this.args...)
}
if this.env != nil {
this.rawCmd.Env = this.env
}
if len(this.dir) > 0 {
this.rawCmd.Dir = this.dir
}
if this.captureStdout {
this.stdout = &bytes.Buffer{}
this.rawCmd.Stdout = this.stdout
}
if this.captureStderr {
this.stderr = &bytes.Buffer{}
this.rawCmd.Stderr = this.stderr
}
return this.rawCmd
}

View File

@@ -0,0 +1,61 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package executils_test
import (
executils "github.com/TeaOSLab/EdgeAdmin/internal/utils/exec"
"testing"
"time"
)
func TestNewTimeoutCmd_Sleep(t *testing.T) {
var cmd = executils.NewTimeoutCmd(1*time.Second, "sleep", "3")
cmd.WithStdout()
cmd.WithStderr()
err := cmd.Run()
t.Log("error:", err)
t.Log("stdout:", cmd.Stdout())
t.Log("stderr:", cmd.Stderr())
}
func TestNewTimeoutCmd_Echo(t *testing.T) {
var cmd = executils.NewTimeoutCmd(10*time.Second, "echo", "-n", "hello")
cmd.WithStdout()
cmd.WithStderr()
err := cmd.Run()
t.Log("error:", err)
t.Log("stdout:", cmd.Stdout())
t.Log("stderr:", cmd.Stderr())
}
func TestNewTimeoutCmd_Echo2(t *testing.T) {
var cmd = executils.NewCmd("echo", "hello")
cmd.WithStdout()
cmd.WithStderr()
err := cmd.Run()
t.Log("error:", err)
t.Log("stdout:", cmd.Stdout())
t.Log("raw stdout:", cmd.RawStdout())
t.Log("stderr:", cmd.Stderr())
t.Log("raw stderr:", cmd.RawStderr())
}
func TestNewTimeoutCmd_Echo3(t *testing.T) {
var cmd = executils.NewCmd("echo", "-n", "hello")
err := cmd.Run()
t.Log("error:", err)
t.Log("stdout:", cmd.Stdout())
t.Log("stderr:", cmd.Stderr())
}
func TestCmd_Process(t *testing.T) {
var cmd = executils.NewCmd("echo", "-n", "hello")
err := cmd.Run()
t.Log("error:", err)
t.Log(cmd.Process())
}
func TestNewTimeoutCmd_String(t *testing.T) {
var cmd = executils.NewCmd("echo", "-n", "hello")
t.Log("stdout:", cmd.String())
}

View File

@@ -0,0 +1,58 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build linux
package executils
import (
"golang.org/x/sys/unix"
"io/fs"
"os"
"os/exec"
"syscall"
)
// LookPath customize our LookPath() function, to work in broken $PATH environment variable
func LookPath(file string) (string, error) {
result, err := exec.LookPath(file)
if err == nil && len(result) > 0 {
return result, nil
}
// add common dirs contains executable files these may be excluded in $PATH environment variable
var binPaths = []string{
"/usr/sbin",
"/usr/bin",
"/usr/local/sbin",
"/usr/local/bin",
}
for _, binPath := range binPaths {
var fullPath = binPath + string(os.PathSeparator) + file
stat, err := os.Stat(fullPath)
if err != nil {
continue
}
if stat.IsDir() {
return "", syscall.EISDIR
}
var mode = stat.Mode()
if mode.IsDir() {
return "", syscall.EISDIR
}
err = syscall.Faccessat(unix.AT_FDCWD, fullPath, unix.X_OK, unix.AT_EACCESS)
if err == nil || (err != syscall.ENOSYS && err != syscall.EPERM) {
return fullPath, err
}
if mode&0111 != 0 {
return fullPath, nil
}
return "", fs.ErrPermission
}
return "", &exec.Error{
Name: file,
Err: exec.ErrNotFound,
}
}

View File

@@ -0,0 +1,10 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !linux
package executils
import "os/exec"
func LookPath(file string) (string, error) {
return exec.LookPath(file)
}

View File

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

View File

@@ -1,19 +1,174 @@
package utils
import (
"encoding/binary"
"bytes"
"errors"
"github.com/TeaOSLab/EdgeCommon/pkg/iputils"
"github.com/iwind/TeaGo/types"
"net"
"strings"
)
// 将IP转换为整型
func IP2Long(ip string) uint32 {
s := net.ParseIP(ip)
if s == nil {
return 0
// ParseIPValue 解析IP值
func ParseIPValue(value string) (newValue string, ipFrom string, ipTo string, ok bool) {
if len(value) == 0 {
return
}
if len(s) == 16 {
return binary.BigEndian.Uint32(s[12:16])
newValue = value
// ip1-ip2
if strings.Contains(value, "-") {
var pieces = strings.Split(value, "-")
if len(pieces) != 2 {
return
}
ipFrom = strings.TrimSpace(pieces[0])
ipTo = strings.TrimSpace(pieces[1])
if !iputils.IsValid(ipFrom) || !iputils.IsValid(ipTo) {
return
}
if !iputils.IsSameVersion(ipFrom, ipTo) {
return
}
if iputils.CompareIP(ipFrom, ipTo) > 0 {
ipFrom, ipTo = ipTo, ipFrom
newValue = ipFrom + "-" + ipTo
}
ok = true
return
}
return binary.BigEndian.Uint32(s)
// ip/mask
if strings.Contains(value, "/") {
cidr, err := iputils.ParseCIDR(value)
if err != nil {
return
}
return newValue, cidr.From().String(), cidr.To().String(), true
}
// single value
if iputils.IsValid(value) {
ipFrom = value
ok = true
return
}
return
}
// ExtractIP 分解IP
// 只支持D段掩码的CIDR
// 最多只记录255个值
func ExtractIP(ipStrings string) ([]string, error) {
ipStrings = strings.ReplaceAll(ipStrings, " ", "")
// CIDR
if strings.Contains(ipStrings, "/") {
_, cidrNet, err := net.ParseCIDR(ipStrings)
if err != nil {
return nil, err
}
var index = strings.Index(ipStrings, "/")
var ipFrom = ipStrings[:index]
var bits = types.Int(ipStrings[index+1:])
if bits < 24 {
return nil, errors.New("CIDR bits should be greater than 24")
}
var ipv4 = net.ParseIP(ipFrom).To4()
if len(ipv4) == 0 {
return nil, errors.New("support IPv4 only")
}
var result = []string{}
ipv4[3] = 0 // 从0开始
for i := 0; i <= 255; i++ {
if cidrNet.Contains(ipv4) {
result = append(result, ipv4.String())
}
ipv4 = NextIP(ipv4)
}
return result, nil
}
// IP Range
if strings.Contains(ipStrings, "-") {
var index = strings.Index(ipStrings, "-")
var ipFromString = ipStrings[:index]
var ipToString = ipStrings[index+1:]
var ipFrom = net.ParseIP(ipFromString).To4()
if len(ipFrom) == 0 {
return nil, errors.New("invalid ip '" + ipFromString + "'")
}
var ipTo = net.ParseIP(ipToString).To4()
if len(ipTo) == 0 {
return nil, errors.New("invalid ip '" + ipToString + "'")
}
if bytes.Compare(ipFrom, ipTo) > 0 {
ipFrom, ipTo = ipTo, ipFrom
}
var result = []string{}
for i := 0; i < 255; i++ {
if bytes.Compare(ipFrom, ipTo) > 0 {
break
}
result = append(result, ipFrom.String())
ipFrom = NextIP(ipFrom)
}
return result, nil
}
return []string{ipStrings}, nil
}
// NextIP IP增加1
func NextIP(prevIP net.IP) net.IP {
var ip = make(net.IP, len(prevIP))
copy(ip, prevIP)
var index = len(ip) - 1
for {
if ip[index] == 255 {
ip[index] = 0
index--
if index < 0 {
break
}
} else {
ip[index]++
break
}
}
return ip
}
// IsLocalIP 判断是否为本地IP
// ip 是To4()或者To16()的结果
func IsLocalIP(ip net.IP) bool {
if ip == nil {
return false
}
if ip[0] == 127 ||
ip[0] == 10 ||
(ip[0] == 172 && ip[1]&0xf0 == 16) ||
(ip[0] == 192 && ip[1] == 168) {
return true
}
if ip.String() == "::1" {
return true
}
return false
}

View File

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

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

@@ -0,0 +1,60 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package utils
import (
"bytes"
"encoding/json"
"errors"
"reflect"
)
// JSONClone 使用JSON克隆对象
func JSONClone(v interface{}) (interface{}, error) {
data, err := json.Marshal(v)
if err != nil {
return nil, err
}
var nv = reflect.New(reflect.TypeOf(v).Elem()).Interface()
err = json.Unmarshal(data, nv)
if err != nil {
return nil, err
}
return nv, nil
}
// JSONIsNull 判断JSON数据是否为null
func JSONIsNull(jsonData []byte) bool {
return len(jsonData) == 0 || bytes.Equal(jsonData, []byte("null"))
}
// JSONDecodeConfig 解码并重新编码
// 是为了去除原有JSON中不需要的数据
func JSONDecodeConfig(data []byte, ptr any) (encodeJSON []byte, err error) {
err = json.Unmarshal(data, ptr)
if err != nil {
return
}
encodeJSON, err = json.Marshal(ptr)
if err != nil {
return
}
// validate config
if ptr != nil {
config, ok := ptr.(interface {
Init() error
})
if ok {
initErr := config.Init()
if initErr != nil {
err = errors.New("validate config failed: " + initErr.Error())
}
}
}
return
}

View File

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

View File

@@ -1,28 +1,81 @@
package utils
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/logs"
"github.com/miekg/dns"
"sync"
)
// 获取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
r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port)
if err != nil {
return "", err
}
if len(r.Answer) == 0 {
return "", nil
var lastErr error
var success = false
var result = ""
var serverAddrs = sharedDNSConfig.Servers
{
var publicDNSHosts = []string{"8.8.8.8" /** Google **/, "8.8.4.4" /** Google **/}
for _, publicDNSHost := range publicDNSHosts {
if !lists.ContainsString(serverAddrs, publicDNSHost) {
serverAddrs = append(serverAddrs, publicDNSHost)
}
}
}
return r.Answer[0].(*dns.CNAME).Target, nil
var wg = &sync.WaitGroup{}
for _, serverAddr := range serverAddrs {
wg.Add(1)
go func(serverAddr string) {
defer wg.Done()
r, _, err := sharedDNSClient.Exchange(m, configutils.QuoteIP(serverAddr)+":"+sharedDNSConfig.Port)
if err != nil {
lastErr = err
return
}
success = true
if len(r.Answer) == 0 {
return
}
result = r.Answer[0].(*dns.CNAME).Target
}(serverAddr)
}
wg.Wait()
if success {
return result, nil
}
return "", lastErr
}

View File

@@ -0,0 +1,15 @@
// 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) {
for _, domain := range []string{"www.yun4s.cn", "example.com"} {
result, err := utils.LookupCNAME(domain)
t.Log(domain, "=>", result, err)
}
}

View File

@@ -0,0 +1,28 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build !plus
package nodelogutils
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/iwind/TeaGo/maps"
)
// FindCommonTags 查找常用的标签
func FindNodeCommonTags(langCode langs.LangCode) []maps.Map {
return []maps.Map{
{
"name": langs.Message(langCode, codes.Log_TagListener),
"code": "LISTENER",
},
{
"name": langs.Message(langCode, codes.Log_TagWAF),
"code": "WAF",
},
{
"name": langs.Message(langCode, codes.Log_TagAccessLog),
"code": "ACCESS_LOG",
},
}
}

View File

@@ -1,6 +1,12 @@
package numberutils
import "strconv"
import (
"fmt"
"github.com/iwind/TeaGo/types"
"regexp"
"strconv"
"strings"
)
func FormatInt64(value int64) string {
return strconv.FormatInt(value, 10)
@@ -9,3 +15,152 @@ func FormatInt64(value int64) string {
func FormatInt(value int) string {
return strconv.Itoa(value)
}
func Pow1024(n int) int64 {
if n <= 0 {
return 1
}
if n == 1 {
return 1024
}
return Pow1024(n-1) * 1024
}
func FormatBytes(bytes int64) string {
if bytes < Pow1024(1) {
return FormatInt64(bytes) + "B"
} else if bytes < Pow1024(2) {
return TrimZeroSuffix(fmt.Sprintf("%.2fKiB", float64(bytes)/float64(Pow1024(1))))
} else if bytes < Pow1024(3) {
return TrimZeroSuffix(fmt.Sprintf("%.2fMiB", float64(bytes)/float64(Pow1024(2))))
} else if bytes < Pow1024(4) {
return TrimZeroSuffix(fmt.Sprintf("%.2fGiB", float64(bytes)/float64(Pow1024(3))))
} else if bytes < Pow1024(5) {
return TrimZeroSuffix(fmt.Sprintf("%.2fTiB", float64(bytes)/float64(Pow1024(4))))
} else if bytes < Pow1024(6) {
return TrimZeroSuffix(fmt.Sprintf("%.2fPiB", float64(bytes)/float64(Pow1024(5))))
} else {
return TrimZeroSuffix(fmt.Sprintf("%.2fEiB", 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))))
}
}
func FormatCount(count int64) string {
if count < 1000 {
return types.String(count)
}
if count < 1000*1000 {
return fmt.Sprintf("%.1fK", float32(count)/1000)
}
if count < 1000*1000*1000 {
return fmt.Sprintf("%.1fM", float32(count)/1000/1000)
}
return fmt.Sprintf("%.1fB", float32(count)/1000/1000/1000)
}
func FormatFloat(f 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 formatDigit(types.String(x))
case string:
return x
}
return ""
}
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

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

@@ -0,0 +1,31 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package otputils
import (
"net/url"
)
// FixIssuer fix issuer in otp provisioning url
func FixIssuer(urlString string) string {
u, err := url.Parse(urlString)
if err != nil {
return urlString
}
var query = u.Query()
if query != nil {
var issuerName = query.Get("issuer")
if len(issuerName) > 0 {
unescapedIssuerName, unescapeErr := url.QueryUnescape(issuerName)
if unescapeErr == nil {
query.Set("issuer", unescapedIssuerName)
u.RawQuery = query.Encode()
}
}
return u.String()
}
return urlString
}

View File

@@ -0,0 +1,18 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package otputils_test
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils/otputils"
"testing"
)
func TestFixIssuer(t *testing.T) {
var beforeURL = "otpauth://totp/GoEdge%25E7%25AE%25A1%25E7%2590%2586%25E5%2591%2598%25E7%25B3%25BB%25E7%25BB%259F:admin?issuer=GoEdge%25E7%25AE%25A1%25E7%2590%2586%25E5%2591%2598%25E7%25B3%25BB%25E7%25BB%259F&secret=Q3J4WNOWBRFLP3HI"
var afterURL = otputils.FixIssuer(beforeURL)
t.Log(afterURL)
if beforeURL == afterURL {
t.Fatal("'afterURL' should not be equal to 'beforeURL'")
}
}

View File

@@ -39,7 +39,7 @@ func (this *ServiceManager) setup() {
this.onceLocker.Do(func() {
logFile := files.NewFile(Tea.Root + "/logs/service.log")
if logFile.Exists() {
logFile.Delete()
_ = logFile.Delete()
}
//logger

View File

@@ -1,13 +1,13 @@
// +build linux
//go:build linux
package utils
import (
"errors"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
executils "github.com/TeaOSLab/EdgeAdmin/internal/utils/exec"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/files"
"io/ioutil"
"os"
"os/exec"
"regexp"
@@ -16,13 +16,13 @@ import (
var systemdServiceFile = "/etc/systemd/system/edge-admin.service"
var initServiceFile = "/etc/init.d/" + teaconst.SystemdServiceName
// 安装服务
// Install 安装服务
func (this *ServiceManager) Install(exePath string, args []string) error {
if os.Getgid() != 0 {
return errors.New("only root users can install the service")
}
systemd, err := exec.LookPath("systemctl")
systemd, err := executils.LookPath("systemctl")
if err != nil {
return this.installInitService(exePath, args)
}
@@ -30,14 +30,14 @@ func (this *ServiceManager) Install(exePath string, args []string) error {
return this.installSystemdService(systemd, exePath, args)
}
// 启动服务
// Start 启动服务
func (this *ServiceManager) Start() error {
if os.Getgid() != 0 {
return errors.New("only root users can start the service")
}
if files.NewFile(systemdServiceFile).Exists() {
systemd, err := exec.LookPath("systemctl")
systemd, err := executils.LookPath("systemctl")
if err != nil {
return err
}
@@ -47,23 +47,23 @@ func (this *ServiceManager) Start() error {
return exec.Command("service", teaconst.ProcessName, "start").Start()
}
// 删除服务
// Uninstall 删除服务
func (this *ServiceManager) Uninstall() error {
if os.Getgid() != 0 {
return errors.New("only root users can uninstall the service")
}
if files.NewFile(systemdServiceFile).Exists() {
systemd, err := exec.LookPath("systemctl")
systemd, err := executils.LookPath("systemctl")
if err != nil {
return err
}
// disable service
exec.Command(systemd, "disable", teaconst.SystemdServiceName+".service").Start()
_ = exec.Command(systemd, "disable", teaconst.SystemdServiceName+".service").Start()
// reload
exec.Command(systemd, "daemon-reload")
_ = exec.Command(systemd, "daemon-reload").Start()
return files.NewFile(systemdServiceFile).Delete()
}
@@ -83,18 +83,18 @@ func (this *ServiceManager) installInitService(exePath string, args []string) er
return errors.New("'scripts/" + shortName + "' file not exists")
}
data, err := ioutil.ReadFile(scriptFile)
data, err := os.ReadFile(scriptFile)
if err != nil {
return err
}
data = regexp.MustCompile("INSTALL_DIR=.+").ReplaceAll(data, []byte("INSTALL_DIR="+Tea.Root))
err = ioutil.WriteFile(initServiceFile, data, 0777)
err = os.WriteFile(initServiceFile, data, 0777)
if err != nil {
return err
}
chkCmd, err := exec.LookPath("chkconfig")
chkCmd, err := executils.LookPath("chkconfig")
if err != nil {
return err
}
@@ -112,7 +112,14 @@ func (this *ServiceManager) installSystemdService(systemd, exePath string, args
shortName := teaconst.SystemdServiceName
longName := "GoEdge Admin" // TODO 将来可以修改
desc := `# Provides: ` + shortName + `
var startCmd = exePath + " daemon"
bashPath, _ := executils.LookPath("bash")
if len(bashPath) > 0 {
startCmd = bashPath + " -c \"" + startCmd + "\""
}
desc := `### BEGIN INIT INFO
# Provides: ` + shortName + `
# Required-Start: $all
# Required-Stop:
# Default-Start: 2 3 4 5
@@ -128,8 +135,8 @@ After=network-online.target
[Service]
Type=simple
Restart=always
RestartSec=1s
ExecStart=` + exePath + ` daemon
RestartSec=5s
ExecStart=` + startCmd + `
ExecStop=` + exePath + ` stop
ExecReload=` + exePath + ` reload
@@ -137,16 +144,16 @@ ExecReload=` + exePath + ` reload
WantedBy=multi-user.target`
// write file
err := ioutil.WriteFile(systemdServiceFile, []byte(desc), 0777)
err := os.WriteFile(systemdServiceFile, []byte(desc), 0777)
if err != nil {
return err
}
// stop current systemd service if running
exec.Command(systemd, "stop", shortName+".service")
_ = exec.Command(systemd, "stop", shortName+".service").Start()
// reload
exec.Command(systemd, "daemon-reload")
_ = exec.Command(systemd, "daemon-reload").Start()
// enable
cmd := exec.Command(systemd, "enable", shortName+".service")

View File

@@ -1,4 +1,4 @@
// +build !linux,!windows
//go:build !linux && !windows
package utils

View File

@@ -1,9 +1,10 @@
// +build windows
//go:build windows
package utils
import (
"fmt"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/iwind/TeaGo/Tea"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/svc"
@@ -15,7 +16,7 @@ import (
func (this *ServiceManager) Install(exePath string, args []string) error {
m, err := mgr.Connect()
if err != nil {
return fmt.Errorf("connecting: %s please 'Run as administrator' again", err.Error())
return fmt.Errorf("connecting: %w please 'Run as administrator' again", err)
}
defer m.Disconnect()
s, err := m.OpenService(this.Name)
@@ -30,7 +31,7 @@ func (this *ServiceManager) Install(exePath string, args []string) error {
StartType: windows.SERVICE_AUTO_START,
}, args...)
if err != nil {
return fmt.Errorf("creating: %s", err.Error())
return fmt.Errorf("creating: %w", err)
}
defer s.Close()
@@ -46,12 +47,12 @@ func (this *ServiceManager) Start() error {
defer m.Disconnect()
s, err := m.OpenService(this.Name)
if err != nil {
return fmt.Errorf("could not access service: %v", err)
return fmt.Errorf("could not access service: %w", err)
}
defer s.Close()
err = s.Start("service")
if err != nil {
return fmt.Errorf("could not start service: %v", err)
return fmt.Errorf("could not start service: %w", err)
}
return nil
@@ -61,12 +62,12 @@ func (this *ServiceManager) Start() error {
func (this *ServiceManager) Uninstall() error {
m, err := mgr.Connect()
if err != nil {
return fmt.Errorf("connecting: %s please 'Run as administrator' again", err.Error())
return fmt.Errorf("connecting: %w please 'Run as administrator' again", err)
}
defer m.Disconnect()
s, err := m.OpenService(this.Name)
if err != nil {
return fmt.Errorf("open service: %s", err.Error())
return fmt.Errorf("open service: %w", err)
}
// shutdown service
@@ -78,7 +79,7 @@ func (this *ServiceManager) Uninstall() error {
defer s.Close()
err = s.Delete()
if err != nil {
return fmt.Errorf("deleting: %s", err.Error())
return fmt.Errorf("deleting: %w", err)
}
return nil
}

View File

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

View File

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

View File

@@ -5,7 +5,7 @@ import (
"strings"
)
// format address
// FormatAddress format address
func FormatAddress(addr string) string {
if strings.HasSuffix(addr, "unix:") {
return addr
@@ -17,7 +17,7 @@ func FormatAddress(addr string) string {
return addr
}
// 分割数字
// SplitNumbers 分割数字
func SplitNumbers(numbers string) (result []int64) {
if len(numbers) == 0 {
return

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