Compare commits

...

457 Commits

Author SHA1 Message Date
刘祥超
78798060e7 版本号更改为0.6.2 2023-01-10 21:18:08 +08:00
刘祥超
b2569a8dac 版本修改为0.6.1 2023-01-09 16:06:34 +08:00
刘祥超
4bd7ec9871 优化远程安装时uname读取方法 2023-01-08 20:13:09 +08:00
刘祥超
95cfad60c4 更新SQL 2023-01-08 20:12:38 +08:00
刘祥超
7f51d451f7 用户Dashboard信息中增加缓存、攻击相关信息 2023-01-08 11:50:47 +08:00
刘祥超
a4fb0fd795 更新SQL 2023-01-07 19:36:09 +08:00
刘祥超
c02928dd44 升级数据库时同时升级edgeClientAgentIPs中的countIPs字段值 2023-01-07 10:29:08 +08:00
刘祥超
e70c49d407 创建缓存任务时即使没有有效的Key,也可以在后台查看创建的任务 2023-01-05 17:09:20 +08:00
刘祥超
e1c1984fd4 华为云可以设置终端节点(endpoint) 2023-01-01 18:29:33 +08:00
刘祥超
f801d304c6 优化代码 2022-12-31 17:31:27 +08:00
刘祥超
178a38c6d9 优化证书加载速度 2022-12-31 17:21:40 +08:00
刘祥超
e356707db7 优化证书数量很多时的页面加载速度 2022-12-31 17:12:39 +08:00
刘祥超
8d3043d0fe 实现UA名单功能 2022-12-30 20:49:20 +08:00
刘祥超
1e494bd1fd 更新SQL 2022-12-29 19:15:01 +08:00
刘祥超
b726c8d589 增加CORS自适应跨域 2022-12-29 17:16:21 +08:00
刘祥超
e71e80703d 远程安装时使用uname取代/usr/bin/uname命令 2022-12-28 19:01:09 +08:00
刘祥超
c9b666e5bc 增加基础的用户邮件通知 2022-12-27 18:53:49 +08:00
刘祥超
874139ea07 调整Agent相关接口权限 2022-12-22 11:42:30 +08:00
刘祥超
05d79ad606 服务没有所属用户时,可以修改所属用户 2022-12-21 17:07:02 +08:00
刘祥超
13c78a5fec 修改/取消服务套餐同时按需变更DNS 2022-12-17 15:16:01 +08:00
刘祥超
f3e3824b7d DNS完善实现SRV和CAA记录 2022-12-15 16:17:54 +08:00
刘祥超
67473c2dcf 优化代码 2022-12-14 17:32:24 +08:00
刘祥超
3921c547be 智能DNS初步支持搜索引擎线路 2022-12-13 18:39:23 +08:00
刘祥超
3e0d2fda6a 优化代码 2022-12-12 11:12:55 +08:00
刘祥超
7ff6c0c18b 优化代码 2022-12-12 10:28:22 +08:00
刘祥超
e76464673a 实现用户通过邮件重置密码功能 2022-12-10 15:57:17 +08:00
刘祥超
1ab849d9b0 初步完成用户电子邮箱绑定(激活) 2022-12-08 20:25:46 +08:00
刘祥超
781c851571 增加测试用例 2022-12-06 14:53:38 +08:00
刘祥超
0b19d93a47 缩短访问日志自动清理时间(从每12个小时改成每6个小时) 2022-12-03 21:51:55 +08:00
刘祥超
2856f7716b 实现线路优先级 2022-12-03 20:49:53 +08:00
刘祥超
c9ba24dc96 操作系统和浏览器ID字段改为bigint 2022-12-03 20:49:46 +08:00
刘祥超
a660fb1f42 调整自动远程启动离线节点的错误级别 2022-12-02 17:34:24 +08:00
刘祥超
8586ad6478 管理员和用户状态为不可用时,删除已生成的API令牌 2022-12-02 17:33:45 +08:00
刘祥超
0fab6fecfe 获取API令牌时检查管理员和用户状态 2022-12-02 17:33:01 +08:00
刘祥超
d752bb08c7 版本号更改为0.6.0 2022-11-29 15:41:21 +08:00
刘祥超
6f845f36c9 版本号修改为0.5.10 2022-11-28 18:59:09 +08:00
刘祥超
66bc60a47c 更新SQL 2022-11-28 18:17:25 +08:00
刘祥超
1c048da1f0 节点版本号修改为0.5.9 2022-11-28 18:14:17 +08:00
刘祥超
da8aa20f83 版本号修改为0.5.9 2022-11-28 15:58:40 +08:00
刘祥超
14315923d8 删除不必要的文件 2022-11-26 19:49:05 +08:00
刘祥超
3d3228fe96 修复删除节点时不能自动同步DNS的问题 2022-11-26 19:02:08 +08:00
刘祥超
173ac5a8aa 优化健康检查连接超时时间 2022-11-26 18:11:35 +08:00
刘祥超
e9c5d7e7cf 健康检查没有开启上下线的时候也发送节点状态变更通知 2022-11-26 15:54:16 +08:00
刘祥超
194127dce9 提交SQL 2022-11-26 15:39:16 +08:00
刘祥超
86cb7e9d41 缓存任务:校验缓存Key时支持域名中含有星号通配符 2022-11-26 15:03:24 +08:00
刘祥超
b2774de6a2 健康检查时只有开启了自动下线才发送上线通知 2022-11-25 15:48:57 +08:00
刘祥超
3a4722b701 用户可以使用管理员设置的公用线路 2022-11-24 17:19:51 +08:00
刘祥超
028aea4e3d 完全没有设置过SSH登录参数的节点也可以远程安装 2022-11-23 19:39:35 +08:00
刘祥超
c3dd97a7c1 增加数据库版本号 2022-11-22 14:32:04 +08:00
刘祥超
d527fcdd78 在节点详情中显示API节点地址 2022-11-21 21:09:06 +08:00
刘祥超
d6f311e057 节点可以单独设置所使用的API节点地址 2022-11-21 19:55:01 +08:00
刘祥超
991e08fa71 远程安装节点时uname读取失败时自动重试 2022-11-18 15:57:06 +08:00
刘祥超
06b44dc101 提升ssh sudo安装的稳定性 2022-11-18 15:44:53 +08:00
刘祥超
2d94b994fa DNS API支持查询多个同名记录/优化ACME申请 2022-11-17 17:33:59 +08:00
刘祥超
c036186dde 优化代码 2022-11-17 10:01:07 +08:00
刘祥超
bc2ad13037 集群被删除或者不可用时,健康检查时不提示错误 2022-11-16 14:10:03 +08:00
刘祥超
bfa04856aa 同步域名解析时自动剔除相同的节点A记录 2022-11-16 09:01:41 +08:00
刘祥超
feb1068441 边缘节点支持设置多个缓存目录 2022-11-15 20:35:59 +08:00
刘祥超
181a4d05b0 生成账单时只处理用户ID大于0的记录 2022-11-15 16:34:33 +08:00
刘祥超
c449265e05 cloudflare域名单页读取数从20修改为50,修复测试用例 2022-11-14 21:15:10 +08:00
刘祥超
e778616b5c 修复cloudflare域名只能读取第一页的问题 2022-11-14 21:04:13 +08:00
刘祥超
dad5be2670 缩短节点运行日志队列长度 2022-11-14 16:42:18 +08:00
刘祥超
414afd17b8 在写入API节点日志时尽量避免重复内容 2022-11-14 16:38:11 +08:00
刘祥超
86a806bca2 修复默认黑白名单不是全局的问题 2022-11-13 10:31:42 +08:00
刘祥超
faed7420a7 安装过程显示更详细内容 2022-11-11 21:48:00 +08:00
刘祥超
ae14ff4f9f 优化操作系统和浏览器统计相关程序 2022-11-11 17:48:30 +08:00
刘祥超
99e1658fdf 默认创建的IP名单设置为全局有效 2022-11-11 16:12:15 +08:00
刘祥超
e79264eefc 检查检查时先检查集群是否已经部署服务,如果没有部署服务,则直接跳过 2022-11-10 12:44:12 +08:00
刘祥超
dd0e26e7bc 访问日志搜索method:XXX和requestMethod:XXX方法 2022-11-09 11:58:50 +08:00
刘祥超
342c4bfbc2 NSRecords增加mxPriority字段 2022-11-06 20:34:57 +08:00
刘祥超
7d2b8fd4c8 保持导出SQL的稳定性 2022-11-06 19:26:23 +08:00
刘祥超
6426622992 优化代码 2022-11-06 19:13:47 +08:00
刘祥超
3d154411de 将指标统计导入到数据库时忽略 transaction deadlock错误 2022-11-06 16:03:33 +08:00
刘祥超
241f41e900 使用版本号来读取节点任务,提升任务同步稳定性 2022-11-06 12:03:11 +08:00
刘祥超
6c3d24d895 修复域名解析--DNS服务商--同步域名时无法解析集群额外附加的CNAME的问题 2022-11-05 19:25:35 +08:00
刘祥超
b2a0204f6b 优化代码 2022-11-05 14:39:40 +08:00
刘祥超
a9d71652b7 带宽相关数据增加百分位 2022-11-04 20:30:53 +08:00
刘祥超
1b6bfb33d6 用户看板增加带宽百分位 2022-11-04 17:39:53 +08:00
刘祥超
aec0d8d681 优化代码 2022-11-04 15:23:02 +08:00
刘祥超
e38871b52d 优化代码 2022-11-03 15:17:59 +08:00
刘祥超
c1b4551dd1 提升数据库升级速度 2022-11-02 16:58:09 +08:00
刘祥超
d479140f87 版本修改为0.5.8 2022-11-02 15:11:27 +08:00
刘祥超
881bb89ac0 准备0.5.7 2022-11-02 10:54:07 +08:00
刘祥超
0e4158f600 优化代码 2022-11-02 10:11:25 +08:00
刘祥超
57bbd77ae5 版本号修改为0.5.7 2022-10-31 19:13:29 +08:00
刘祥超
6906b3094b 增加edge-api reset命令 2022-10-30 20:07:16 +08:00
刘祥超
aec28b5087 集群中自动设置CNAME记录如果已经存在,则跳过 2022-10-28 15:27:52 +08:00
刘祥超
b59ed1f73e 测试环境下申请ACME证书时打印调试日志 2022-10-27 20:12:00 +08:00
刘祥超
2a83f61bdd 优化代码 2022-10-27 18:12:15 +08:00
刘祥超
4f21d60ca4 更新SQL 2022-10-27 11:18:49 +08:00
刘祥超
0a6111b2e5 自动检测本地数据库磁盘是否已满,如果已满,则不再写入访问日志 2022-10-27 10:27:47 +08:00
刘祥超
4b425e1698 节点SSH登录自动使用集群设置 2022-10-26 19:25:07 +08:00
刘祥超
bee7da807b 节点设置中增加“通过IP名单”选项 2022-10-26 10:41:53 +08:00
刘祥超
a906a7db06 提升流量账单精度 2022-10-26 09:38:13 +08:00
刘祥超
ea62cf0ff7 增加用户账单中流量精度 2022-10-25 19:14:31 +08:00
刘祥超
72e0c55f5d IP名单支持模糊查询 2022-10-25 10:15:32 +08:00
刘祥超
3a88f23181 修复域名解析--集群中单节点多IP时无法修改IP的Bug 2022-10-24 16:34:07 +08:00
刘祥超
967c9080fb 更新用户服务可用状态时同时返回状态 2022-10-23 20:12:28 +08:00
刘祥超
f44b9434ad 更新相关库 2022-10-23 19:57:43 +08:00
刘祥超
e21a3c5f8c 节点所属集群删除后,不再接收API请求 2022-10-23 19:56:58 +08:00
刘祥超
88dae56b6c 优化用户服务整体启用和禁用 2022-10-23 16:22:20 +08:00
刘祥超
40c3475306 提升小数数字精度 2022-10-23 11:54:00 +08:00
刘祥超
318c8dd566 优化小数数字格式化 2022-10-23 09:25:20 +08:00
刘祥超
a5f30b1573 修复默认生成的集群没有开启节点和服务DNS同步的Bug 2022-10-23 09:02:45 +08:00
刘祥超
a8ec959c70 优化小数格式化 2022-10-22 10:22:45 +08:00
刘祥超
c393b2f480 更新SQL 2022-10-22 10:22:31 +08:00
刘祥超
548f56f8f0 用户账单增加字段/优化代码 2022-10-21 15:45:22 +08:00
刘祥超
9aa71365b9 安装生成的默认集群默认的DNS设置包含节点和服务自动同步 2022-10-21 14:48:59 +08:00
刘祥超
292fb72a26 优化代码 2022-10-20 22:59:17 +08:00
刘祥超
e5315c3b8d DNSPod和Alidns记录信息增加缓存 2022-10-20 21:47:21 +08:00
刘祥超
a4dd7bb75a 优化DNSPod和Alidns相关代码 2022-10-20 18:06:27 +08:00
刘祥超
53ef0f3fb2 修复访问日志采样率可能会被放大的Bug 2022-10-20 16:12:35 +08:00
刘祥超
6af8bff802 增加操作节点区域相关接口 2022-10-20 15:11:57 +08:00
刘祥超
d849f7440a 更新SQL 2022-10-20 10:42:06 +08:00
刘祥超
425c0ec44c 增加流量包相关表 2022-10-20 10:24:36 +08:00
刘祥超
aad0b01581 修复在域名同步后,无法看到线路等问题的Bug 2022-10-19 19:56:36 +08:00
刘祥超
bc2c3dfa0b 查询DNSPod域名和记录时每页尺寸从100增加到3000 2022-10-17 19:49:36 +08:00
刘祥超
0fe76430c6 实现用户计费方式相关多个接口 2022-10-15 19:16:08 +08:00
刘祥超
979ff4c44e 优化代码 2022-10-14 16:16:42 +08:00
刘祥超
b0b6b5984f 优化代码/删除不需要的代码 2022-10-14 10:03:29 +08:00
刘祥超
5d4da6cccb 查询带宽统计时,自动调整开始和结束日期顺序 2022-10-04 08:55:03 +08:00
刘祥超
912ffa062f 更新SQL 2022-10-03 20:54:18 +08:00
刘祥超
1db4661c75 增加多个服务流量、带宽统计API 2022-10-03 19:28:03 +08:00
刘祥超
f7dd9e3f39 创建通知消息的时候限制内容长度不超过1024长度 2022-10-03 16:07:14 +08:00
刘祥超
0cc74b920e 版本修改为0.5.6 2022-10-01 08:49:32 +08:00
刘祥超
51c3807d01 删除不必要的文件 2022-10-01 07:14:28 +08:00
刘祥超
c2c42ca2b7 优化服务列表返回速度 2022-09-30 13:50:19 +08:00
刘祥超
2a6db6ebfe 边缘节点远程安装文件最小化(从16.xM减少到2.xM) 2022-09-30 10:34:32 +08:00
刘祥超
30d8edbdcf 执行uname和systemctl时增加命令完整路径 2022-09-30 09:40:38 +08:00
刘祥超
177afafe12 完善订单相关表 2022-09-29 10:22:17 +08:00
刘祥超
98765b6e2a 版本调整为v0.5.5 2022-09-28 18:56:47 +08:00
刘祥超
e4e0aab010 阶段性提交 2022-09-28 17:38:52 +08:00
刘祥超
ed87b4e2a9 用户节点版本改为0.5.4 2022-09-28 08:56:40 +08:00
刘祥超
337eb36d25 DNS版本改为0.2.8 2022-09-28 08:16:57 +08:00
刘祥超
c44e40d72d systemd服务增加BEGIN INIT INFO 2022-09-28 08:16:49 +08:00
刘祥超
2e8ba831a1 DNS版本修改为0.2.7.1 2022-09-27 08:06:16 +08:00
刘祥超
a706c2a5a5 将版本修改为0.5.4 2022-09-26 15:17:00 +08:00
刘祥超
093826222a 将版本修改为v0.5.3.1 2022-09-26 12:16:44 +08:00
刘祥超
0d7b487afc 修复开源版本无法编译的问题 2022-09-26 12:16:20 +08:00
刘祥超
8de17b6d9c 只有在数据库用户是root时才执行某些命令 2022-09-26 12:16:08 +08:00
刘祥超
49d217a883 提交EdgeDNS API相关代码 2022-09-26 11:51:45 +08:00
刘祥超
4827555899 启动过程增加多个提示 2022-09-26 11:00:58 +08:00
刘祥超
41f3825ee2 使用并发队列安装和升级数据表/启动时自动调整MySQL变量 2022-09-25 20:34:19 +08:00
刘祥超
096aa153ab 创建缓存策略时默认缓存条件自动支持206 Partial Content,并使用简单条件设置 2022-09-25 10:37:05 +08:00
刘祥超
8f8b611ac1 查询5分钟流量接口增加timeFrom和timeTo参数 2022-09-24 18:55:33 +08:00
刘祥超
08b1c038f1 服务流量接口增加5分钟查询接口 2022-09-24 18:36:27 +08:00
刘祥超
934b10a254 REST 接口接收内容为空时,默认为 {} 2022-09-24 18:35:29 +08:00
刘祥超
4947f13416 更新SQL 2022-09-24 18:34:46 +08:00
刘祥超
84198d5948 优化智能DNS相关代码 2022-09-24 14:07:19 +08:00
刘祥超
d4a04bc798 智能DNS支持应答模式配置 2022-09-23 19:01:18 +08:00
刘祥超
c94b3c26c1 优化代码 2022-09-23 15:25:51 +08:00
刘祥超
5655f89ba6 优化代码 2022-09-23 09:28:19 +08:00
刘祥超
c568ad3e9a 查看访问日志列表时,不查询requestBody和responseBody,防止内容过大 2022-09-22 16:47:36 +08:00
刘祥超
d2f532447d 同步防盗链功能 2022-09-22 16:41:02 +08:00
刘祥超
ec97feab28 增加防盗链功能 2022-09-22 16:33:46 +08:00
刘祥超
316cd36f71 删除集群的时候,同时删除对应的域名解析/集群切换二级域名先删除再添加新的域名解析 2022-09-22 14:09:28 +08:00
刘祥超
702a0f1ecf DNS访问日志创建表已经存在时不提示错误 2022-09-21 20:41:21 +08:00
刘祥超
2d2b7b7bff 优化代码 2022-09-21 15:09:39 +08:00
刘祥超
6f8c5a8e99 修复按日带宽峰值查询错误的Bug 2022-09-20 13:57:45 +08:00
刘祥超
afb35953e7 DNS集群增加时区选项 2022-09-19 17:00:39 +08:00
刘祥超
2a1f78a440 修复一处带宽计算错误 2022-09-19 14:39:22 +08:00
刘祥超
fb084a9f48 优化代码 2022-09-18 12:39:17 +08:00
刘祥超
e7f620d28f 同步域名解析时不再强制要求修复节点问题 2022-09-18 10:41:29 +08:00
刘祥超
2f64d713e8 优化代码/DNS域名增加分页 2022-09-18 10:23:04 +08:00
刘祥超
86bf316468 可以设置是否自动安装nftables 2022-09-17 21:05:18 +08:00
刘祥超
bf320271d4 修复DNSPod只能取前100个域名的问题 2022-09-17 17:16:30 +08:00
刘祥超
150a63fe98 用户端刷新预热缓存超过5分钟自动认为已完成 2022-09-17 16:50:03 +08:00
刘祥超
1d1ff11eb7 优化接口权限 2022-09-17 16:07:37 +08:00
刘祥超
55eecce416 集群增加是否远程启动选项 2022-09-17 15:11:34 +08:00
刘祥超
5f822062da 检查域名是否存在时同时检查泛域名 2022-09-17 11:38:47 +08:00
刘祥超
ee2c253e7d 节点离线时尝试自动通过API节点远程启动 2022-09-17 10:18:00 +08:00
刘祥超
5b0adb4b84 创建集群的时候初始化全局服务配置 2022-09-16 19:34:19 +08:00
刘祥超
8c72540a6e 集群设置中增加服务设置 2022-09-16 18:42:14 +08:00
刘祥超
7f811997a9 证书申请任务不区分管理员 2022-09-16 15:34:03 +08:00
刘祥超
ca94e31451 启动时自动设置binlog过期时间 2022-09-16 10:39:17 +08:00
刘祥超
320d381bd9 集群增加自动同步时钟选项 2022-09-15 15:56:50 +08:00
刘祥超
c8057457cc 更新SQL 2022-09-15 11:48:51 +08:00
刘祥超
c78c4d58ff 用户系统可以切换CDN和智能DNS 2022-09-13 19:04:31 +08:00
刘祥超
d1f8e7e757 增加DNS套餐相关数据 2022-09-13 10:49:58 +08:00
刘祥超
5386b30eba 用户节点版本修改为0.5.0 2022-09-13 10:49:17 +08:00
刘祥超
8ceb1334cd 调整GRPC参数 2022-09-12 22:03:06 +08:00
刘祥超
023e563de1 调整GRPC参数 2022-09-12 22:00:12 +08:00
刘祥超
39e6d11d71 优化代码 2022-09-11 14:41:06 +08:00
刘祥超
a5a9117ce0 增强系统配置接口的权限检查 2022-09-10 17:00:54 +08:00
刘祥超
e95b0bd9a6 实现DNS域名验证 2022-09-10 16:13:21 +08:00
刘祥超
9699a9adad 自动调整写入单次写入访问日志数量 2022-09-09 21:15:03 +08:00
刘祥超
67729abd13 修复多个日志数据库节点写入NS日志时分布不平均的问题 2022-09-09 21:06:43 +08:00
刘祥超
4c7ebce97a 修复多个日志数据库节点分布不平均的问题 2022-09-09 20:37:45 +08:00
刘祥超
44e7ce9f79 自动升级NS域名状态为verified 2022-09-09 15:25:32 +08:00
刘祥超
3468fcf8a6 DNS版本修改为0.2.7 2022-09-09 14:29:29 +08:00
刘祥超
3522c22a28 修复无法一个常量错误 2022-09-09 10:39:28 +08:00
刘祥超
333a9c6611 修改管理界面设置中的时区时同时也会应用到API节点/API节点默认时区为Asia/Shanghai 2022-09-09 10:28:44 +08:00
刘祥超
e3426a84e2 域名解析增加EdgeDNS API 2022-09-08 19:36:32 +08:00
刘祥超
a25b0e6c9d 创建集群的时候可以设置DNS记录的默认TTL 2022-09-08 11:02:26 +08:00
刘祥超
c271cadabd 计算区域监控终端低版本数量时忽略已禁用的终端 2022-09-08 10:14:02 +08:00
刘祥超
428bb7eb0f 访问日志支持中文域名搜索 2022-09-07 17:07:46 +08:00
刘祥超
6ae9f447b6 增加启动过程标记 2022-09-07 15:57:31 +08:00
刘祥超
7cc503b698 将版本修改为0.5.3 2022-09-06 09:23:28 +08:00
刘祥超
c66e28cb9d 减少带宽统计显示的空档期 2022-09-05 16:04:34 +08:00
刘祥超
e5109b24d4 版本修改为0.5.2.1 2022-09-05 16:02:55 +08:00
刘祥超
695b8482de 将版本修改为0.5.3 2022-09-05 11:03:12 +08:00
刘祥超
d0b908bcaa 自动添加firewalld使用异步 2022-09-04 06:36:22 +08:00
刘祥超
3de25d4fe1 优化代码 2022-09-03 22:23:16 +08:00
刘祥超
07194855bf 优化代码 2022-09-03 22:03:22 +08:00
刘祥超
d0f1eb13ee 优化节点活跃检测机制 2022-09-03 12:43:06 +08:00
刘祥超
a0930bfd74 远程安装节点出错时打印stderr 2022-08-30 11:40:01 +08:00
刘祥超
08cff8affc 可以通过用户API修改鉴权 2022-08-30 11:23:35 +08:00
刘祥超
02132e9262 用户系统也可以申请ACME证书 2022-08-28 20:02:13 +08:00
刘祥超
61b6a49885 增加修改全体用户功能API 2022-08-28 17:01:09 +08:00
刘祥超
896e54ebe8 提供按小时、按天查询带宽峰值的API 2022-08-28 15:56:16 +08:00
刘祥超
1b36bad60a 指标统计使用事务 2022-08-27 18:50:42 +08:00
刘祥超
fc14800d70 服务列表带宽使用新的算法 2022-08-27 18:39:00 +08:00
刘祥超
fa61f277e4 服务访问日志改成通过事务写入,以提升写入速度 2022-08-27 14:57:47 +08:00
刘祥超
9117309472 可以修改服务的CNAME 2022-08-26 19:51:21 +08:00
刘祥超
6bb2977d59 Ln节点可以指定访问IP 2022-08-25 20:37:10 +08:00
刘祥超
df9dce76cb 集群DNS设置中增加”包含Ln节点“选项 2022-08-25 19:18:30 +08:00
刘祥超
4cb9c85a1c 节点运行日志可以按照节点ID设置为已读 2022-08-25 18:26:52 +08:00
刘祥超
f4f5389ffb 请求限制API支持用户调用 2022-08-25 15:35:55 +08:00
刘祥超
5d336eb77d 优化代码 2022-08-23 21:42:05 +08:00
刘祥超
c552eb3b0e IP库增加制品管理/统计中相关区域名称可以显示别名 2022-08-23 19:40:17 +08:00
刘祥超
455952e9e4 提交SQL 2022-08-22 15:12:20 +08:00
刘祥超
7132401c7f NS节点基本的DDoS防护 2022-08-22 15:11:22 +08:00
刘祥超
a4dddfb139 优化代码 2022-08-22 11:02:16 +08:00
刘祥超
7ef32bad97 IP库改为手动初始化 2022-08-21 23:09:59 +08:00
刘祥超
732513a644 用户节点版本修改为0.4.1 2022-08-21 20:50:00 +08:00
刘祥超
756cf4a9ae 初步完成新版IP库 2022-08-21 20:38:34 +08:00
刘祥超
a15a630265 更新SQL 2022-08-20 19:57:25 +08:00
刘祥超
3fab1b8294 DNS节点版本号改为0.2.6 2022-08-20 15:27:02 +08:00
刘祥超
215635f429 版本修改为0.5.2 2022-08-17 18:58:20 +08:00
刘祥超
dbb1ae180b 版本修改为0.5.1 2022-08-15 19:38:40 +08:00
刘祥超
e8d4d01d85 改进一处日志 2022-08-15 15:17:09 +08:00
刘祥超
6593989a84 修复日志内容可能过长而无法存入数据库的问题 2022-08-15 15:05:47 +08:00
刘祥超
004e640321 修复升级数据库时主键可能冲突的问题 2022-08-15 00:02:38 +08:00
刘祥超
7ad315ae4b IP库管理阶段性提交(未完成) 2022-08-14 20:03:01 +08:00
刘祥超
ba938e5361 新版IP库管理阶段性提交(未完成) 2022-08-13 23:55:48 +08:00
刘祥超
9ddf02a0e6 更新TeaGo 2022-08-11 11:52:35 +08:00
刘祥超
ebcbd5690d 删除不必要的文件 2022-08-09 18:30:23 +08:00
刘祥超
bbca766fa4 删除不必要的文件 2022-08-09 17:35:35 +08:00
刘祥超
99c7819d3a 更新SQL 2022-08-07 19:04:16 +08:00
刘祥超
08bb3e66f8 修改版本号为0.5.0 2022-08-07 19:02:19 +08:00
刘祥超
9159820742 只有发送过离线通知的节点才会发送恢复在线通知 2022-08-07 17:28:54 +08:00
刘祥超
1a565b2ebb 优化代码/启用的日志策略排在最前面 2022-08-07 15:10:05 +08:00
刘祥超
98847c53ea 更新SQL 2022-08-06 20:31:28 +08:00
刘祥超
14bafc8f20 优化代码 2022-08-06 20:28:32 +08:00
刘祥超
58a5bd0092 优化代码 2022-08-05 21:05:34 +08:00
刘祥超
4f1ce52f6a 优化代码 2022-08-05 19:25:31 +08:00
刘祥超
14ba7f6899 优化访问日志策略测试时的失败提示 2022-08-05 19:11:21 +08:00
刘祥超
e582e37c06 优化代码 2022-08-05 14:45:56 +08:00
刘祥超
6a3fa9f0ca 删除不必要的文件 2022-08-05 14:40:42 +08:00
刘祥超
e0a9965fed 简化API 2022-08-04 19:36:25 +08:00
刘祥超
481fa8cd2d 增加查找使用某个证书的NS集群数量的API 2022-08-04 16:25:09 +08:00
刘祥超
95349dc457 允许用户标记上传文件状态 2022-08-04 16:01:07 +08:00
刘祥超
fc839f96d2 优化代码 2022-08-04 15:12:39 +08:00
刘祥超
0414cc02e8 优化代码 2022-08-04 11:41:42 +08:00
刘祥超
b8babaae39 更新SQL 2022-08-03 10:45:09 +08:00
刘祥超
285ce1b312 延长节点执行任务超时时间 2022-08-01 18:57:19 +08:00
刘祥超
c309da81ae 服务带宽API增加按月、按日查询接口 2022-08-01 15:40:57 +08:00
刘祥超
c325fde52b 更新SQL 2022-08-01 11:01:51 +08:00
刘祥超
0f69b45d25 修改用户节点版本号为0.4.0 2022-08-01 11:00:52 +08:00
刘祥超
e02084ba5d 增加用户订单相关表 2022-07-31 19:56:56 +08:00
刘祥超
642b23dbb7 优化代码 2022-07-30 16:28:28 +08:00
刘祥超
b1dc385c87 自动转换用户提交的域名为小写 2022-07-30 16:25:16 +08:00
刘祥超
89a69e3165 删除集群的时候同时删除相关节点运行日志 2022-07-28 09:47:01 +08:00
刘祥超
530954dd6c EdgeDNS:访问日志增加集群和记录类型筛选 2022-07-27 20:19:29 +08:00
刘祥超
33635f7a1b 智能DNS支持自定义端口 2022-07-27 16:56:17 +08:00
刘祥超
8ac964e805 优化远程安装程序 2022-07-27 08:35:15 +08:00
刘祥超
a1519baf0f 远程升级节点时,如果老的文件不存在,则不提示 2022-07-26 20:10:50 +08:00
刘祥超
e6e32a39bb 修改DNS节点版本为0.2.5 2022-07-26 11:15:22 +08:00
刘祥超
d828b7f8a4 修改版本号为0.4.11 2022-07-26 08:57:48 +08:00
刘祥超
07b377c2fb 用户状态发生变化时,同步服务状态 2022-07-24 17:13:05 +08:00
刘祥超
33a3795773 用户增加OTP认证设置 2022-07-24 16:14:56 +08:00
刘祥超
bddb3cae96 用户列表中显示实名审核状态 2022-07-24 11:57:42 +08:00
刘祥超
6a525c2b82 相关接口增加实名认证状态字段 2022-07-24 11:28:59 +08:00
刘祥超
3a137c1c3f 提交SQL 2022-07-24 10:08:40 +08:00
刘祥超
19867568a9 可以重置用户实名认证状态 2022-07-24 10:08:08 +08:00
刘祥超
c2600b911b 优化代码/实现基础的实名认证功能 2022-07-24 09:56:27 +08:00
刘祥超
fe511ae7e5 优化代码 2022-07-22 15:05:30 +08:00
刘祥超
876c631c85 优化代码 2022-07-22 14:35:17 +08:00
刘祥超
a4cc138ef3 API节点自动生成实例代号,用来外界查询多个API节点实例是否为同一个 2022-07-21 19:21:11 +08:00
刘祥超
3f9a7a8a49 升级gosock 2022-07-21 16:30:19 +08:00
刘祥超
09d8ef00c2 API节点状态中增加主程序位置信息 2022-07-21 15:23:08 +08:00
刘祥超
b4f77ddc63 修改版本为v0.4.10 2022-07-20 18:14:27 +08:00
刘祥超
6a9045ba44 提交SQL 2022-07-18 09:13:16 +08:00
刘祥超
bc7ee19962 优化代码 2022-07-17 17:08:28 +08:00
刘祥超
2484cd4ae6 可以通过ID查找服务 2022-07-17 11:43:23 +08:00
刘祥超
57b8a96c6d 增加用户身份认证相关接口 2022-07-17 11:00:14 +08:00
刘祥超
6c8ab7cb94 集群DNS设置增加允许通过CNAME访问网站服务选项 2022-07-14 09:49:09 +08:00
刘祥超
b2c2bd247b 更新SQL 2022-07-12 14:22:20 +08:00
刘祥超
29d9e6db81 域名解析支持DNS.COM(商业版) 2022-07-11 11:51:49 +08:00
刘祥超
24fcb48c75 删除节点的时候同时也删除相关的运行日志 2022-07-07 20:31:12 +08:00
刘祥超
4911198fe7 安全设置中增加禁止搜索引擎、禁止爬虫、允许访问的域名等选项 2022-07-07 19:58:50 +08:00
刘祥超
c2af796992 服务看板增加峰值带宽数据 2022-07-07 17:11:49 +08:00
刘祥超
51c8572e53 用户界面设置中增加流量、带宽相关设置 2022-07-07 12:40:05 +08:00
刘祥超
c15cc6d75c 增加按用户统计带宽/修改其他相关代码 2022-07-07 09:18:08 +08:00
刘祥超
c0a99a4ba3 升级Go版本为1.18 2022-07-06 16:13:52 +08:00
刘祥超
62e26bed5a 增加服务带宽统计 2022-07-05 20:08:58 +08:00
刘祥超
9e68a2915c 增加UAM(5秒盾)集群设置 2022-07-03 22:10:46 +08:00
刘祥超
5b809fda1f 反向代理设置中增加移除回源主机名端口功能 2022-06-30 12:12:41 +08:00
刘祥超
88b782940a 提交SQL 2022-06-29 22:17:32 +08:00
刘祥超
85c596644d 实现源站端口跟随功能 2022-06-29 21:55:57 +08:00
刘祥超
ffa2d884bd 移除386、mips等不常用节点安装文件 2022-06-29 17:11:13 +08:00
刘祥超
813ed18610 优化编译脚本 2022-06-29 16:57:46 +08:00
刘祥超
564b11eae7 优化编译脚本 2022-06-29 14:52:11 +08:00
刘祥超
d2af0307b9 将DNS版本改为0.2.4 2022-06-28 20:31:59 +08:00
刘祥超
4fa6b03238 对于小内存(不大于2G),缩短服务统计导入数据库的时间 2022-06-25 20:57:03 +08:00
刘祥超
4bda78aa8c 限制节点自动升级时的速度和并发数 2022-06-25 20:36:31 +08:00
刘祥超
6002cc96d9 增加修改服务所在分组API 2022-06-25 19:21:46 +08:00
刘祥超
56c09f5be7 指标数据写入改成队列执行 2022-06-22 21:51:44 +08:00
刘祥超
e1fb8e4c74 删除某个IP时更新版本 2022-06-22 18:59:27 +08:00
刘祥超
14f055ce7c fix typo 2022-06-22 14:00:56 +08:00
刘祥超
7b7f2b0a00 修改版本号为v0.4.9 2022-06-20 16:00:27 +08:00
刘祥超
75662586fa 修改相关节点版本 2022-06-20 09:34:40 +08:00
刘祥超
c024331fa0 增加统计服务某日、某月流量API 2022-06-18 14:26:43 +08:00
刘祥超
b01ea79c5c 升级IP名单权限判断逻辑 2022-06-15 19:22:33 +08:00
刘祥超
d59f80b3ec 优化缓存任务Key状态增加执行中状态 2022-06-15 15:18:18 +08:00
刘祥超
b87e48c1f9 城市API增加省份信息 2022-06-14 17:37:42 +08:00
刘祥超
0e7a7f168f 访问日志查询增加requestPath:/hello、proto:HTTP/1.1、scheme:http等语法 2022-06-12 20:30:28 +08:00
刘祥超
2bbc09d3af 优化syslog提示、优化其他代码 2022-06-08 19:55:06 +08:00
刘祥超
97b50fab28 删除EdgePlus 2022-06-08 15:14:30 +08:00
刘祥超
83c867cb65 API节点启动失败后记录相关问题和处理建议 2022-06-08 15:13:24 +08:00
刘祥超
3c4b7ca57b 节点状态中增加时间戳字段 2022-06-06 19:39:08 +08:00
刘祥超
a2f98d2f25 可以设置用户每天执行缓存任务的额度 2022-06-05 21:15:28 +08:00
刘祥超
71677a8638 增加刷新、预热缓存任务管理 2022-06-05 17:13:56 +08:00
刘祥超
95a2187f95 优化API/修改用户集群不影响套餐服务 2022-05-25 11:44:18 +08:00
刘祥超
86bec813bf fix typo 2022-05-22 11:48:31 +08:00
刘祥超
0d7bd6f04e 增加LICENSE和README 2022-05-22 11:36:54 +08:00
刘祥超
d7117209b2 删除不过期IP时不立即删除,以等待节点完成同步 2022-05-21 22:06:04 +08:00
刘祥超
7a340ac68b 新创建WAF时增加默认选项 2022-05-21 18:58:03 +08:00
刘祥超
353b1b4ad1 WAF策略中增加验证码相关定制设置 2022-05-20 22:07:23 +08:00
刘祥超
fdac8beb40 健康检查增加是否记录访问日志选项 2022-05-19 17:14:32 +08:00
刘祥超
f098723a41 实现基础的DDoS防护 2022-05-18 21:02:53 +08:00
刘祥超
6ded627903 增加通过IP删除IP名单中IP参数 2022-05-10 15:11:48 +08:00
刘祥超
ed2d5ee5cc fix typo 2022-05-08 19:33:17 +08:00
刘祥超
c677368482 集群节点列表可以使用“未分组”筛选 2022-05-08 19:33:10 +08:00
刘祥超
51ccd614a7 忽略部分MySQL 1213错误 2022-05-08 00:24:22 +08:00
刘祥超
9be7f61b8c 阿里云DNS增加区域ID 2022-05-07 21:00:07 +08:00
刘祥超
14ad97c937 DNS服务商增加厂家筛选 2022-05-07 20:41:53 +08:00
刘祥超
94609d8ef4 EdgeUser版本升级为0.3.4 2022-05-05 18:56:35 +08:00
刘祥超
e34e38bcb2 Update sql.go 2022-05-04 20:35:38 +08:00
刘祥超
b10d9fe842 路由规则可以单独设置UAM(仅企业版可用) 2022-05-04 20:32:34 +08:00
刘祥超
992e725378 节点增加DNS解析库类型设置 2022-05-04 16:40:34 +08:00
刘祥超
346de7ca7a 版本修改为0.4.8 2022-04-25 11:11:43 +08:00
刘祥超
b212e124c2 统计看板中合并相同Key的指标数据 2022-04-24 15:24:40 +08:00
刘祥超
ee2781fe65 优化代码 2022-04-23 13:26:15 +08:00
刘祥超
89c1edc9ee 多个API节点时选择一个作为主节点/优化任务相关代码 2022-04-23 12:32:30 +08:00
刘祥超
773f3e1a7e 修复无法使用倒序分表查询日志的问题 2022-04-22 22:23:07 +08:00
刘祥超
e6f6f4dcc2 集群概要信息中增加系统服务状态 2022-04-22 22:04:29 +08:00
刘祥超
174946aa4d 修复集群健康检查无法自动上下线IP地址的Bug 2022-04-22 21:53:38 +08:00
刘祥超
93c594a785 更新SQL 2022-04-22 17:16:19 +08:00
刘祥超
2aea527dff 访问日志策略增加只记录WAF相关访问日志选项 2022-04-22 17:13:59 +08:00
刘祥超
fa30015ca5 增加WAF策略日志设置 2022-04-21 20:00:56 +08:00
刘祥超
d06c8cebf5 IP列表增加名单类型筛选 2022-04-21 15:09:18 +08:00
刘祥超
e4ef2e8253 WAF策略增加日志配置(暂未开放) /修复通过IP可能无法查询到访问日志的Bug 2022-04-21 09:41:04 +08:00
刘祥超
665aa06cc7 优化代码 2022-04-19 19:48:37 +08:00
刘祥超
88dfda82d6 Linux下自动添加端口到Firewalld 2022-04-19 19:35:50 +08:00
刘祥超
243463df83 优化代码 2022-04-19 12:58:00 +08:00
刘祥超
2dc1bfcb55 创建和修改证书的时候检查时间 2022-04-19 11:09:42 +08:00
刘祥超
e6f7cafe7e 当修改集群主域名和DNS子域名时,自动删除旧的相关域名 2022-04-18 21:00:40 +08:00
刘祥超
26fe3558f4 如果服务变更集群前后域名ID一致,则不执行删除操作 2022-04-18 18:35:57 +08:00
刘祥超
ea09ef3d91 服务切换集群后,直接删除老的域名记录 2022-04-18 18:21:29 +08:00
刘祥超
c4ca2521ee 单个服务切换集群时可以选择是否保留配置 2022-04-18 17:18:00 +08:00
刘祥超
db5cdd2957 修复没有日志数据库时无法进行分区查询的Bug 2022-04-17 16:50:38 +08:00
刘祥超
0f483b98ec 删除一处调试信息 2022-04-17 16:24:27 +08:00
刘祥超
0fe51abeb1 访问日志可以使用分表查询 2022-04-17 16:18:53 +08:00
刘祥超
db6b7f57bb 修复看板中统计数据可能不显示的问题 2022-04-16 22:22:05 +08:00
刘祥超
663ead19e4 优化节点排序 2022-04-16 14:45:54 +08:00
刘祥超
bc8adb663a 服务列表增加下行带宽 2022-04-15 12:14:59 +08:00
刘祥超
08b5dd7531 判断节点数量时增加集群状态检查 2022-04-14 22:00:47 +08:00
刘祥超
8177f3d7e4 可以通过groupId=-1查询到未分组的服务 2022-04-14 16:58:21 +08:00
刘祥超
bb5252caf6 edgeServers增加plainServerNames字段 2022-04-14 16:44:00 +08:00
刘祥超
bd4a47d2a1 服务列表中分组信息中增加UserId字段 2022-04-13 15:01:45 +08:00
刘祥超
300be4e936 预估同时间流量的时候刻度改为10分钟 2022-04-10 21:57:26 +08:00
刘祥超
c9dbfb79a7 按天统计流量接口可以预估某日同时间流量 2022-04-10 21:25:24 +08:00
刘祥超
589ae124f1 修复访问日志可能显示重复的问题 2022-04-10 18:21:52 +08:00
刘祥超
d72b440406 修改DNS版本为0.2.2 2022-04-10 16:02:21 +08:00
刘祥超
63e4e7cf9f 优化本地日志 2022-04-10 15:57:36 +08:00
刘祥超
ad416dddec 增加两个数据库相关调试命令 2022-04-08 15:09:33 +08:00
刘祥超
4e18129c6c 更新TeaGo 2022-04-08 14:57:20 +08:00
刘祥超
c03d9f1880 优化数据库相关代码 2022-04-08 14:15:45 +08:00
刘祥超
fe448e6556 增加当日统计接口 2022-04-07 19:46:50 +08:00
刘祥超
adcb33fce4 优化节点列表 2022-04-07 18:31:38 +08:00
刘祥超
e58c3774b6 优化代码 2022-04-05 19:32:35 +08:00
刘祥超
cd7e01c2f0 商业版支持L2节点 2022-04-04 12:08:08 +08:00
刘祥超
d884777a55 增加单元测试 2022-04-02 11:52:42 +08:00
刘祥超
c48aba1c99 更新TeaGo 2022-04-02 11:52:11 +08:00
刘祥超
8cc0faf1d7 集群可以单独设置WebP策略 2022-04-01 16:42:23 +08:00
刘祥超
9851b1a146 集群可以设置WebP策略 2022-04-01 16:18:54 +08:00
刘祥超
36162c603d 看板:只有指标数据不为空时才缓存 2022-04-01 10:00:10 +08:00
刘祥超
cc2782584e 自定义页面支持用户操作 2022-03-31 15:30:04 +08:00
刘祥超
1c1e82ee38 指标统计数据分表 2022-03-30 15:35:42 +08:00
刘祥超
4b3a9cedfa 可以用域名搜索DNS账号 2022-03-30 11:15:38 +08:00
刘祥超
e9497ee65d DNSPod支持国际站 2022-03-30 10:56:22 +08:00
刘祥超
29fae55dc6 IP列表可以使用级别筛选 2022-03-30 09:39:43 +08:00
刘祥超
54fdf3b762 支持DNSPod国际版(需要测试) 2022-03-30 09:12:42 +08:00
刘祥超
f609008984 商业版增加UAM功能 2022-03-29 21:24:31 +08:00
刘祥超
f9b6838dc6 写入指标统计数据时忽略MySQL 1213错误 2022-03-29 20:01:49 +08:00
刘祥超
6d2ecb9af3 对统计指标进行分表 2022-03-28 16:25:16 +08:00
刘祥超
7c4a01137b 数据库管理中列出更多可手动清理的数据表 2022-03-28 11:19:50 +08:00
刘祥超
418c15f79f 数据库管理中列出更多可手动清理的数据表 2022-03-28 11:12:49 +08:00
刘祥超
d13176b8a5 可以自行设定指标数据保留时间 2022-03-28 09:37:28 +08:00
刘祥超
b2752ddd5a 优化看板打开速度 2022-03-27 16:39:54 +08:00
刘祥超
7aea2fd48c 访问日志关键词支持完整的URL/优化Like语句 2022-03-27 12:22:47 +08:00
刘祥超
803f11659c 优化代码 2022-03-26 22:04:16 +08:00
刘祥超
073926ff67 修改用户平台版本为0.3.3 2022-03-26 22:04:10 +08:00
刘祥超
65b4832c94 增加脚本相关表 2022-03-25 14:11:17 +08:00
刘祥超
5f793f1f76 IP找不到不再提示错误 2022-03-24 21:41:14 +08:00
刘祥超
0ce1df25bc 可以修复单页或者全部服务日志 2022-03-23 17:31:26 +08:00
刘祥超
06ad9cc63b 版本号改为0.4.7 2022-03-23 14:45:37 +08:00
刘祥超
57247d0f5b 修复访问日志可能无法写入当天数据表的Bug 2022-03-23 10:58:54 +08:00
刘祥超
813267c50f 修复访问日志表可能无法自动创建的Bug 2022-03-23 10:05:42 +08:00
刘祥超
b8078e73b2 更改版本为v0.4.6 2022-03-23 10:03:57 +08:00
刘祥超
f958c57984 优化代码 2022-03-22 22:11:32 +08:00
刘祥超
30bfbdc01e 优化代码 2022-03-22 21:45:07 +08:00
刘祥超
314362c24d 优化代码 2022-03-22 19:30:30 +08:00
刘祥超
8582715be7 字段中的blob和JSON类型映射为[]byte和dbs.JSON 2022-03-21 21:39:36 +08:00
刘祥超
74e585263c 升级TeaGo 2022-03-21 19:46:14 +08:00
刘祥超
35cb10fffe 自动升级一个SQL注入规则 2022-03-20 11:54:17 +08:00
刘祥超
7c84794ac4 GRPC通讯支持gzip压缩 2022-03-20 11:28:45 +08:00
刘祥超
5859f5df91 更新相关库 2022-03-20 10:50:34 +08:00
刘祥超
e370944233 OCSP支持过期时间 2022-03-18 20:21:24 +08:00
刘祥超
a23a2ed826 获取OCSP更新列表兼容MySQL 5.7.22以下版本 2022-03-18 18:28:28 +08:00
刘祥超
3f9c250dff 动态更新OCSP,防止过期 2022-03-18 17:08:51 +08:00
刘祥超
06c9c9403b WAF统计图表中把page归为拦截,把tag归为记录 2022-03-17 16:49:46 +08:00
刘祥超
0e580a0890 源站支持自定义回源主机名 2022-03-17 15:48:40 +08:00
刘祥超
7aba622403 增加置顶集群功能 2022-03-17 11:12:46 +08:00
刘祥超
72cc8389e6 修复服务配置可能无法更新的Bug 2022-03-16 21:42:56 +08:00
刘祥超
39cf470b0c 上传SQL 2022-03-16 17:15:54 +08:00
刘祥超
6e71dda713 节点可以单独设置缓存目录 2022-03-16 15:23:58 +08:00
刘祥超
10909e28c8 修复集群配置/节点配置无法同步的Bug 2022-03-16 15:23:36 +08:00
刘祥超
6bd6e350b9 WAF全局看板中拦截类型只显示当天的统计/合并同名规则分组统计 2022-03-14 16:44:56 +08:00
刘祥超
d2ae7834ae 增加SQL子版本 2022-03-14 16:06:25 +08:00
刘祥超
c1ffd25c8b 上传SQL 2022-03-14 15:43:24 +08:00
刘祥超
931e55162b 缓存策略可以使用存储类型筛选 2022-03-14 15:42:48 +08:00
刘祥超
fa2bac6d1d 实现源站跟随功能 2022-03-14 15:42:45 +08:00
刘祥超
2f7b7240dd 增加证书OCSP错误日志管理 2022-03-11 20:27:53 +08:00
刘祥超
da67e726a2 优化代码 2022-03-11 17:39:08 +08:00
刘祥超
6cb5529c3f 证书ocsp获取失败时,报告错误 2022-03-11 13:02:53 +08:00
刘祥超
473d0d9439 修改OCSP自动更新的间隔时间和条数 2022-03-11 10:00:53 +08:00
刘祥超
bf0db231fc 上传sql 2022-03-10 21:33:08 +08:00
刘祥超
5644906b77 HTTP DNS QueryRecord动作支持返回null 2022-03-10 19:45:03 +08:00
刘祥超
3e32fe8e10 HTTP DNS请求时增加Content-Type 2022-03-10 19:13:53 +08:00
刘祥超
8459f106e9 域名操作错误时显示具体的域名、记录信息等 2022-03-10 17:42:23 +08:00
刘祥超
b768bbce5d 修改用户平台版本为0.3.2 2022-03-10 16:05:21 +08:00
刘祥超
9e7beb39c0 修复用户可能无法添加黑白名单IP的Bug 2022-03-10 15:59:00 +08:00
刘祥超
94287f5857 增加OCSP Stapling功能 2022-03-10 11:54:35 +08:00
刘祥超
60690dfd01 使用IPv6的IP搜索访问日志时自动去掉[] 2022-03-09 11:10:56 +08:00
刘祥超
fb00a7931e 支持使用小时筛选访问日志 2022-03-09 11:00:54 +08:00
刘祥超
42a6494bde 增加对访问日志自动分表配置 2022-03-09 10:01:24 +08:00
刘祥超
85a46a9827 自动对访问日志进行分表 2022-03-08 19:55:39 +08:00
刘祥超
088636553c 修复审计日志无法自动清理的Bug 2022-03-08 09:53:10 +08:00
刘祥超
95de3b12e2 缓存一些看板数据,以加快看板打开速度 2022-03-07 11:45:58 +08:00
刘祥超
b66b8d198a 更新相关库 2022-03-07 09:58:17 +08:00
刘祥超
e968a79886 更新TeaGo 2022-03-04 15:08:56 +08:00
刘祥超
4fc5d5b549 升级相关依赖库 2022-03-04 12:31:44 +08:00
刘祥超
77606709b3 增加是否同步写入压缩缓存设置 2022-02-24 20:11:53 +08:00
刘祥超
3529ceefcd 修改版本为0.4.5 2022-02-24 19:25:31 +08:00
刘祥超
7e851f07b1 修改版本为0.4.4 2022-02-23 14:49:12 +08:00
763 changed files with 33040 additions and 22870 deletions

3
.gitignore vendored
View File

@@ -1,2 +1,3 @@
*_plus.go
*-plus.sh
*-plus.sh
*_plus_test.go

29
LICENSE Normal file
View File

@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2020, LiuXiangChao
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. 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.
3. Neither the name of the copyright holder 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 HOLDER 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.

1
README.md Normal file
View File

@@ -0,0 +1 @@
GoEdge API节点源码

View File

@@ -1,76 +1,76 @@
#!/usr/bin/env bash
function build() {
ROOT=$(dirname $0)
ROOT=$(dirname "$0")
NAME="edge-api"
DIST=$ROOT/"../dist/${NAME}"
OS=${1}
ARCH=${2}
TAG=${3}
NODE_ARCHITECTS=("amd64" "386" "arm64" "mips64" "mips64le")
NODE_ARCHITECTS=("amd64" "arm64")
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
if [ -z "$TAG" ]; then
TAG="community"
fi
VERSION=$(lookup-version $ROOT/../internal/const/const.go)
VERSION=$(lookup-version "$ROOT"/../internal/const/const.go)
ZIP="${NAME}-${OS}-${ARCH}-${TAG}-v${VERSION}.zip"
# build edge-node
NodeVersion=$(lookup-version $ROOT"/../../EdgeNode/internal/const/const.go")
NodeVersion=$(lookup-version "$ROOT""/../../EdgeNode/internal/const/const.go")
echo "building edge-node v${NodeVersion} ..."
EDGE_NODE_BUILD_SCRIPT=$ROOT"/../../EdgeNode/build/build.sh"
if [ ! -f $EDGE_NODE_BUILD_SCRIPT ]; then
if [ ! -f "$EDGE_NODE_BUILD_SCRIPT" ]; then
echo "unable to find edge-node build script 'EdgeNode/build/build.sh'"
exit
fi
cd $ROOT"/../../EdgeNode/build"
cd "$ROOT""/../../EdgeNode/build" || exit
echo "=============================="
for arch in "${NODE_ARCHITECTS[@]}"; do
if [ ! -f $ROOT"/../../EdgeNode/dist/edge-node-linux-${arch}-${TAG}-v${NodeVersion}.zip" ]; then
./build.sh linux $arch $TAG
if [ ! -f "$ROOT""/../../EdgeNode/dist/edge-node-linux-${arch}-${TAG}-v${NodeVersion}.zip" ]; then
./build.sh linux "$arch" $TAG
else
echo "use built node linux/$arch/v${NodeVersion}"
fi
done
echo "=============================="
cd -
cd - || exit
rm -f $ROOT/deploy/*.zip
rm -f "$ROOT"/deploy/*.zip
for arch in "${NODE_ARCHITECTS[@]}"; do
cp $ROOT"/../../EdgeNode/dist/edge-node-linux-${arch}-${TAG}-v${NodeVersion}.zip" $ROOT/deploy/edge-node-linux-${arch}-v${NodeVersion}.zip
cp "$ROOT""/../../EdgeNode/dist/edge-node-linux-${arch}-${TAG}-v${NodeVersion}.zip" "$ROOT"/deploy/edge-node-linux-"${arch}"-v"${NodeVersion}".zip
done
# build edge-dns
if [ "$TAG" = "plus" ]; then
DNS_ROOT=$ROOT"/../../EdgeDNS"
if [ -d $DNS_ROOT ]; then
DNSNodeVersion=$(lookup-version $ROOT"/../../EdgeDNS/internal/const/const.go")
if [ -d "$DNS_ROOT" ]; then
DNSNodeVersion=$(lookup-version "$ROOT""/../../EdgeDNS/internal/const/const.go")
echo "building edge-dns ${DNSNodeVersion} ..."
EDGE_DNS_NODE_BUILD_SCRIPT=$ROOT"/../../EdgeDNS/build/build.sh"
if [ ! -f $EDGE_DNS_NODE_BUILD_SCRIPT ]; then
if [ ! -f "$EDGE_DNS_NODE_BUILD_SCRIPT" ]; then
echo "unable to find edge-dns build script 'EdgeDNS/build/build.sh'"
exit
fi
cd $ROOT"/../../EdgeDNS/build"
cd "$ROOT""/../../EdgeDNS/build" || exit
echo "=============================="
architects=("amd64")
architects=("amd64" "arm64")
for arch in "${architects[@]}"; do
./build.sh linux $arch $TAG
./build.sh linux "$arch" $TAG
done
echo "=============================="
cd -
cd - || exit
for arch in "${architects[@]}"; do
cp $ROOT"/../../EdgeDNS/dist/edge-dns-linux-${arch}-v${DNSNodeVersion}.zip" $ROOT/deploy/edge-dns-linux-${arch}-v${DNSNodeVersion}.zip
cp "$ROOT""/../../EdgeDNS/dist/edge-dns-linux-${arch}-v${DNSNodeVersion}.zip" "$ROOT"/deploy/edge-dns-linux-"${arch}"-v"${DNSNodeVersion}".zip
done
fi
fi
@@ -78,48 +78,48 @@ function build() {
# build sql
if [ $TAG = "plus" ]; then
echo "building sql ..."
${ROOT}/sql.sh
"${ROOT}"/sql.sh
fi
# 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
mkdir "$DIST"/data
fi
cp $ROOT/configs/api.template.yaml $DIST/configs/
cp $ROOT/configs/db.template.yaml $DIST/configs/
cp -R $ROOT/deploy $DIST/
rm -f $dist/deploy/.gitignore
cp -R $ROOT/installers $DIST/
cp -R $ROOT/resources $DIST/
rm -f $DIST/resources/ipdata/ip2region/global_region.csv
rm -f $DIST/resources/ipdata/ip2region/ip.merge.txt
cp "$ROOT"/configs/api.template.yaml "$DIST"/configs/
cp "$ROOT"/configs/db.template.yaml "$DIST"/configs/
cp -R "$ROOT"/deploy "$DIST/"
rm -f "$DIST"/deploy/.gitignore
cp -R "$ROOT"/installers "$DIST"/
# building edge installer
echo "building node installer ..."
architects=("amd64" "386" "arm64")
architects=("amd64" "arm64")
for arch in "${architects[@]}"; do
# TODO support arm, mips ...
env GOOS=linux GOARCH=${arch} go build -tags $TAG --ldflags="-s -w" -o $ROOT/installers/edge-installer-helper-linux-${arch} $ROOT/../cmd/installer-helper/main.go
env GOOS=linux GOARCH="${arch}" go build -trimpath -tags $TAG --ldflags="-s -w" -o "$ROOT"/installers/edge-installer-helper-linux-"${arch}" "$ROOT"/../cmd/installer-helper/main.go
done
# building edge dns installer
echo "building dns node installer ..."
architects=("amd64" "386" "arm64")
for arch in "${architects[@]}"; do
# TODO support arm, mips ...
env GOOS=linux GOARCH=${arch} go build -tags $TAG --ldflags="-s -w" -o $ROOT/installers/edge-installer-dns-helper-linux-${arch} $ROOT/../cmd/installer-dns-helper/main.go
done
if [ $TAG = "plus" ]; then
echo "building dns node installer ..."
architects=("amd64" "arm64")
for arch in "${architects[@]}"; do
# TODO support arm, mips ...
env GOOS=linux GOARCH="${arch}" go build -trimpath -tags $TAG --ldflags="-s -w" -o "$ROOT"/installers/edge-installer-dns-helper-linux-"${arch}" "$ROOT"/../cmd/installer-dns-helper/main.go
done
fi
# building api node
env GOOS=$OS GOARCH=$ARCH go build -tags $TAG --ldflags="-s -w" -o $DIST/bin/edge-api $ROOT/../cmd/edge-api/main.go
env GOOS="$OS" GOARCH="$ARCH" go build -trimpath -tags $TAG --ldflags="-s -w" -o "$DIST"/bin/edge-api "$ROOT"/../cmd/edge-api/main.go
# 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
echo "zip files"
cd "${DIST}/../" || exit
@@ -135,15 +135,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 $3
build "$1" "$2" "$3"

View File

@@ -9,3 +9,7 @@ dbs:
prefix: "edge"
models:
package: internal/web/models
fields:
bool: [ "uamIsOn", "followPort", "requestHostExcludingPort", "autoRemoteStart", "autoInstallNftables" ]

File diff suppressed because it is too large Load Diff

View File

@@ -2,8 +2,10 @@ package main
import (
"encoding/json"
"flag"
"fmt"
"github.com/TeaOSLab/EdgeAPI/internal/apps"
"github.com/TeaOSLab/EdgeAPI/internal/configs"
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
"github.com/TeaOSLab/EdgeAPI/internal/nodes"
"github.com/TeaOSLab/EdgeAPI/internal/setup"
@@ -11,6 +13,7 @@ import (
"github.com/iwind/TeaGo/Tea"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"github.com/iwind/gosock/pkg/gosock"
"log"
"os"
@@ -20,14 +23,15 @@ func main() {
if !Tea.IsTesting() {
Tea.Env = "prod"
}
app := apps.NewAppCmd()
var app = apps.NewAppCmd()
app.Version(teaconst.Version)
app.Product(teaconst.ProductName)
app.Usage(teaconst.ProcessName + " [start|stop|restart|setup|upgrade|service|daemon]")
app.Usage(teaconst.ProcessName + " [start|stop|restart|setup|upgrade|service|daemon|issues]")
app.On("setup", func() {
setupCmd := setup.NewSetupFromCmd()
var setupCmd = setup.NewSetupFromCmd()
err := setupCmd.Run()
result := maps.Map{}
var result = maps.Map{}
if err != nil {
result["isOk"] = false
result["error"] = err.Error()
@@ -69,6 +73,14 @@ func main() {
}
fmt.Println("done")
})
app.On("reset", func() {
err := configs.ResetAPIConfig()
if err != nil {
fmt.Println("[ERROR]reset failed: " + err.Error())
return
}
fmt.Println("done")
})
app.On("goman", func() {
var sock = gosock.NewTmpSock(teaconst.ProcessName)
reply, err := sock.Send(&gosock.Command{Code: "goman"})
@@ -97,6 +109,82 @@ func main() {
}
}
})
app.On("db.stmt.prepare", func() {
var sock = gosock.NewTmpSock(teaconst.ProcessName)
reply, err := sock.Send(&gosock.Command{Code: "db.stmt.prepare"})
if err != nil {
fmt.Println("[ERROR]" + err.Error())
} else {
var isOn = maps.NewMap(reply.Params).GetBool("isOn")
if isOn {
fmt.Println("show statements: on")
} else {
fmt.Println("show statements: off")
}
}
})
app.On("db.stmt.count", func() {
var sock = gosock.NewTmpSock(teaconst.ProcessName)
reply, err := sock.Send(&gosock.Command{Code: "db.stmt.count"})
if err != nil {
fmt.Println("[ERROR]" + err.Error())
} else {
var count = maps.NewMap(reply.Params).GetInt("count")
fmt.Println("prepared statements count: " + types.String(count))
}
})
app.On("issues", func() {
var flagSet = flag.NewFlagSet("issues", flag.ExitOnError)
var formatJSON = false
flagSet.BoolVar(&formatJSON, "json", false, "")
_ = flagSet.Parse(os.Args[2:])
data, err := os.ReadFile(Tea.LogFile("issues.log"))
if err != nil {
if formatJSON {
fmt.Print("[]")
} else {
fmt.Println("no issues yet")
}
} else {
var issueMaps = []maps.Map{}
err = json.Unmarshal(data, &issueMaps)
if err != nil {
if formatJSON {
fmt.Print("[]")
} else {
fmt.Println("no issues yet")
}
} else {
if formatJSON {
fmt.Print(string(data))
} else {
if len(issueMaps) == 0 {
fmt.Println("no issues yet")
} else {
for i, issue := range issueMaps {
fmt.Println("issue " + types.String(i+1) + ": " + issue.GetString("message"))
}
}
}
}
}
})
app.On("instance", func() {
var sock = gosock.NewTmpSock(teaconst.ProcessName)
reply, err := sock.Send(&gosock.Command{Code: "instance"})
if err != nil {
fmt.Println("[ERROR]" + err.Error())
} else {
replyJSON, err := json.MarshalIndent(reply.Params, "", " ")
if err != nil {
fmt.Println("[ERROR]marshal result failed: " + err.Error())
} else {
fmt.Println(string(replyJSON))
}
}
})
app.Run(func() {
nodes.NewAPINode().Start()
})

View File

@@ -2,7 +2,7 @@ package main
import (
"flag"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeAPI/internal/installers/helpers"
"github.com/iwind/gosock/pkg/gosock"
"os"
"os/exec"
@@ -51,7 +51,7 @@ func main() {
return
}
unzip := utils.NewUnzip(zipPath, targetPath)
unzip := helpers.NewUnzip(zipPath, targetPath)
err := unzip.Run()
if err != nil {
stderr("ERROR: " + err.Error())

View File

@@ -1,8 +1,9 @@
package main
// 注意这里的依赖文件应该最小化,从而使编译后的文件最小化
import (
"flag"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeAPI/internal/installers/helpers"
"github.com/iwind/gosock/pkg/gosock"
"os"
"os/exec"
@@ -51,7 +52,7 @@ func main() {
return
}
unzip := utils.NewUnzip(zipPath, targetPath)
unzip := helpers.NewUnzip(zipPath, targetPath)
err := unzip.Run()
if err != nil {
stderr("ERROR: " + err.Error())

View File

@@ -1,193 +0,0 @@
package main
import (
"bytes"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/regions"
"github.com/iwind/TeaGo/Tea"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/logs"
"io/ioutil"
"os"
"regexp"
"strings"
)
func main() {
// 导入数据
if lists.ContainsString(os.Args, "import") {
dbs.NotifyReady()
data, err := ioutil.ReadFile(Tea.Root + "/resources/ipdata/ip2region/global_region.csv")
if err != nil {
logs.Println("[ERROR]" + err.Error())
return
}
if len(data) == 0 {
logs.Println("[ERROR]file content should not be empty")
return
}
lines := bytes.Split(data, []byte{'\n'})
for _, line := range lines {
line = bytes.TrimSpace(line)
if len(line) == 0 {
continue
}
s := string(line)
reg := regexp.MustCompile(`(?U)(\d+),(\d+),(.+),(\d+),`)
if !reg.MatchString(s) {
continue
}
result := reg.FindStringSubmatch(s)
dataId := result[1]
parentDataId := result[2]
name := result[3]
level := result[4]
switch level {
case "1": // 国家|地区
countryId, err := regions.SharedRegionCountryDAO.FindCountryIdWithDataId(nil, dataId)
if err != nil {
logs.Println("[ERROR]" + err.Error())
return
}
if countryId == 0 {
logs.Println("creating country or region ", name)
_, err = regions.SharedRegionCountryDAO.CreateCountry(nil, name, dataId)
if err != nil {
logs.Println("[ERROR]" + err.Error())
return
}
}
case "2": // 省份|地区
provinceId, err := regions.SharedRegionProvinceDAO.FindProvinceIdWithDataId(nil, dataId)
if err != nil {
logs.Println("[ERROR]" + err.Error())
return
}
if provinceId == 0 {
logs.Println("creating province", name)
countryId, err := regions.SharedRegionCountryDAO.FindCountryIdWithDataId(nil, parentDataId)
if err != nil {
logs.Println("[ERROR]" + err.Error())
return
}
if countryId == 0 {
logs.Println("[ERROR]can not find country from data id '" + parentDataId + "'")
return
}
_, err = regions.SharedRegionProvinceDAO.CreateProvince(nil, countryId, name, dataId)
if err != nil {
logs.Println("[ERROR]" + err.Error())
return
}
}
case "3": // 城市
cityId, err := regions.SharedRegionCityDAO.FindCityWithDataId(nil, dataId)
if err != nil {
logs.Println("[ERROR]" + err.Error())
return
}
if cityId == 0 {
logs.Println("creating city", name)
provinceId, err := regions.SharedRegionProvinceDAO.FindProvinceIdWithDataId(nil, parentDataId)
if err != nil {
logs.Println("[ERROR]" + err.Error())
return
}
_, err = regions.SharedRegionCityDAO.CreateCity(nil, provinceId, name, dataId)
if err != nil {
logs.Println("[ERROR]" + err.Error())
return
}
}
}
}
logs.Println("done")
}
// 检查数据
if lists.ContainsString(os.Args, "check") {
dbs.NotifyReady()
data, err := ioutil.ReadFile(Tea.Root + "/resources/ipdata/ip2region/ip.merge.txt")
if err != nil {
logs.Println("[ERROR]" + err.Error())
return
}
if len(data) == 0 {
logs.Println("[ERROR]file should not be empty")
return
}
lines := bytes.Split(data, []byte("\n"))
for index, line := range lines {
s := string(bytes.TrimSpace(line))
if len(s) == 0 {
continue
}
pieces := strings.Split(s, "|")
countryName := pieces[2]
provinceName := pieces[4]
providerName := pieces[6]
// 记录provider
if len(providerName) > 0 && providerName != "0" {
providerId, err := regions.SharedRegionProviderDAO.FindProviderIdWithNameCacheable(nil, providerName)
if err != nil {
logs.Println("[ERROR]find provider id failed: " + err.Error())
return
}
if providerId == 0 {
logs.Println("creating new provider '"+providerName+"' ... ", index, "line")
_, err = regions.SharedRegionProviderDAO.CreateProvider(nil, providerName)
if err != nil {
logs.Println("create new provider failed: " + providerName)
return
}
logs.Println("created new provider '" + providerName + "'")
return
}
}
if lists.ContainsString([]string{"0", "欧洲", "北美地区", "法国南部领地", "非洲地区", "亚太地区"}, countryName) {
continue
}
// 检查国家
countryId, err := regions.SharedRegionCountryDAO.FindCountryIdWithNameCacheable(nil, countryName)
if err != nil {
logs.Println("[ERROR]" + err.Error())
return
}
if countryId == 0 {
logs.Println("[ERROR]can not find country '"+countryName+"', index: ", index, "data: "+s)
return
}
// 检查省份
if countryName == "中国" {
if lists.ContainsString([]string{"0"}, provinceName) {
continue
}
provinceId, err := regions.SharedRegionProvinceDAO.FindProvinceIdWithNameCacheable(nil, countryId, provinceName)
if err != nil {
logs.Println("[ERROR]" + err.Error())
return
}
if provinceId == 0 {
logs.Println("[ERROR]can not find province '"+provinceName+"', index: ", index, "data: "+s)
return
}
}
}
logs.Println("done")
}
}

View File

@@ -7,7 +7,6 @@ import (
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/dbs"
"go/format"
"io/ioutil"
"os"
"path/filepath"
"strconv"
@@ -19,7 +18,7 @@ func main() {
fmt.Println("[ERROR]" + err.Error())
return
}
results, err := setup.NewSQLDump().Dump(db)
results, err := setup.NewSQLDump().Dump(db, true)
if err != nil {
fmt.Println("[ERROR]" + err.Error())
return
@@ -67,7 +66,7 @@ func init() {
return
}
err = ioutil.WriteFile(sqlFile, dst, 0666)
err = os.WriteFile(sqlFile, dst, 0666)
if err != nil {
fmt.Println("[ERROR]write file failed: " + err.Error())
return

3
dist/.gitignore vendored
View File

@@ -1 +1,2 @@
*.zip
*.zip
edge-api

49
go.mod
View File

@@ -1,31 +1,50 @@
module github.com/TeaOSLab/EdgeAPI
go 1.15
go 1.18
replace github.com/TeaOSLab/EdgeCommon => ../EdgeCommon
require (
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
github.com/TeaOSLab/EdgeCommon v0.0.0-00010101000000-000000000000
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183
github.com/andybalholm/brotli v1.0.4
github.com/cespare/xxhash/v2 v2.1.1
github.com/go-acme/lego/v4 v4.5.2
github.com/go-ole/go-ole v1.2.4 // indirect
github.com/go-sql-driver/mysql v1.5.0
github.com/go-yaml/yaml v2.1.0+incompatible
github.com/golang/protobuf v1.5.2
github.com/iwind/TeaGo v0.0.0-20211026123858-7de7a21cad24
github.com/iwind/gosock v0.0.0-20210722083328-12b2d66abec3
github.com/json-iterator/go v1.1.12 // indirect
github.com/lionsoul2014/ip2region v2.2.0-release+incompatible
github.com/iwind/TeaGo v0.0.0-20220811034530-657e3f15b79e
github.com/iwind/gosock v0.0.0-20220505115348-f88412125a62
github.com/miekg/dns v1.1.43
github.com/mozillazg/go-pinyin v0.18.0
github.com/pkg/sftp v1.12.0
github.com/shirou/gopsutil v3.21.5+incompatible
github.com/tklauser/go-sysconf v0.3.6 // indirect
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced // indirect
google.golang.org/grpc v1.38.0
google.golang.org/protobuf v1.26.0
github.com/shirou/gopsutil/v3 v3.22.2
github.com/smartwalle/alipay/v3 v3.1.7
golang.org/x/crypto v0.1.0
golang.org/x/net v0.1.0
golang.org/x/sys v0.1.0
google.golang.org/grpc v1.45.0
google.golang.org/protobuf v1.27.1
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/cenkalti/backoff/v4 v4.1.1 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/smartwalle/crypto4go v1.0.2 // indirect
github.com/tklauser/go-sysconf v0.3.9 // indirect
github.com/tklauser/numcpus v0.3.0 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/text v0.4.0 // indirect
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
)

105
go.sum
View File

@@ -43,8 +43,6 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -52,6 +50,7 @@ github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183 h1:dkj8/dxOQ4L1XpwCzRLqukvUBbx
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
@@ -78,6 +77,10 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cloudflare/cloudflare-go v0.20.0/go.mod h1:sPWL/lIC6biLEdyGZwBQ1rGQKF1FhM7N60fuNiFdYTI=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
@@ -101,7 +104,8 @@ github.com/dnsimple/dnsimple-go v0.70.1/go.mod h1:F9WHww9cC76hrnwGFfAfrqdW99j3MO
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/exoscale/egoscale v0.67.0/go.mod h1:wi0myUxPsV8SdEtdJHQJxFLL/wEw9fiw9Gs1PWRkvkM=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
@@ -122,8 +126,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
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-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-redis/redis/v8 v8.0.0-beta.7/go.mod h1:FGJAWDWFht1sQ4qxyJHZZbVyvnVcKQN0E3u5/5lRz+g=
@@ -132,8 +136,6 @@ github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gG
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
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/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -161,6 +163,7 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
@@ -173,8 +176,10 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/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.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
@@ -201,6 +206,7 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
@@ -231,13 +237,10 @@ github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhK
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/infobloxopen/infoblox-go-client v1.1.1/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI=
github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/TeaGo v0.0.0-20210628135026-38575a4ab060/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/TeaGo v0.0.0-20210831140440-a2a442471b13 h1:HuEJ5xJfujW1Q6rNDhOu5LQXEBB2qLPah3jYslT8Gz4=
github.com/iwind/TeaGo v0.0.0-20210831140440-a2a442471b13/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/TeaGo v0.0.0-20211026123858-7de7a21cad24 h1:1cGulkD2SNJJRok5OKwyhP/Ddm+PgSWKOupn0cR36/A=
github.com/iwind/TeaGo v0.0.0-20211026123858-7de7a21cad24/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/gosock v0.0.0-20210722083328-12b2d66abec3 h1:aBSonas7vFcgTj9u96/bWGILGv1ZbUSTLiOzcI1ZT6c=
github.com/iwind/gosock v0.0.0-20210722083328-12b2d66abec3/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
github.com/iwind/TeaGo v0.0.0-20220811034530-657e3f15b79e h1:cw4b6ecXdXvLd45YSstD8r9ClcnVK4ljZMZCept2aOk=
github.com/iwind/TeaGo v0.0.0-20220811034530-657e3f15b79e/go.mod h1:fi/Pq+/5m2HZoseM+39dMF57ANXRt6w4PkGu3NXPc5s=
github.com/iwind/gosock v0.0.0-20220505115348-f88412125a62 h1:HJH6RDheAY156DnIfJSD/bEvqyXzsZuE2gzs8PuUjoo=
github.com/iwind/gosock v0.0.0-20220505115348-f88412125a62/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
github.com/jarcoal/httpmock v1.0.6/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
@@ -250,8 +253,6 @@ github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
@@ -277,12 +278,12 @@ github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/linode/linodego v0.31.1/go.mod h1:BR0gVkCJffEdIGJSl6bHR80Ty+Uvg/2jkjmrWaFectM=
github.com/lionsoul2014/ip2region v2.2.0-release+incompatible h1:1qp9iks+69h7IGLazAplzS9Ca14HAxuD5c0rbFdPGy4=
github.com/lionsoul2014/ip2region v2.2.0-release+incompatible/go.mod h1:+ZBN7PBoh5gG6/y0ZQ85vJDBe21WnfbRrQQwTfliJJI=
github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
github.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ=
github.com/liquidweb/liquidweb-go v1.6.3/go.mod h1:SuXXp+thr28LnjEw18AYtWwIbWMHSUiajPQs8T9c/Rc=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -322,7 +323,6 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ
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/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
@@ -371,6 +371,8 @@ github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw
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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
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/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
@@ -390,18 +392,23 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sacloud/libsacloud v1.36.2/go.mod h1:P7YAOVmnIn3DKHqCZcUKYUXmSwGBm3yS7IBEjKVSrjg=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shirou/gopsutil v3.21.5+incompatible h1:OloQyEerMi7JUrXiNzy8wQ5XN+baemxSl12QgIzt0jc=
github.com/shirou/gopsutil v3.21.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil/v3 v3.22.2 h1:wCrArWFkHYIdDxx/FSfF5RB4dpJYW6t7rcp3+zL8uks=
github.com/shirou/gopsutil/v3 v3.22.2/go.mod h1:WapW1AOOPlHyXr+yOyw3uYx36enocrtSoSBy0L5vUHY=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/smartwalle/alipay/v3 v3.1.7 h1:J4U5slABafKVD/b9gPCZe/3HAPB8Pa2NOYOPcugEJBo=
github.com/smartwalle/alipay/v3 v3.1.7/go.mod h1:cZUMCCnsux9YAxA0/f3PWUR+7wckWtE1BqxbVRtGij0=
github.com/smartwalle/crypto4go v1.0.2 h1:9DUEOOsPhmp00438L4oBdcL8EZG1zumecft5bWj5phI=
github.com/smartwalle/crypto4go v1.0.2/go.mod h1:LQ7vCZIb7BE5+MuMtJBuO8ORkkQ01m4DXDBWPzLbkMY=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w=
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
@@ -436,10 +443,10 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tklauser/go-sysconf v0.3.6 h1:oc1sJWvKkmvIxhDHeKWvZS4f6AW+YcoguSfRF2/Hmo4=
github.com/tklauser/go-sysconf v0.3.6/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA=
github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM=
github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo=
github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ=
github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/transip/gotransip/v6 v6.6.1/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g=
github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g=
@@ -456,13 +463,15 @@ github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQ
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/otel v0.7.0/go.mod h1:aZMyHG5TqDOXEgH2tyLiXSUKly1jT3yqE9PmrzIeCdo=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
@@ -472,6 +481,7 @@ golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -482,8 +492,9 @@ golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
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-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -507,7 +518,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
@@ -516,7 +526,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -551,8 +560,9 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -588,6 +598,7 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/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-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -611,19 +622,21 @@ golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201110211018-35f3e6cf4a65/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-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -631,8 +644,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -674,7 +688,6 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -714,9 +727,10 @@ google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced h1:c5geK1iMU3cDKtFrCVQIcjR3W+JOZMuhIyICMCTbtus=
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e h1:fNKDNuUyC4WH+inqDMpfXDdfvwfYILbsX+oskGZ8hxg=
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -726,9 +740,10 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -739,8 +754,9 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -772,8 +788,9 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -1,66 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package accesslogs
import (
"encoding/json"
"fmt"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"strconv"
"time"
)
type BaseStorage struct {
isOk bool
version int
}
func (this *BaseStorage) SetVersion(version int) {
this.version = version
}
func (this *BaseStorage) Version() int {
return this.version
}
func (this *BaseStorage) IsOk() bool {
return this.isOk
}
func (this *BaseStorage) SetOk(isOk bool) {
this.isOk = isOk
}
// Marshal 对日志进行编码
func (this *BaseStorage) Marshal(accessLog *pb.HTTPAccessLog) ([]byte, error) {
return json.Marshal(accessLog)
}
// FormatVariables 格式化字符串中的变量
func (this *BaseStorage) FormatVariables(s string) string {
now := time.Now()
return configutils.ParseVariables(s, func(varName string) (value string) {
switch varName {
case "year":
return strconv.Itoa(now.Year())
case "month":
return fmt.Sprintf("%02d", now.Month())
case "week":
_, week := now.ISOWeek()
return fmt.Sprintf("%02d", week)
case "day":
return fmt.Sprintf("%02d", now.Day())
case "hour":
return fmt.Sprintf("%02d", now.Hour())
case "minute":
return fmt.Sprintf("%02d", now.Minute())
case "second":
return fmt.Sprintf("%02d", now.Second())
case "date":
return fmt.Sprintf("%d%02d%02d", now.Year(), now.Month(), now.Day())
}
return varName
})
}

View File

@@ -1,95 +0,0 @@
package accesslogs
import (
"bytes"
"errors"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/logs"
"os/exec"
"sync"
)
// CommandStorage 通过命令行存储
type CommandStorage struct {
BaseStorage
config *serverconfigs.AccessLogCommandStorageConfig
writeLocker sync.Mutex
}
func NewCommandStorage(config *serverconfigs.AccessLogCommandStorageConfig) *CommandStorage {
return &CommandStorage{config: config}
}
func (this *CommandStorage) Config() interface{} {
return this.config
}
// Start 启动
func (this *CommandStorage) Start() error {
if len(this.config.Command) == 0 {
return errors.New("'command' should not be empty")
}
return nil
}
// 写入日志
func (this *CommandStorage) Write(accessLogs []*pb.HTTPAccessLog) error {
if len(accessLogs) == 0 {
return nil
}
this.writeLocker.Lock()
defer this.writeLocker.Unlock()
cmd := exec.Command(this.config.Command, this.config.Args...)
if len(this.config.Dir) > 0 {
cmd.Dir = this.config.Dir
}
stdout := bytes.NewBuffer([]byte{})
cmd.Stdout = stdout
w, err := cmd.StdinPipe()
if err != nil {
return err
}
err = cmd.Start()
if err != nil {
return err
}
for _, accessLog := range accessLogs {
data, err := this.Marshal(accessLog)
if err != nil {
logs.Error(err)
continue
}
_, err = w.Write(data)
if err != nil {
logs.Error(err)
}
_, err = w.Write([]byte("\n"))
if err != nil {
logs.Error(err)
}
}
_ = w.Close()
err = cmd.Wait()
if err != nil {
logs.Error(err)
if stdout.Len() > 0 {
logs.Error(errors.New(string(stdout.Bytes())))
}
}
return nil
}
// Close 关闭
func (this *CommandStorage) Close() error {
return nil
}

View File

@@ -1,63 +0,0 @@
package accesslogs
import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"os"
"os/exec"
"testing"
"time"
)
func TestCommandStorage_Write(t *testing.T) {
php, err := exec.LookPath("php")
if err != nil { // not found php, so we can not test
t.Log("php:", err)
return
}
cwd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
before := time.Now()
storage := NewCommandStorage(&serverconfigs.AccessLogCommandStorageConfig{
Command: php,
Args: []string{cwd + "/tests/command_storage.php"},
})
err = storage.Start()
if err != nil {
t.Fatal(err)
}
err = storage.Write([]*pb.HTTPAccessLog{
{
RequestMethod: "GET",
RequestPath: "/hello",
},
{
RequestMethod: "GET",
RequestPath: "/world",
},
{
RequestMethod: "GET",
RequestPath: "/lu",
},
{
RequestMethod: "GET",
RequestPath: "/ping",
},
})
if err != nil {
t.Fatal(err)
}
err = storage.Close()
if err != nil {
t.Fatal(err)
}
t.Log(time.Since(before).Seconds(), "seconds")
}

View File

@@ -1,127 +0,0 @@
package accesslogs
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"io/ioutil"
"net/http"
"regexp"
"strings"
"time"
)
// ESStorage ElasticSearch存储策略
type ESStorage struct {
BaseStorage
config *serverconfigs.AccessLogESStorageConfig
}
func NewESStorage(config *serverconfigs.AccessLogESStorageConfig) *ESStorage {
return &ESStorage{config: config}
}
func (this *ESStorage) Config() interface{} {
return this.config
}
// Start 开启
func (this *ESStorage) Start() error {
if len(this.config.Endpoint) == 0 {
return errors.New("'endpoint' should not be nil")
}
if !regexp.MustCompile(`(?i)^(http|https)://`).MatchString(this.config.Endpoint) {
this.config.Endpoint = "http://" + this.config.Endpoint
}
if len(this.config.Index) == 0 {
return errors.New("'index' should not be nil")
}
if len(this.config.MappingType) == 0 {
return errors.New("'mappingType' should not be nil")
}
return nil
}
// 写入日志
func (this *ESStorage) Write(accessLogs []*pb.HTTPAccessLog) error {
if len(accessLogs) == 0 {
return nil
}
bulk := &strings.Builder{}
indexName := this.FormatVariables(this.config.Index)
typeName := this.FormatVariables(this.config.MappingType)
for _, accessLog := range accessLogs {
if len(accessLog.RequestId) == 0 {
continue
}
opData, err := json.Marshal(map[string]interface{}{
"index": map[string]interface{}{
"_index": indexName,
"_type": typeName,
"_id": accessLog.RequestId,
},
})
if err != nil {
remotelogs.Error("ACCESS_LOG_ES_STORAGE", "write failed: "+err.Error())
continue
}
data, err := this.Marshal(accessLog)
if err != nil {
remotelogs.Error("ACCESS_LOG_ES_STORAGE", "marshal data failed: "+err.Error())
continue
}
bulk.Write(opData)
bulk.WriteString("\n")
bulk.Write(data)
bulk.WriteString("\n")
}
if bulk.Len() == 0 {
return nil
}
req, err := http.NewRequest(http.MethodPost, this.config.Endpoint+"/_bulk", strings.NewReader(bulk.String()))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", strings.ReplaceAll(teaconst.ProductName, " ", "-")+"/"+teaconst.Version)
if len(this.config.Username) > 0 || len(this.config.Password) > 0 {
req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(this.config.Username+":"+this.config.Password)))
}
client := utils.SharedHttpClient(10 * time.Second)
defer func() {
_ = req.Body.Close()
}()
resp, err := client.Do(req)
if err != nil {
return err
}
defer func() {
_ = resp.Body.Close()
}()
if resp.StatusCode != http.StatusOK {
bodyData, _ := ioutil.ReadAll(resp.Body)
return errors.New("ElasticSearch response status code: " + fmt.Sprintf("%d", resp.StatusCode) + " content: " + string(bodyData))
}
return nil
}
// Close 关闭
func (this *ESStorage) Close() error {
return nil
}

View File

@@ -1,53 +0,0 @@
package accesslogs
import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"testing"
"time"
)
func TestESStorage_Write(t *testing.T) {
storage := NewESStorage(&serverconfigs.AccessLogESStorageConfig{
Endpoint: "http://127.0.0.1:9200",
Index: "logs",
MappingType: "accessLogs",
Username: "hello",
Password: "world",
})
err := storage.Start()
if err != nil {
t.Fatal(err)
}
{
err = storage.Write([]*pb.HTTPAccessLog{
{
RequestMethod: "POST",
RequestPath: "/1",
TimeLocal: time.Now().Format("2/Jan/2006:15:04:05 -0700"),
TimeISO8601: "2018-07-23T22:23:35+08:00",
Header: map[string]*pb.Strings{
"Content-Type": {Values: []string{"text/html"}},
},
},
{
RequestMethod: "GET",
RequestPath: "/2",
TimeLocal: time.Now().Format("2/Jan/2006:15:04:05 -0700"),
TimeISO8601: "2018-07-23T22:23:35+08:00",
Header: map[string]*pb.Strings{
"Content-Type": {Values: []string{"text/css"}},
},
},
})
if err != nil {
t.Fatal(err)
}
}
err = storage.Close()
if err != nil {
t.Fatal(err)
}
}

View File

@@ -1,127 +0,0 @@
package accesslogs
import (
"errors"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/logs"
"os"
"path/filepath"
"sync"
)
// FileStorage 文件存储策略
type FileStorage struct {
BaseStorage
config *serverconfigs.AccessLogFileStorageConfig
writeLocker sync.Mutex
files map[string]*os.File // path => *File
filesLocker sync.Mutex
}
func NewFileStorage(config *serverconfigs.AccessLogFileStorageConfig) *FileStorage {
return &FileStorage{
config: config,
}
}
func (this *FileStorage) Config() interface{} {
return this.config
}
// Start 开启
func (this *FileStorage) Start() error {
if len(this.config.Path) == 0 {
return errors.New("'path' should not be empty")
}
this.files = map[string]*os.File{}
return nil
}
// Write 写入日志
func (this *FileStorage) Write(accessLogs []*pb.HTTPAccessLog) error {
if len(accessLogs) == 0 {
return nil
}
fp := this.fp()
if fp == nil {
return errors.New("file pointer should not be nil")
}
this.writeLocker.Lock()
defer this.writeLocker.Unlock()
for _, accessLog := range accessLogs {
data, err := this.Marshal(accessLog)
if err != nil {
logs.Error(err)
continue
}
_, err = fp.Write(data)
if err != nil {
_ = this.Close()
break
}
_, _ = fp.WriteString("\n")
}
return nil
}
// Close 关闭
func (this *FileStorage) Close() error {
this.filesLocker.Lock()
defer this.filesLocker.Unlock()
var resultErr error
for _, f := range this.files {
err := f.Close()
if err != nil {
resultErr = err
}
}
return resultErr
}
func (this *FileStorage) fp() *os.File {
path := this.FormatVariables(this.config.Path)
this.filesLocker.Lock()
defer this.filesLocker.Unlock()
fp, ok := this.files[path]
if ok {
return fp
}
// 关闭其他的文件
for _, f := range this.files {
_ = f.Close()
}
// 是否创建文件目录
if this.config.AutoCreate {
dir := filepath.Dir(path)
_, err := os.Stat(dir)
if os.IsNotExist(err) {
err = os.MkdirAll(dir, 0777)
if err != nil {
logs.Error(err)
return nil
}
}
}
// 打开新文件
fp, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
logs.Error(err)
return nil
}
this.files[path] = fp
return fp
}

View File

@@ -1,70 +0,0 @@
package accesslogs
import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/Tea"
"testing"
"time"
)
func TestFileStorage_Write(t *testing.T) {
storage := NewFileStorage(&serverconfigs.AccessLogFileStorageConfig{
Path: Tea.Root + "/logs/access-${date}.log",
})
err := storage.Start()
if err != nil {
t.Fatal(err)
}
{
err = storage.Write([]*pb.HTTPAccessLog{
{
RequestPath: "/hello",
},
{
RequestPath: "/world",
},
})
if err != nil {
t.Fatal(err)
}
}
{
err = storage.Write([]*pb.HTTPAccessLog{
{
RequestPath: "/1",
},
{
RequestPath: "/2",
},
})
if err != nil {
t.Fatal(err)
}
}
{
err = storage.Write([]*pb.HTTPAccessLog{
{
RequestMethod: "POST",
RequestPath: "/1",
TimeLocal: time.Now().Format("2/Jan/2006:15:04:05 -0700"),
},
{
RequestMethod: "GET",
RequestPath: "/2",
TimeLocal: time.Now().Format("2/Jan/2006:15:04:05 -0700"),
},
})
if err != nil {
t.Fatal(err)
}
}
err = storage.Close()
if err != nil {
t.Fatal(err)
}
}

View File

@@ -1,30 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package accesslogs
import "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
// StorageInterface 日志存储接口
type StorageInterface interface {
// Version 获取版本
Version() int
// SetVersion 设置版本
SetVersion(version int)
IsOk() bool
SetOk(ok bool)
// Config 获取配置
Config() interface{}
// Start 开启
Start() error
// Write 写入日志
Write(accessLogs []*pb.HTTPAccessLog) error
// Close 关闭
Close() error
}

View File

@@ -1,183 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package accesslogs
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/types"
"sync"
"time"
)
var SharedStorageManager = NewStorageManager()
type StorageManager struct {
storageMap map[int64]StorageInterface // policyId => Storage
locker sync.Mutex
}
func NewStorageManager() *StorageManager {
return &StorageManager{
storageMap: map[int64]StorageInterface{},
}
}
func (this *StorageManager) Start() {
var ticker = time.NewTicker(1 * time.Minute)
if Tea.IsTesting() {
ticker = time.NewTicker(5 * time.Second)
}
// 启动时执行一次
var err = this.Loop()
if err != nil {
remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "update error: "+err.Error())
}
// 循环执行
for range ticker.C {
err := this.Loop()
if err != nil {
remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "update error: "+err.Error())
}
}
}
// Loop 更新
func (this *StorageManager) Loop() error {
policies, err := models.SharedHTTPAccessLogPolicyDAO.FindAllEnabledAndOnPolicies(nil)
if err != nil {
return err
}
var policyIds = []int64{}
for _, policy := range policies {
if policy.IsOn == 1 {
policyIds = append(policyIds, int64(policy.Id))
}
}
this.locker.Lock()
defer this.locker.Unlock()
// 关闭不用的
for policyId, storage := range this.storageMap {
if !lists.ContainsInt64(policyIds, policyId) {
err := storage.Close()
if err != nil {
remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "close '"+types.String(policyId)+"' failed: "+err.Error())
}
delete(this.storageMap, policyId)
remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "remove '"+types.String(policyId)+"'")
}
}
for _, policy := range policies {
var policyId = int64(policy.Id)
storage, ok := this.storageMap[policyId]
if ok {
// 检查配置是否有变更
if types.Int(policy.Version) != storage.Version() {
err = storage.Close()
if err != nil {
remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "close policy '"+types.String(policyId)+"' failed: "+err.Error())
// 继续往下执行
}
if len(policy.Options) > 0 {
err = json.Unmarshal([]byte(policy.Options), storage.Config())
if err != nil {
remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "unmarshal policy '"+types.String(policyId)+"' config failed: "+err.Error())
storage.SetOk(false)
continue
}
}
storage.SetVersion(types.Int(policy.Version))
err := storage.Start()
if err != nil {
remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "start policy '"+types.String(policyId)+"' failed: "+err.Error())
continue
}
storage.SetOk(true)
remotelogs.Println("ACCESS_LOG_STORAGE_MANAGER", "restart policy '"+types.String(policyId)+"'")
}
} else {
storage, err := this.createStorage(policy.Type, []byte(policy.Options))
if err != nil {
remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "create policy '"+types.String(policyId)+"' failed: "+err.Error())
continue
}
storage.SetVersion(types.Int(policy.Version))
this.storageMap[policyId] = storage
err = storage.Start()
if err != nil {
remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "start policy '"+types.String(policyId)+"' failed: "+err.Error())
continue
}
storage.SetOk(true)
remotelogs.Println("ACCESS_LOG_STORAGE_MANAGER", "start policy '"+types.String(policyId)+"'")
}
}
return nil
}
func (this *StorageManager) createStorage(storageType string, optionsJSON []byte) (StorageInterface, error) {
switch storageType {
case serverconfigs.AccessLogStorageTypeFile:
var config = &serverconfigs.AccessLogFileStorageConfig{}
if len(optionsJSON) > 0 {
err := json.Unmarshal(optionsJSON, config)
if err != nil {
return nil, err
}
}
return NewFileStorage(config), nil
case serverconfigs.AccessLogStorageTypeES:
var config = &serverconfigs.AccessLogESStorageConfig{}
if len(optionsJSON) > 0 {
err := json.Unmarshal(optionsJSON, config)
if err != nil {
return nil, err
}
}
return NewESStorage(config), nil
case serverconfigs.AccessLogStorageTypeTCP:
var config = &serverconfigs.AccessLogTCPStorageConfig{}
if len(optionsJSON) > 0 {
err := json.Unmarshal(optionsJSON, config)
if err != nil {
return nil, err
}
}
return NewTCPStorage(config), nil
case serverconfigs.AccessLogStorageTypeSyslog:
var config = &serverconfigs.AccessLogSyslogStorageConfig{}
if len(optionsJSON) > 0 {
err := json.Unmarshal(optionsJSON, config)
if err != nil {
return nil, err
}
}
return NewSyslogStorage(config), nil
case serverconfigs.AccessLogStorageTypeCommand:
var config = &serverconfigs.AccessLogCommandStorageConfig{}
if len(optionsJSON) > 0 {
err := json.Unmarshal(optionsJSON, config)
if err != nil {
return nil, err
}
}
return NewCommandStorage(config), nil
}
return nil, errors.New("invalid policy type '" + storageType + "'")
}

View File

@@ -1,17 +0,0 @@
package accesslogs
import (
"github.com/iwind/TeaGo/dbs"
"testing"
)
func TestStorageManager_Loop(t *testing.T) {
dbs.NotifyReady()
var storage = NewStorageManager()
err := storage.Loop()
if err != nil {
t.Fatal(err)
}
t.Log(storage.storageMap)
}

View File

@@ -1,15 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build !plus
// +build !plus
package accesslogs
import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
// 写入日志
func (this *StorageManager) Write(policyId int64, accessLogs []*pb.HTTPAccessLog) error {
return nil
}

View File

@@ -1,137 +0,0 @@
package accesslogs
import (
"errors"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/logs"
"os/exec"
"runtime"
"strconv"
)
type SyslogStorageProtocol = string
const (
SyslogStorageProtocolTCP SyslogStorageProtocol = "tcp"
SyslogStorageProtocolUDP SyslogStorageProtocol = "udp"
SyslogStorageProtocolNone SyslogStorageProtocol = "none"
SyslogStorageProtocolSocket SyslogStorageProtocol = "socket"
)
type SyslogStoragePriority = int
// SyslogStorage syslog存储策略
type SyslogStorage struct {
BaseStorage
config *serverconfigs.AccessLogSyslogStorageConfig
exe string
}
func NewSyslogStorage(config *serverconfigs.AccessLogSyslogStorageConfig) *SyslogStorage {
return &SyslogStorage{config: config}
}
func (this *SyslogStorage) Config() interface{} {
return this.config
}
// Start 开启
func (this *SyslogStorage) Start() error {
if runtime.GOOS != "linux" {
return errors.New("'syslog' storage only works on linux")
}
exe, err := exec.LookPath("logger")
if err != nil {
return err
}
this.exe = exe
return nil
}
// 写入日志
func (this *SyslogStorage) Write(accessLogs []*pb.HTTPAccessLog) error {
if len(accessLogs) == 0 {
return nil
}
args := []string{}
if len(this.config.Tag) > 0 {
args = append(args, "-t", this.config.Tag)
}
if this.config.Priority >= 0 {
args = append(args, "-p", strconv.Itoa(this.config.Priority))
}
switch this.config.Protocol {
case SyslogStorageProtocolTCP:
args = append(args, "-T")
if len(this.config.ServerAddr) > 0 {
args = append(args, "-n", this.config.ServerAddr)
}
if this.config.ServerPort > 0 {
args = append(args, "-P", strconv.Itoa(this.config.ServerPort))
}
case SyslogStorageProtocolUDP:
args = append(args, "-d")
if len(this.config.ServerAddr) > 0 {
args = append(args, "-n", this.config.ServerAddr)
}
if this.config.ServerPort > 0 {
args = append(args, "-P", strconv.Itoa(this.config.ServerPort))
}
case SyslogStorageProtocolSocket:
args = append(args, "-u")
args = append(args, this.config.Socket)
case SyslogStorageProtocolNone:
// do nothing
}
args = append(args, "-S", "10240")
cmd := exec.Command(this.exe, args...)
w, err := cmd.StdinPipe()
if err != nil {
return err
}
err = cmd.Start()
if err != nil {
return err
}
for _, accessLog := range accessLogs {
data, err := this.Marshal(accessLog)
if err != nil {
logs.Error(err)
continue
}
_, err = w.Write(data)
if err != nil {
logs.Error(err)
}
_, err = w.Write([]byte("\n"))
if err != nil {
logs.Error(err)
}
}
_ = w.Close()
err = cmd.Wait()
if err != nil {
return err
}
return nil
}
// Close 关闭
func (this *SyslogStorage) Close() error {
return nil
}

View File

@@ -1,111 +0,0 @@
package accesslogs
import (
"errors"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/logs"
"net"
"sync"
)
// TCPStorage TCP存储策略
type TCPStorage struct {
BaseStorage
config *serverconfigs.AccessLogTCPStorageConfig
writeLocker sync.Mutex
connLocker sync.Mutex
conn net.Conn
}
func NewTCPStorage(config *serverconfigs.AccessLogTCPStorageConfig) *TCPStorage {
return &TCPStorage{config: config}
}
func (this *TCPStorage) Config() interface{} {
return this.config
}
// Start 开启
func (this *TCPStorage) Start() error {
if len(this.config.Network) == 0 {
return errors.New("'network' should not be empty")
}
if len(this.config.Addr) == 0 {
return errors.New("'addr' should not be empty")
}
return nil
}
// 写入日志
func (this *TCPStorage) Write(accessLogs []*pb.HTTPAccessLog) error {
if len(accessLogs) == 0 {
return nil
}
err := this.connect()
if err != nil {
return err
}
conn := this.conn
if conn == nil {
return errors.New("connection should not be nil")
}
this.writeLocker.Lock()
defer this.writeLocker.Unlock()
for _, accessLog := range accessLogs {
data, err := this.Marshal(accessLog)
if err != nil {
logs.Error(err)
continue
}
_, err = conn.Write(data)
if err != nil {
_ = this.Close()
break
}
_, err = conn.Write([]byte("\n"))
if err != nil {
_ = this.Close()
break
}
}
return nil
}
// Close 关闭
func (this *TCPStorage) Close() error {
this.connLocker.Lock()
defer this.connLocker.Unlock()
if this.conn != nil {
err := this.conn.Close()
this.conn = nil
return err
}
return nil
}
func (this *TCPStorage) connect() error {
this.connLocker.Lock()
defer this.connLocker.Unlock()
if this.conn != nil {
return nil
}
conn, err := net.Dial(this.config.Network, this.config.Addr)
if err != nil {
return err
}
this.conn = conn
return nil
}

View File

@@ -1,72 +0,0 @@
package accesslogs
import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"net"
"testing"
"time"
)
func TestTCPStorage_Write(t *testing.T) {
go func() {
server, err := net.Listen("tcp", "127.0.0.1:9981")
if err != nil {
t.Error(err)
return
}
for {
conn, err := server.Accept()
if err != nil {
break
}
buf := make([]byte, 1024)
for {
n, err := conn.Read(buf)
if n > 0 {
t.Log(string(buf[:n]))
}
if err != nil {
break
}
}
break
}
_ = server.Close()
}()
storage := NewTCPStorage(&serverconfigs.AccessLogTCPStorageConfig{
Network: "tcp",
Addr: "127.0.0.1:9981",
})
err := storage.Start()
if err != nil {
t.Fatal(err)
}
{
err = storage.Write([]*pb.HTTPAccessLog{
{
RequestMethod: "POST",
RequestPath: "/1",
TimeLocal: time.Now().Format("2/Jan/2006:15:04:05 -0700"),
},
{
RequestMethod: "GET",
RequestPath: "/2",
TimeLocal: time.Now().Format("2/Jan/2006:15:04:05 -0700"),
},
})
if err != nil {
t.Fatal(err)
}
}
time.Sleep(2 * time.Second)
err = storage.Close()
if err != nil {
t.Fatal(err)
}
}

View File

@@ -1,24 +0,0 @@
<?php
// test command storage
// open access log file
$fp = fopen("/tmp/goedge-command-storage.log", "a+");
// read access logs from stdin
$stdin = fopen("php://stdin", "r");
while(true) {
if (feof($stdin)) {
break;
}
$line = fgets($stdin);
// write to access log file
fwrite($fp, $line);
}
// close file pointers
fclose($fp);
fclose($stdin);
?>

View File

@@ -10,7 +10,7 @@ import (
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/lego"
acmelog "github.com/go-acme/lego/v4/log"
"io/ioutil"
"io"
"log"
"testing"
@@ -50,7 +50,7 @@ func (this *MyProvider) CleanUp(domain, token, keyAuth string) error {
// 参考 https://go-acme.github.io/lego/usage/library/
func TestGenerate(t *testing.T) {
acmelog.Logger = log.New(ioutil.Discard, "", log.LstdFlags)
acmelog.Logger = log.New(io.Discard, "", log.LstdFlags)
// 生成私钥
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
@@ -94,7 +94,7 @@ func TestGenerate(t *testing.T) {
}
func TestGenerate_EAB(t *testing.T) {
acmelog.Logger = log.New(ioutil.Discard, "", log.LstdFlags)
acmelog.Logger = log.New(io.Discard, "", log.LstdFlags)
// 生成私钥
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)

View File

@@ -5,12 +5,18 @@ import (
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/dnstypes"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/iwind/TeaGo/lists"
"os"
"strings"
"sync"
)
type DNSProvider struct {
raw dnsclients.ProviderInterface
dnsDomain string
locker sync.Mutex
deletedRecordNames []string
}
func NewDNSProvider(raw dnsclients.ProviderInterface, dnsDomain string) *DNSProvider {
@@ -21,39 +27,47 @@ func NewDNSProvider(raw dnsclients.ProviderInterface, dnsDomain string) *DNSProv
}
func (this *DNSProvider) Present(domain, token, keyAuth string) error {
_ = os.Setenv("LEGO_DISABLE_CNAME_SUPPORT", "true")
fqdn, value := dns01.GetRecord(domain, keyAuth)
// 设置记录
index := strings.Index(fqdn, "."+this.dnsDomain)
var index = strings.Index(fqdn, "."+this.dnsDomain)
if index < 0 {
return errors.New("invalid fqdn value")
}
recordName := fqdn[:index]
record, err := this.raw.QueryRecord(this.dnsDomain, recordName, dnstypes.RecordTypeTXT)
if err != nil {
return errors.New("query DNS record failed: " + err.Error())
var recordName = fqdn[:index]
// 先删除老的
this.locker.Lock()
var wasDeleted = lists.ContainsString(this.deletedRecordNames, recordName)
this.locker.Unlock()
if !wasDeleted {
records, err := this.raw.QueryRecords(this.dnsDomain, recordName, dnstypes.RecordTypeTXT)
if err != nil {
return errors.New("query DNS record failed: " + err.Error())
}
for _, record := range records {
err = this.raw.DeleteRecord(this.dnsDomain, record)
if err != nil {
return err
}
}
this.locker.Lock()
this.deletedRecordNames = append(this.deletedRecordNames, recordName)
this.locker.Unlock()
}
if record == nil {
err = this.raw.AddRecord(this.dnsDomain, &dnstypes.Record{
Id: "",
Name: recordName,
Type: dnstypes.RecordTypeTXT,
Value: value,
Route: this.raw.DefaultRoute(),
})
if err != nil {
return errors.New("create DNS record failed: " + err.Error())
}
} else {
err = this.raw.UpdateRecord(this.dnsDomain, record, &dnstypes.Record{
Name: recordName,
Type: dnstypes.RecordTypeTXT,
Value: value,
Route: this.raw.DefaultRoute(),
})
if err != nil {
return errors.New("update DNS record failed: " + err.Error())
}
// 添加新的
err := this.raw.AddRecord(this.dnsDomain, &dnstypes.Record{
Id: "",
Name: recordName,
Type: dnstypes.RecordTypeTXT,
Value: value,
Route: this.raw.DefaultRoute(),
})
if err != nil {
return errors.New("create DNS record failed: " + err.Error())
}
return nil

View File

@@ -9,30 +9,11 @@ type Provider struct {
Code string `json:"code"`
Description string `json:"description"`
APIURL string `json:"apiURL"`
TestAPIURL string `json:"testAPIURL"`
RequireEAB bool `json:"requireEAB"`
EABDescription string `json:"eabDescription"`
}
func FindAllProviders() []*Provider {
return []*Provider{
{
Name: "Let's Encrypt",
Code: DefaultProviderCode,
Description: "非盈利组织Let's Encrypt提供的免费证书。",
APIURL: "https://acme-v02.api.letsencrypt.org/directory",
RequireEAB: false,
},
{
Name: "ZeroSSL",
Code: "zerossl",
Description: "相关文档 <a href=\"https://zerossl.com/documentation/acme/\" target=\"_blank\">https://zerossl.com/documentation/acme/</a>。",
APIURL: "https://acme.zerossl.com/v2/DV90",
RequireEAB: true,
EABDescription: "在官网<a href=\"https://app.zerossl.com/developer\" target=\"_blank\">[Developer]</a>页面底部点击\"Generate\"按钮生成。",
},
}
}
func FindProviderWithCode(code string) *Provider {
for _, provider := range FindAllProviders() {
if provider.Code == code {

View File

@@ -0,0 +1,24 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package acme
func FindAllProviders() []*Provider {
return []*Provider{
{
Name: "Let's Encrypt",
Code: DefaultProviderCode,
Description: "非盈利组织Let's Encrypt提供的免费证书。",
APIURL: "https://acme-v02.api.letsencrypt.org/directory",
RequireEAB: false,
},
{
Name: "ZeroSSL",
Code: "zerossl",
Description: "相关文档 <a href=\"https://zerossl.com/documentation/acme/\" target=\"_blank\">https://zerossl.com/documentation/acme/</a>。",
APIURL: "https://acme.zerossl.com/v2/DV90",
RequireEAB: true,
EABDescription: "在官网<a href=\"https://app.zerossl.com/developer\" target=\"_blank\">[Developer]</a>页面底部点击\"Generate\"按钮生成。",
},
}
}

View File

@@ -8,7 +8,8 @@ import (
"github.com/go-acme/lego/v4/lego"
acmelog "github.com/go-acme/lego/v4/log"
"github.com/go-acme/lego/v4/registration"
"io/ioutil"
"github.com/iwind/TeaGo/Tea"
"io"
"log"
)
@@ -40,6 +41,7 @@ func (this *Request) Run() (certData []byte, keyData []byte, err error) {
}
if this.task.Provider.RequireEAB && this.task.Account == nil {
err = errors.New("account should not be nil when provider require EAB")
return
}
switch this.task.AuthType {
@@ -55,7 +57,9 @@ func (this *Request) Run() (certData []byte, keyData []byte, err error) {
func (this *Request) runDNS() (certData []byte, keyData []byte, err error) {
if !this.debug {
acmelog.Logger = log.New(ioutil.Discard, "", log.LstdFlags)
if !Tea.IsTesting() {
acmelog.Logger = log.New(io.Discard, "", log.LstdFlags)
}
}
if this.task.User == nil {
@@ -75,7 +79,7 @@ func (this *Request) runDNS() (certData []byte, keyData []byte, err error) {
return
}
config := lego.NewConfig(this.task.User)
var config = lego.NewConfig(this.task.User)
config.Certificate.KeyType = certcrypto.RSA2048
config.CADirURL = this.task.Provider.APIURL
config.UserAgent = teaconst.ProductName + "/" + teaconst.Version
@@ -86,7 +90,7 @@ func (this *Request) runDNS() (certData []byte, keyData []byte, err error) {
}
// 注册用户
resource := this.task.User.GetRegistration()
var resource = this.task.User.GetRegistration()
if resource != nil {
resource, err = client.Registration.QueryRegistration()
if err != nil {
@@ -124,7 +128,7 @@ func (this *Request) runDNS() (certData []byte, keyData []byte, err error) {
}
// 申请证书
request := certificate.ObtainRequest{
var request = certificate.ObtainRequest{
Domains: this.task.Domains,
Bundle: true,
}
@@ -138,7 +142,9 @@ func (this *Request) runDNS() (certData []byte, keyData []byte, err error) {
func (this *Request) runHTTP() (certData []byte, keyData []byte, err error) {
if !this.debug {
acmelog.Logger = log.New(ioutil.Discard, "", log.LstdFlags)
if !Tea.IsTesting() {
acmelog.Logger = log.New(io.Discard, "", log.LstdFlags)
}
}
if this.task.User == nil {
@@ -146,7 +152,7 @@ func (this *Request) runHTTP() (certData []byte, keyData []byte, err error) {
return
}
config := lego.NewConfig(this.task.User)
var config = lego.NewConfig(this.task.User)
config.Certificate.KeyType = certcrypto.RSA2048
config.CADirURL = this.task.Provider.APIURL
config.UserAgent = teaconst.ProductName + "/" + teaconst.Version
@@ -157,7 +163,7 @@ func (this *Request) runHTTP() (certData []byte, keyData []byte, err error) {
}
// 注册用户
resource := this.task.User.GetRegistration()
var resource = this.task.User.GetRegistration()
if resource != nil {
resource, err = client.Registration.QueryRegistration()
if err != nil {
@@ -195,7 +201,7 @@ func (this *Request) runHTTP() (certData []byte, keyData []byte, err error) {
}
// 申请证书
request := certificate.ObtainRequest{
var request = certificate.ObtainRequest{
Domains: this.task.Domains,
Bundle: true,
}

View File

@@ -1,51 +1,108 @@
package apps
import (
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/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

@@ -2,9 +2,8 @@ package configs
import (
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
"github.com/go-yaml/yaml"
"github.com/iwind/TeaGo/Tea"
"io/ioutil"
"gopkg.in/yaml.v3"
"os"
"path/filepath"
)
@@ -42,7 +41,7 @@ func SharedAPIConfig() (*APIConfig, error) {
var data []byte
var err error
for _, path := range paths {
data, err = ioutil.ReadFile(path)
data, err = os.ReadFile(path)
if err == nil {
if path == localFile {
isFromLocal = true
@@ -63,7 +62,7 @@ func SharedAPIConfig() (*APIConfig, error) {
if !isFromLocal {
// 恢复文件
_ = ioutil.WriteFile(localFile, data, 0666)
_ = os.WriteFile(localFile, data, 0666)
}
// 恢复数据库文件
@@ -80,9 +79,9 @@ func SharedAPIConfig() (*APIConfig, error) {
for _, path := range paths {
_, err := os.Stat(path)
if err == nil {
data, err := ioutil.ReadFile(path)
data, err := os.ReadFile(path)
if err == nil {
_ = ioutil.WriteFile(dbConfigFile, data, 0666)
_ = os.WriteFile(dbConfigFile, data, 0666)
break
}
}
@@ -122,14 +121,58 @@ func (this *APIConfig) WriteFile(path string) error {
for _, backupDir := range backupDirs {
stat, err := os.Stat(backupDir)
if err == nil && stat.IsDir() {
_ = ioutil.WriteFile(backupDir+"/"+filename, data, 0666)
_ = os.WriteFile(backupDir+"/"+filename, data, 0666)
} else if err != nil && os.IsNotExist(err) {
err = os.Mkdir(backupDir, 0777)
if err == nil {
_ = ioutil.WriteFile(backupDir+"/"+filename, data, 0666)
_ = os.WriteFile(backupDir+"/"+filename, data, 0666)
}
}
}
return ioutil.WriteFile(path, data, 0666)
return os.WriteFile(path, data, 0666)
}
// ResetAPIConfig 重置配置
func ResetAPIConfig() error {
for _, filename := range []string{"api.yaml", "db.yaml"} {
// 重置 configs/api.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-api/api.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-api/api.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
}

View File

@@ -1,7 +1,7 @@
package teaconst
const (
Version = "0.4.3"
Version = "0.6.2"
ProductName = "Edge API"
ProcessName = "edge-api"
@@ -18,10 +18,8 @@ const (
// 其他节点版本号,用来检测是否有需要升级的节点
NodeVersion = "0.4.3"
UserNodeVersion = "0.3.1"
AuthorityNodeVersion = "0.0.2"
MonitorNodeVersion = "0.0.3"
DNSNodeVersion = "0.2.1"
ReportNodeVersion = "0.1.0"
NodeVersion = "0.6.2"
// SQLVersion SQL版本号
SQLVersion = "9"
)

View File

@@ -2,9 +2,18 @@
package teaconst
var (
IsPlus = false
MaxNodes int32 = 0
NodeId int64 = 0
Debug = false
import (
"crypto/sha1"
"fmt"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
"time"
)
var (
IsPlus = false
MaxNodes int32 = 0
NodeId int64 = 0
Debug = false
InstanceCode = fmt.Sprintf("%x", sha1.Sum([]byte("INSTANCE"+types.String(time.Now().UnixNano())+"@"+types.String(rands.Int64()))))
)

View File

@@ -3,6 +3,7 @@ package db
import (
"database/sql"
"database/sql/driver"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
_ "github.com/iwind/TeaGo/bootstrap"
@@ -42,3 +43,13 @@ func TestDB_Instance(t *testing.T) {
}
time.Sleep(100 * time.Second)
}
func TestDB_Reuse(t *testing.T) {
var dao = models.NewVersionDAO()
for i := 0; i < 20_000; i++ {
_, _, err := dao.Query(nil).Attr("version", i).Reuse(true).FindOne()
if err != nil {
t.Fatal(err)
}
}
}

View File

@@ -0,0 +1,33 @@
package accounts
import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
)
const (
OrderMethodStateEnabled = 1 // 已启用
OrderMethodStateDisabled = 0 // 已禁用
)
type OrderMethodDAO dbs.DAO
func NewOrderMethodDAO() *OrderMethodDAO {
return dbs.NewDAO(&OrderMethodDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeOrderMethods",
Model: new(OrderMethod),
PkName: "id",
},
}).(*OrderMethodDAO)
}
var SharedOrderMethodDAO *OrderMethodDAO
func init() {
dbs.OnReady(func() {
SharedOrderMethodDAO = NewOrderMethodDAO()
})
}

View File

@@ -0,0 +1,6 @@
package accounts_test
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
)

View File

@@ -0,0 +1,40 @@
package accounts
import "github.com/iwind/TeaGo/dbs"
// OrderMethod 订单支付方式
type OrderMethod struct {
Id uint32 `field:"id"` // ID
Name string `field:"name"` // 名称
IsOn bool `field:"isOn"` // 是否启用
Description string `field:"description"` // 描述
ParentCode string `field:"parentCode"` // 内置的父级代号
Code string `field:"code"` // 代号
Url string `field:"url"` // URL
Secret string `field:"secret"` // 密钥
Params dbs.JSON `field:"params"` // 参数
ClientType string `field:"clientType"` // 客户端类型
QrcodeTitle string `field:"qrcodeTitle"` // 二维码标题
Order uint32 `field:"order"` // 排序
State uint8 `field:"state"` // 状态
}
type OrderMethodOperator struct {
Id any // ID
Name any // 名称
IsOn any // 是否启用
Description any // 描述
ParentCode any // 内置的父级代号
Code any // 代号
Url any // URL
Secret any // 密钥
Params any // 参数
ClientType any // 客户端类型
QrcodeTitle any // 二维码标题
Order any // 排序
State any // 状态
}
func NewOrderMethodOperator() *OrderMethodOperator {
return &OrderMethodOperator{}
}

View File

@@ -0,0 +1 @@
package accounts

View File

@@ -1,80 +0,0 @@
package accounts
import (
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type UserAccountDailyStatDAO dbs.DAO
func NewUserAccountDailyStatDAO() *UserAccountDailyStatDAO {
return dbs.NewDAO(&UserAccountDailyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeUserAccountDailyStats",
Model: new(UserAccountDailyStat),
PkName: "id",
},
}).(*UserAccountDailyStatDAO)
}
var SharedUserAccountDailyStatDAO *UserAccountDailyStatDAO
func init() {
dbs.OnReady(func() {
SharedUserAccountDailyStatDAO = NewUserAccountDailyStatDAO()
})
}
// UpdateDailyStat 更新当天统计数据
func (this *UserAccountDailyStatDAO) UpdateDailyStat(tx *dbs.Tx) error {
var day = timeutil.Format("Ymd")
var month = timeutil.Format("Ym")
income, err := SharedUserAccountLogDAO.SumDailyEventTypes(tx, day, userconfigs.AccountIncomeEventTypes)
if err != nil {
return err
}
expense, err := SharedUserAccountLogDAO.SumDailyEventTypes(tx, day, userconfigs.AccountExpenseEventTypes)
if err != nil {
return err
}
if expense < 0 {
expense = -expense
}
return this.Query(tx).
InsertOrUpdateQuickly(maps.Map{
"day": day,
"month": month,
"income": income,
"expense": expense,
}, maps.Map{
"income": income,
"expense": expense,
})
}
// FindDailyStats 查看按天统计
func (this *UserAccountDailyStatDAO) FindDailyStats(tx *dbs.Tx, dayFrom string, dayTo string) (result []*UserAccountDailyStat, err error) {
_, err = this.Query(tx).
Between("day", dayFrom, dayTo).
Slice(&result).
FindAll()
return
}
// FindMonthlyStats 查看某月统计
func (this *UserAccountDailyStatDAO) FindMonthlyStats(tx *dbs.Tx, dayFrom string, dayTo string) (result []*UserAccountDailyStat, err error) {
_, err = this.Query(tx).
Result("SUM(income) AS income", "SUM(expense) AS expense", "month").
Between("day", dayFrom, dayTo).
Group("month").
Slice(&result).
FindAll()
return
}

View File

@@ -1,253 +0,0 @@
package accounts
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"time"
)
func init() {
dbs.OnReadyDone(func() {
goman.New(func() {
// 自动支付账单任务
var ticker = time.NewTicker(12 * time.Hour)
for range ticker.C {
if SharedUserAccountDAO.Instance != nil {
err := SharedUserAccountDAO.Instance.RunTx(func(tx *dbs.Tx) error {
return SharedUserAccountDAO.PayBills(tx)
})
if err != nil {
remotelogs.Error("USER_ACCOUNT_DAO", "pay bills task failed: "+err.Error())
}
}
}
})
})
}
type UserAccountDAO dbs.DAO
func NewUserAccountDAO() *UserAccountDAO {
return dbs.NewDAO(&UserAccountDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeUserAccounts",
Model: new(UserAccount),
PkName: "id",
},
}).(*UserAccountDAO)
}
var SharedUserAccountDAO *UserAccountDAO
func init() {
dbs.OnReady(func() {
SharedUserAccountDAO = NewUserAccountDAO()
})
}
// FindUserAccountWithUserId 根据用户ID查找用户账户
func (this *UserAccountDAO) FindUserAccountWithUserId(tx *dbs.Tx, userId int64) (*UserAccount, error) {
if userId <= 0 {
return nil, errors.New("invalid userId '" + types.String(userId) + "'")
}
// 用户是否存在
user, err := models.SharedUserDAO.FindEnabledUser(tx, userId, nil)
if err != nil {
return nil, err
}
if user == nil {
return nil, errors.New("invalid userId '" + types.String(userId) + "'")
}
account, err := this.Query(tx).
Attr("userId", userId).
Find()
if err != nil {
return nil, err
}
if account != nil {
return account.(*UserAccount), nil
}
var op = NewUserAccountOperator()
op.UserId = userId
_, err = this.SaveInt64(tx, op)
if err != nil {
return nil, err
}
return this.FindUserAccountWithUserId(tx, userId)
}
// FindUserAccountWithAccountId 根据ID查找用户账户
func (this *UserAccountDAO) FindUserAccountWithAccountId(tx *dbs.Tx, accountId int64) (*UserAccount, error) {
one, err := this.Query(tx).
Pk(accountId).
Find()
if one != nil {
return one.(*UserAccount), nil
}
return nil, err
}
// UpdateUserAccount 操作用户账户
func (this *UserAccountDAO) UpdateUserAccount(tx *dbs.Tx, accountId int64, delta float32, eventType userconfigs.AccountEventType, description string, params maps.Map) error {
account, err := this.FindUserAccountWithAccountId(tx, accountId)
if err != nil {
return err
}
if account == nil {
return errors.New("invalid account id '" + types.String(accountId) + "'")
}
var userId = int64(account.UserId)
var deltaFloat64 = float64(delta)
if deltaFloat64 < 0 && account.Total < -deltaFloat64 {
return errors.New("not enough account quota to decrease")
}
// 操作账户
err = this.Query(tx).
Pk(account.Id).
Set("total", dbs.SQL("total+:delta")).
Param("delta", delta).
UpdateQuickly()
if err != nil {
return err
}
// 生成日志
err = SharedUserAccountLogDAO.CreateAccountLog(tx, userId, accountId, delta, 0, eventType, description, params)
if err != nil {
return err
}
return nil
}
// UpdateUserAccountFrozen 操作用户账户冻结余额
func (this *UserAccountDAO) UpdateUserAccountFrozen(tx *dbs.Tx, userId int64, delta float32, eventType userconfigs.AccountEventType, description string, params maps.Map) error {
account, err := this.FindUserAccountWithUserId(tx, userId)
if err != nil {
return err
}
var deltaFloat64 = float64(delta)
if deltaFloat64 < 0 && account.TotalFrozen < -deltaFloat64 {
return errors.New("not enough account frozen quota to decrease")
}
// 操作账户
err = this.Query(tx).
Pk(account.Id).
Set("totalFrozen", dbs.SQL("total+:delta")).
Param("delta", delta).
UpdateQuickly()
if err != nil {
return err
}
// 生成日志
err = SharedUserAccountLogDAO.CreateAccountLog(tx, userId, int64(account.Id), 0, delta, eventType, description, params)
if err != nil {
return err
}
return nil
}
// CountAllAccounts 计算所有账户数量
func (this *UserAccountDAO) CountAllAccounts(tx *dbs.Tx, keyword string) (int64, error) {
var query = this.Query(tx)
if len(keyword) > 0 {
query.Where("userId IN (SELECT id FROM " + models.SharedUserDAO.Table + " WHERE state=1 AND (username LIKE :keyword OR fullname LIKE :keyword))")
query.Param("keyword", keyword)
} else {
query.Where("userId IN (SELECT id FROM " + models.SharedUserDAO.Table + " WHERE state=1)")
}
return query.Count()
}
// ListAccounts 列出单页账户
func (this *UserAccountDAO) ListAccounts(tx *dbs.Tx, keyword string, offset int64, size int64) (result []*UserAccount, err error) {
var query = this.Query(tx)
if len(keyword) > 0 {
query.Where("userId IN (SELECT id FROM " + models.SharedUserDAO.Table + " WHERE state=1 AND (username LIKE :keyword OR fullname LIKE :keyword))")
query.Param("keyword", keyword)
} else {
query.Where("userId IN (SELECT id FROM " + models.SharedUserDAO.Table + " WHERE state=1)")
}
_, err = query.
DescPk().
Offset(offset).
Limit(size).
Slice(&result).
FindAll()
return
}
// PayBills 尝试自动支付账单
func (this *UserAccountDAO) PayBills(tx *dbs.Tx) error {
bills, err := models.SharedUserBillDAO.FindUnpaidBills(tx, 10000)
if err != nil {
return err
}
// 先支付久远的
lists.Reverse(bills)
for _, bill := range bills {
if bill.Amount <= 0 {
err = models.SharedUserBillDAO.UpdateUserBillIsPaid(tx, int64(bill.Id), true)
if err != nil {
return err
}
continue
}
account, err := SharedUserAccountDAO.FindUserAccountWithUserId(tx, int64(bill.UserId))
if err != nil {
return err
}
if account == nil || account.Total < bill.Amount {
continue
}
// 扣款
err = SharedUserAccountDAO.UpdateUserAccount(tx, int64(account.Id), -float32(bill.Amount), userconfigs.AccountEventTypePayBill, "支付账单"+bill.Code, maps.Map{"billId": bill.Id})
if err != nil {
return err
}
// 改为已支付
err = models.SharedUserBillDAO.UpdateUserBillIsPaid(tx, int64(bill.Id), true)
if err != nil {
return err
}
}
return nil
}
// CheckUserAccount 检查用户账户
func (this *UserAccountDAO) CheckUserAccount(tx *dbs.Tx, userId int64, accountId int64) error {
exists, err := this.Query(tx).
Pk(accountId).
Attr("userId", userId).
Exist()
if err != nil {
return err
}
if !exists {
return models.ErrNotFound
}
return nil
}

View File

@@ -1,128 +0,0 @@
package accounts
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type UserAccountLogDAO dbs.DAO
func NewUserAccountLogDAO() *UserAccountLogDAO {
return dbs.NewDAO(&UserAccountLogDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeUserAccountLogs",
Model: new(UserAccountLog),
PkName: "id",
},
}).(*UserAccountLogDAO)
}
var SharedUserAccountLogDAO *UserAccountLogDAO
func init() {
dbs.OnReady(func() {
SharedUserAccountLogDAO = NewUserAccountLogDAO()
})
}
// CreateAccountLog 生成用户账户日志
func (this *UserAccountLogDAO) CreateAccountLog(tx *dbs.Tx, userId int64, accountId int64, delta float32, deltaFrozen float32, eventType userconfigs.AccountEventType, description string, params maps.Map) error {
var op = NewUserAccountLogOperator()
op.UserId = userId
op.AccountId = accountId
op.Delta = delta
op.DeltaFrozen = deltaFrozen
account, err := SharedUserAccountDAO.FindUserAccountWithAccountId(tx, accountId)
if err != nil {
return err
}
if account == nil {
return errors.New("invalid account id '" + types.String(accountId) + "'")
}
op.Total = account.Total
op.TotalFrozen = account.TotalFrozen
op.EventType = eventType
op.Description = description
if params == nil {
params = maps.Map{}
}
op.Params = params.AsJSON()
op.Day = timeutil.Format("Ymd")
err = this.Save(tx, op)
if err != nil {
return err
}
return SharedUserAccountDailyStatDAO.UpdateDailyStat(tx)
}
// CountAccountLogs 计算日志数量
func (this *UserAccountLogDAO) CountAccountLogs(tx *dbs.Tx, userId int64, accountId int64, keyword string, eventType string) (int64, error) {
var query = this.Query(tx)
if userId > 0 {
query.Attr("userId", userId)
}
if accountId > 0 {
query.Attr("accountId", accountId)
}
if len(keyword) > 0 {
query.Where("(userId IN (SELECT id FROM " + models.SharedUserDAO.Table + " WHERE state=1 AND (username LIKE :keyword OR fullname LIKE :keyword)) OR description LIKE :keyword)")
query.Param("keyword", "%"+keyword+"%")
}
if len(eventType) > 0 {
query.Attr("eventType", eventType)
}
return query.Count()
}
// ListAccountLogs 列出单页日志
func (this *UserAccountLogDAO) ListAccountLogs(tx *dbs.Tx, userId int64, accountId int64, keyword string, eventType string, offset int64, size int64) (result []*UserAccountLog, err error) {
var query = this.Query(tx)
if userId > 0 {
query.Attr("userId", userId)
}
if accountId > 0 {
query.Attr("accountId", accountId)
}
if len(keyword) > 0 {
query.Where("(userId IN (SELECT id FROM " + models.SharedUserDAO.Table + " WHERE state=1 AND (username LIKE :keyword OR fullname LIKE :keyword)) OR description LIKE :keyword)")
query.Param("keyword", "%"+keyword+"%")
}
if len(eventType) > 0 {
query.Attr("eventType", eventType)
}
_, err = query.
DescPk().
Offset(offset).
Limit(size).
Slice(&result).
FindAll()
return
}
// SumDailyEventTypes 统计某天数据总和
func (this *UserAccountLogDAO) SumDailyEventTypes(tx *dbs.Tx, day string, eventTypes []userconfigs.AccountEventType) (float32, error) {
if len(eventTypes) == 0 {
return 0, nil
}
result, err := this.Query(tx).
Attr("day", day).
Attr("eventType", eventTypes).
Sum("delta", 0)
if err != nil {
return 0, err
}
return types.Float32(result), nil
}

View File

@@ -1,19 +1,21 @@
package accounts
import "github.com/iwind/TeaGo/dbs"
// UserAccountLog 用户账户日志
type UserAccountLog struct {
Id uint64 `field:"id"` // ID
UserId uint64 `field:"userId"` // 用户ID
AccountId uint64 `field:"accountId"` // 账户ID
Delta float64 `field:"delta"` // 操作余额的数量(可为负)
DeltaFrozen float64 `field:"deltaFrozen"` // 操作冻结的数量(可为负)
Total float64 `field:"total"` // 操作后余额
TotalFrozen float64 `field:"totalFrozen"` // 操作后冻结余额
EventType string `field:"eventType"` // 类型
Description string `field:"description"` // 描述文字
Day string `field:"day"` // YYYYMMDD
CreatedAt uint64 `field:"createdAt"` // 时间
Params string `field:"params"` // 参数
Id uint64 `field:"id"` // ID
UserId uint64 `field:"userId"` // 用户ID
AccountId uint64 `field:"accountId"` // 账户ID
Delta float64 `field:"delta"` // 操作余额的数量(可为负)
DeltaFrozen float64 `field:"deltaFrozen"` // 操作冻结的数量(可为负)
Total float64 `field:"total"` // 操作后余额
TotalFrozen float64 `field:"totalFrozen"` // 操作后冻结余额
EventType string `field:"eventType"` // 类型
Description string `field:"description"` // 描述文字
Day string `field:"day"` // YYYYMMDD
CreatedAt uint64 `field:"createdAt"` // 时间
Params dbs.JSON `field:"params"` // 参数
}
type UserAccountLogOperator struct {

View File

@@ -0,0 +1,33 @@
package accounts
import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
)
const (
UserOrderStateEnabled = 1 // 已启用
UserOrderStateDisabled = 0 // 已禁用
)
type UserOrderDAO dbs.DAO
func NewUserOrderDAO() *UserOrderDAO {
return dbs.NewDAO(&UserOrderDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeUserOrders",
Model: new(UserOrder),
PkName: "id",
},
}).(*UserOrderDAO)
}
var SharedUserOrderDAO *UserOrderDAO
func init() {
dbs.OnReady(func() {
SharedUserOrderDAO = NewUserOrderDAO()
})
}

View File

@@ -0,0 +1,6 @@
package accounts_test
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
)

View File

@@ -0,0 +1,28 @@
package accounts
import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
)
type UserOrderLogDAO dbs.DAO
func NewUserOrderLogDAO() *UserOrderLogDAO {
return dbs.NewDAO(&UserOrderLogDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeUserOrderLogs",
Model: new(UserOrderLog),
PkName: "id",
},
}).(*UserOrderLogDAO)
}
var SharedUserOrderLogDAO *UserOrderLogDAO
func init() {
dbs.OnReady(func() {
SharedUserOrderLogDAO = NewUserOrderLogDAO()
})
}

View File

@@ -0,0 +1,6 @@
package accounts_test
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
)

View File

@@ -0,0 +1,28 @@
package accounts
import "github.com/iwind/TeaGo/dbs"
// UserOrderLog 订单日志
type UserOrderLog struct {
Id uint64 `field:"id"` // ID
AdminId uint64 `field:"adminId"` // 管理员ID
UserId uint64 `field:"userId"` // 用户ID
OrderId uint64 `field:"orderId"` // 订单ID
Status string `field:"status"` // 状态
Snapshot dbs.JSON `field:"snapshot"` // 状态快照
CreatedAt uint64 `field:"createdAt"` // 创建时间
}
type UserOrderLogOperator struct {
Id interface{} // ID
AdminId interface{} // 管理员ID
UserId interface{} // 用户ID
OrderId interface{} // 订单ID
Status interface{} // 状态
Snapshot interface{} // 状态快照
CreatedAt interface{} // 创建时间
}
func NewUserOrderLogOperator() *UserOrderLogOperator {
return &UserOrderLogOperator{}
}

View File

@@ -0,0 +1 @@
package accounts

View File

@@ -0,0 +1,40 @@
package accounts
import "github.com/iwind/TeaGo/dbs"
// UserOrder 用户订单
type UserOrder struct {
Id uint64 `field:"id"` // 用户订单
UserId uint64 `field:"userId"` // 用户ID
Code string `field:"code"` // 订单号
Type string `field:"type"` // 订单类型
MethodId uint32 `field:"methodId"` // 支付方式
Status string `field:"status"` // 订单状态
Amount float64 `field:"amount"` // 金额
Params dbs.JSON `field:"params"` // 附加参数
ExpiredAt uint64 `field:"expiredAt"` // 过期时间
CreatedAt uint64 `field:"createdAt"` // 创建时间
CancelledAt uint64 `field:"cancelledAt"` // 取消时间
FinishedAt uint64 `field:"finishedAt"` // 结束时间
State uint8 `field:"state"` // 状态
}
type UserOrderOperator struct {
Id interface{} // 用户订单
UserId interface{} // 用户ID
Code interface{} // 订单号
Type interface{} // 订单类型
MethodId interface{} // 支付方式
Status interface{} // 订单状态
Amount interface{} // 金额
Params interface{} // 附加参数
ExpiredAt interface{} // 过期时间
CreatedAt interface{} // 创建时间
CancelledAt interface{} // 取消时间
FinishedAt interface{} // 结束时间
State interface{} // 状态
}
func NewUserOrderOperator() *UserOrderOperator {
return &UserOrderOperator{}
}

View File

@@ -0,0 +1 @@
package accounts

View File

@@ -29,7 +29,7 @@ func init() {
// 创建认证信息
func (this *ACMEAuthenticationDAO) CreateAuth(tx *dbs.Tx, taskId int64, domain string, token string, key string) error {
op := NewACMEAuthenticationOperator()
var op = NewACMEAuthenticationOperator()
op.TaskId = taskId
op.Domain = domain
op.Token = token

View File

@@ -1,6 +1,7 @@
package acme
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
@@ -72,8 +73,9 @@ func (this *ACMEProviderAccountDAO) FindACMEProviderAccountName(tx *dbs.Tx, id i
}
// CreateAccount 创建账号
func (this *ACMEProviderAccountDAO) CreateAccount(tx *dbs.Tx, name string, providerCode string, eabKid string, eabKey string) (int64, error) {
func (this *ACMEProviderAccountDAO) CreateAccount(tx *dbs.Tx, userId int64, name string, providerCode string, eabKid string, eabKey string) (int64, error) {
var op = NewACMEProviderAccountOperator()
op.UserId = userId
op.Name = name
op.ProviderCode = providerCode
op.EabKid = eabKid
@@ -98,15 +100,18 @@ func (this *ACMEProviderAccountDAO) UpdateAccount(tx *dbs.Tx, accountId int64, n
}
// CountAllEnabledAccounts 计算账号数量
func (this *ACMEProviderAccountDAO) CountAllEnabledAccounts(tx *dbs.Tx) (int64, error) {
func (this *ACMEProviderAccountDAO) CountAllEnabledAccounts(tx *dbs.Tx, userId int64) (int64, error) {
return this.Query(tx).
State(ACMEProviderAccountStateEnabled).
Attr("userId", userId).
Count()
}
// ListEnabledAccounts 查找单页账号
func (this *ACMEProviderAccountDAO) ListEnabledAccounts(tx *dbs.Tx, offset int64, size int64) (result []*ACMEProviderAccount, err error) {
func (this *ACMEProviderAccountDAO) ListEnabledAccounts(tx *dbs.Tx, userId int64, offset int64, size int64) (result []*ACMEProviderAccount, err error) {
_, err = this.Query(tx).
State(ACMEProviderAccountStateEnabled).
Attr("userId", userId).
Offset(offset).
Limit(size).
DescPk().
@@ -116,12 +121,34 @@ func (this *ACMEProviderAccountDAO) ListEnabledAccounts(tx *dbs.Tx, offset int64
}
// FindAllEnabledAccountsWithProviderCode 根据服务商代号查找账号
func (this *ACMEProviderAccountDAO) FindAllEnabledAccountsWithProviderCode(tx *dbs.Tx, providerCode string) (result []*ACMEProviderAccount, err error) {
func (this *ACMEProviderAccountDAO) FindAllEnabledAccountsWithProviderCode(tx *dbs.Tx, userId int64, providerCode string) (result []*ACMEProviderAccount, err error) {
_, err = this.Query(tx).
State(ACMEProviderAccountStateEnabled).
Attr("providerCode", providerCode).
Attr("userId", userId).
DescPk().
Slice(&result).
FindAll()
return
}
// CheckUserAccount 检查是否为用户的服务商账号
func (this *ACMEProviderAccountDAO) CheckUserAccount(tx *dbs.Tx, userId int64, accountId int64) error {
if userId <= 0 || accountId <= 0 {
return models.ErrNotFound
}
b, err := this.Query(tx).
Pk(accountId).
State(ACMEProviderAccountStateEnabled).
Attr("userId", userId).
Exist()
if err != nil {
return err
}
if !b {
return models.ErrNotFound
}
return nil
}

View File

@@ -3,24 +3,26 @@ package acme
// ACMEProviderAccount ACME提供商
type ACMEProviderAccount struct {
Id uint64 `field:"id"` // ID
IsOn uint8 `field:"isOn"` // 是否启
UserId uint64 `field:"userId"` // 用户ID
IsOn bool `field:"isOn"` // 是否启用
Name string `field:"name"` // 名称
ProviderCode string `field:"providerCode"` // 代号
Error string `field:"error"` // 最后一条错误信息
EabKid string `field:"eabKid"` // KID
EabKey string `field:"eabKey"` // Key
Error string `field:"error"` // 最后一条错误信息
State uint8 `field:"state"` // 状态
}
type ACMEProviderAccountOperator struct {
Id interface{} // ID
IsOn interface{} // 是否启
Name interface{} // 名称
ProviderCode interface{} // 代号
Error interface{} // 最后一条错误信息
EabKid interface{} // KID
EabKey interface{} // Key
State interface{} // 状态
Id any // ID
UserId any // 用户ID
IsOn any // 是否启用
Name any // 名称
ProviderCode any // 代号
EabKid any // KID
EabKey any // Key
Error any // 最后一条错误信息
State any // 状态
}
func NewACMEProviderAccountOperator() *ACMEProviderAccountOperator {

View File

@@ -106,8 +106,9 @@ func (this *ACMETaskDAO) DisableAllTasksWithCertId(tx *dbs.Tx, certId int64) err
}
// CountAllEnabledACMETasks 计算所有任务数量
func (this *ACMETaskDAO) CountAllEnabledACMETasks(tx *dbs.Tx, adminId int64, userId int64, isAvailable bool, isExpired bool, expiringDays int64, keyword string) (int64, error) {
query := dbutils.NewQuery(tx, this, adminId, userId)
func (this *ACMETaskDAO) CountAllEnabledACMETasks(tx *dbs.Tx, userId int64, isAvailable bool, isExpired bool, expiringDays int64, keyword string) (int64, error) {
var query = this.Query(tx)
query.Attr("userId", userId) // 这个条件必须加上
if isAvailable || isExpired || expiringDays > 0 {
query.Gt("certId", 0)
@@ -125,11 +126,11 @@ func (this *ACMETaskDAO) CountAllEnabledACMETasks(tx *dbs.Tx, adminId int64, use
if len(keyword) > 0 {
query.Where("(domains LIKE :keyword)").
Param("keyword", "%"+keyword+"%")
Param("keyword", dbutils.QuoteLike(keyword))
}
if len(keyword) > 0 {
query.Where("domains LIKE :keyword").
Param("keyword", "%"+keyword+"%")
Param("keyword", dbutils.QuoteLike(keyword))
}
return query.State(ACMETaskStateEnabled).
@@ -137,8 +138,9 @@ func (this *ACMETaskDAO) CountAllEnabledACMETasks(tx *dbs.Tx, adminId int64, use
}
// ListEnabledACMETasks 列出单页任务
func (this *ACMETaskDAO) ListEnabledACMETasks(tx *dbs.Tx, adminId int64, userId int64, isAvailable bool, isExpired bool, expiringDays int64, keyword string, offset int64, size int64) (result []*ACMETask, err error) {
query := dbutils.NewQuery(tx, this, adminId, userId)
func (this *ACMETaskDAO) ListEnabledACMETasks(tx *dbs.Tx, userId int64, isAvailable bool, isExpired bool, expiringDays int64, keyword string, offset int64, size int64) (result []*ACMETask, err error) {
var query = this.Query(tx)
query.Attr("userId", userId) // 这个条件必须加上
if isAvailable || isExpired || expiringDays > 0 {
query.Gt("certId", 0)
@@ -155,7 +157,7 @@ func (this *ACMETaskDAO) ListEnabledACMETasks(tx *dbs.Tx, adminId int64, userId
}
if len(keyword) > 0 {
query.Where("(domains LIKE :keyword)").
Param("keyword", "%"+keyword+"%")
Param("keyword", dbutils.QuoteLike(keyword))
}
_, err = query.
State(ACMETaskStateEnabled).
@@ -169,7 +171,7 @@ func (this *ACMETaskDAO) ListEnabledACMETasks(tx *dbs.Tx, adminId int64, userId
// CreateACMETask 创建任务
func (this *ACMETaskDAO) CreateACMETask(tx *dbs.Tx, adminId int64, userId int64, authType acmeutils.AuthType, acmeUserId int64, dnsProviderId int64, dnsDomain string, domains []string, autoRenew bool, authURL string) (int64, error) {
op := NewACMETaskOperator()
var op = NewACMETaskOperator()
op.AdminId = adminId
op.UserId = userId
op.AuthType = authType
@@ -204,7 +206,7 @@ func (this *ACMETaskDAO) UpdateACMETask(tx *dbs.Tx, acmeTaskId int64, acmeUserId
return errors.New("invalid acmeTaskId")
}
op := NewACMETaskOperator()
var op = NewACMETaskOperator()
op.Id = acmeTaskId
op.AcmeUserId = acmeUserId
op.DnsProviderId = dnsProviderId
@@ -227,8 +229,13 @@ func (this *ACMETaskDAO) UpdateACMETask(tx *dbs.Tx, acmeTaskId int64, acmeUserId
}
// CheckACMETask 检查权限
func (this *ACMETaskDAO) CheckACMETask(tx *dbs.Tx, adminId int64, userId int64, acmeTaskId int64) (bool, error) {
return dbutils.NewQuery(tx, this, adminId, userId).
func (this *ACMETaskDAO) CheckACMETask(tx *dbs.Tx, userId int64, acmeTaskId int64) (bool, error) {
var query = this.Query(tx)
if userId > 0 {
query.Attr("userId", userId)
}
return query.
State(ACMETaskStateEnabled).
Pk(acmeTaskId).
Exist()
@@ -240,7 +247,7 @@ func (this *ACMETaskDAO) UpdateACMETaskCert(tx *dbs.Tx, taskId int64, certId int
return errors.New("invalid taskId")
}
op := NewACMETaskOperator()
var op = NewACMETaskOperator()
op.Id = taskId
op.CertId = certId
err := this.Save(tx, op)
@@ -271,7 +278,7 @@ func (this *ACMETaskDAO) runTaskWithoutLog(tx *dbs.Tx, taskId int64) (isOk bool,
errMsg = "找不到要执行的任务"
return
}
if task.IsOn != 1 {
if !task.IsOn {
errMsg = "任务没有启用"
return
}
@@ -319,7 +326,7 @@ func (this *ACMETaskDAO) runTaskWithoutLog(tx *dbs.Tx, taskId int64) (isOk bool,
return
}
remoteUser := acmeutils.NewUser(user.Email, privateKey, func(resource *registration.Resource) error {
var remoteUser = acmeutils.NewUser(user.Email, privateKey, func(resource *registration.Resource) error {
resourceJSON, err := json.Marshal(resource)
if err != nil {
return err
@@ -330,7 +337,7 @@ func (this *ACMETaskDAO) runTaskWithoutLog(tx *dbs.Tx, taskId int64) (isOk bool,
})
if len(user.Registration) > 0 {
err = remoteUser.SetRegistration([]byte(user.Registration))
err = remoteUser.SetRegistration(user.Registration)
if err != nil {
errMsg = "设置注册信息时出错:" + err.Error()
return
@@ -349,7 +356,7 @@ func (this *ACMETaskDAO) runTaskWithoutLog(tx *dbs.Tx, taskId int64) (isOk bool,
errMsg = "找不到DNS服务商账号"
return
}
providerInterface := dnsclients.FindProvider(dnsProvider.Type)
providerInterface := dnsclients.FindProvider(dnsProvider.Type, int64(dnsProvider.Id))
if providerInterface == nil {
errMsg = "暂不支持此类型的DNS服务商 '" + dnsProvider.Type + "'"
return
@@ -382,7 +389,7 @@ func (this *ACMETaskDAO) runTaskWithoutLog(tx *dbs.Tx, taskId int64) (isOk bool,
acmeTask.Provider = acmeProvider
acmeTask.Account = acmeAccount
acmeRequest := acmeutils.NewRequest(acmeTask)
var acmeRequest = acmeutils.NewRequest(acmeTask)
acmeRequest.OnAuth(func(domain, token, keyAuth string) {
err := SharedACMEAuthenticationDAO.CreateAuth(tx, taskId, domain, token, keyAuth)
if err != nil {
@@ -398,7 +405,7 @@ func (this *ACMETaskDAO) runTaskWithoutLog(tx *dbs.Tx, taskId int64) (isOk bool,
if err != nil {
remotelogs.Error("ACME", "encode auth data failed: '"+task.AuthURL+"'")
} else {
client := utils.SharedHttpClient(5 * time.Second)
var client = utils.SharedHttpClient(10 * time.Second)
req, err := http.NewRequest(http.MethodPost, task.AuthURL, bytes.NewReader(authJSON))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", teaconst.ProductName+"/"+teaconst.Version)
@@ -423,7 +430,7 @@ func (this *ACMETaskDAO) runTaskWithoutLog(tx *dbs.Tx, taskId int64) (isOk bool,
}
// 分析证书
sslConfig := &sslconfigs.SSLCertConfig{
var sslConfig = &sslconfigs.SSLCertConfig{
CertData: certData,
KeyData: keyData,
}
@@ -453,7 +460,7 @@ func (this *ACMETaskDAO) runTaskWithoutLog(tx *dbs.Tx, taskId int64) (isOk bool,
return
}
err = models.SharedSSLCertDAO.UpdateCert(tx, resultCertId, cert.IsOn == 1, cert.Name, cert.Description, cert.ServerName, cert.IsCA == 1, certData, keyData, sslConfig.TimeBeginAt, sslConfig.TimeEndAt, sslConfig.DNSNames, sslConfig.CommonNames)
err = models.SharedSSLCertDAO.UpdateCert(tx, resultCertId, cert.IsOn, cert.Name, cert.Description, cert.ServerName, cert.IsCA, certData, keyData, sslConfig.TimeBeginAt, sslConfig.TimeEndAt, sslConfig.DNSNames, sslConfig.CommonNames)
if err != nil {
errMsg = "证书生成成功,但是修改数据库中的证书信息时出错:" + err.Error()
return

View File

@@ -27,9 +27,9 @@ func init() {
})
}
// 生成日志
// CreateACMETaskLog 生成日志
func (this *ACMETaskLogDAO) CreateACMETaskLog(tx *dbs.Tx, taskId int64, isOk bool, errMsg string) error {
op := NewACMETaskLogOperator()
var op = NewACMETaskLogOperator()
op.TaskId = taskId
op.Error = errMsg
op.IsOk = isOk
@@ -37,7 +37,7 @@ func (this *ACMETaskLogDAO) CreateACMETaskLog(tx *dbs.Tx, taskId int64, isOk boo
return err
}
// 取得任务的最后一条执行日志
// FindLatestACMETasKLog 取得任务的最后一条执行日志
func (this *ACMETaskLogDAO) FindLatestACMETasKLog(tx *dbs.Tx, taskId int64) (*ACMETaskLog, error) {
one, err := this.Query(tx).
Attr("taskId", taskId).

View File

@@ -1,10 +1,10 @@
package acme
// ACME任务运行日志
// ACMETaskLog ACME任务运行日志
type ACMETaskLog struct {
Id uint64 `field:"id"` // ID
TaskId uint64 `field:"taskId"` // 任务ID
IsOk uint8 `field:"isOk"` // 是否成功
IsOk bool `field:"isOk"` // 是否成功
Error string `field:"error"` // 错误信息
CreatedAt uint64 `field:"createdAt"` // 运行时间
}

View File

@@ -1,21 +1,23 @@
package acme
import "github.com/iwind/TeaGo/dbs"
// ACMETask ACME任务
type ACMETask struct {
Id uint64 `field:"id"` // ID
AdminId uint32 `field:"adminId"` // 管理员ID
UserId uint32 `field:"userId"` // 用户ID
IsOn uint8 `field:"isOn"` // 是否启用
AcmeUserId uint32 `field:"acmeUserId"` // ACME用户ID
DnsDomain string `field:"dnsDomain"` // DNS主域名
DnsProviderId uint64 `field:"dnsProviderId"` // DNS服务商
Domains string `field:"domains"` // 证书域名
CreatedAt uint64 `field:"createdAt"` // 创建时间
State uint8 `field:"state"` // 状态
CertId uint64 `field:"certId"` // 生成的证书ID
AutoRenew uint8 `field:"autoRenew"` // 是否自动更新
AuthType string `field:"authType"` // 认证类型
AuthURL string `field:"authURL"` // 认证URL
Id uint64 `field:"id"` // ID
AdminId uint32 `field:"adminId"` // 管理员ID
UserId uint32 `field:"userId"` // 用户ID
IsOn bool `field:"isOn"` // 是否启用
AcmeUserId uint32 `field:"acmeUserId"` // ACME用户ID
DnsDomain string `field:"dnsDomain"` // DNS主域名
DnsProviderId uint64 `field:"dnsProviderId"` // DNS服务商
Domains dbs.JSON `field:"domains"` // 证书域名
CreatedAt uint64 `field:"createdAt"` // 创建时间
State uint8 `field:"state"` // 状态
CertId uint64 `field:"certId"` // 生成的证书ID
AutoRenew uint8 `field:"autoRenew"` // 是否自动更新
AuthType string `field:"authType"` // 认证类型
AuthURL string `field:"authURL"` // 认证URL
}
type ACMETaskOperator struct {

View File

@@ -5,13 +5,13 @@ import (
"github.com/iwind/TeaGo/logs"
)
// 将域名解析成字符串数组
// DecodeDomains 将域名解析成字符串数组
func (this *ACMETask) DecodeDomains() []string {
if len(this.Domains) == 0 || this.Domains == "null" {
if len(this.Domains) == 0 {
return nil
}
result := []string{}
err := json.Unmarshal([]byte(this.Domains), &result)
err := json.Unmarshal(this.Domains, &result)
if err != nil {
logs.Error(err)
return nil

View File

@@ -83,7 +83,7 @@ func (this *ACMEUserDAO) CreateACMEUser(tx *dbs.Tx, adminId int64, userId int64,
}
privateKeyText := base64.StdEncoding.EncodeToString(privateKeyData)
op := NewACMEUserOperator()
var op = NewACMEUserOperator()
op.AdminId = adminId
op.UserId = userId
op.ProviderCode = providerCode
@@ -104,7 +104,7 @@ func (this *ACMEUserDAO) UpdateACMEUser(tx *dbs.Tx, acmeUserId int64, descriptio
if acmeUserId <= 0 {
return errors.New("invalid acmeUserId")
}
op := NewACMEUserOperator()
var op = NewACMEUserOperator()
op.Id = acmeUserId
op.Description = description
err := this.Save(tx, op)
@@ -116,7 +116,7 @@ func (this *ACMEUserDAO) UpdateACMEUserRegistration(tx *dbs.Tx, acmeUserId int64
if acmeUserId <= 0 {
return errors.New("invalid acmeUserId")
}
op := NewACMEUserOperator()
var op = NewACMEUserOperator()
op.Id = acmeUserId
op.Registration = registrationJSON
err := this.Save(tx, op)

View File

@@ -1,18 +1,20 @@
package acme
import "github.com/iwind/TeaGo/dbs"
// ACMEUser ACME用户
type ACMEUser struct {
Id uint64 `field:"id"` // ID
AdminId uint32 `field:"adminId"` // 管理员ID
UserId uint32 `field:"userId"` // 用户ID
PrivateKey string `field:"privateKey"` // 私钥
Email string `field:"email"` // E-mail
CreatedAt uint64 `field:"createdAt"` // 创建时间
State uint8 `field:"state"` // 状态
Description string `field:"description"` // 备注介绍
Registration string `field:"registration"` // 注册信息
ProviderCode string `field:"providerCode"` // 服务商代号
AccountId uint64 `field:"accountId"` // 提供商ID
Id uint64 `field:"id"` // ID
AdminId uint32 `field:"adminId"` // 管理员ID
UserId uint32 `field:"userId"` // 用户ID
PrivateKey string `field:"privateKey"` // 私钥
Email string `field:"email"` // E-mail
CreatedAt uint64 `field:"createdAt"` // 创建时间
State uint8 `field:"state"` // 状态
Description string `field:"description"` // 备注介绍
Registration dbs.JSON `field:"registration"` // 注册信息
ProviderCode string `field:"providerCode"` // 服务商代号
AccountId uint64 `field:"accountId"` // 提供商ID
}
type ACMEUserOperator struct {

View File

@@ -44,11 +44,17 @@ func (this *AdminDAO) EnableAdmin(tx *dbs.Tx, id int64) (rowsAffected int64, err
}
// DisableAdmin 禁用条目
func (this *AdminDAO) DisableAdmin(tx *dbs.Tx, id int64) (rowsAffected int64, err error) {
return this.Query(tx).
Pk(id).
func (this *AdminDAO) DisableAdmin(tx *dbs.Tx, adminId int64) error {
err := this.Query(tx).
Pk(adminId).
Set("state", AdminStateDisabled).
Update()
UpdateQuickly()
if err != nil {
return err
}
// 删除AccessTokens
return SharedAPIAccessTokenDAO.DeleteAccessTokens(tx, adminId, 0)
}
// FindEnabledAdmin 查找启用中的条目
@@ -63,6 +69,19 @@ func (this *AdminDAO) FindEnabledAdmin(tx *dbs.Tx, id int64) (*Admin, error) {
return result.(*Admin), err
}
// FindBasicAdmin 查找管理员基本信息
func (this *AdminDAO) FindBasicAdmin(tx *dbs.Tx, id int64) (*Admin, error) {
result, err := this.Query(tx).
Result("id", "username", "fullname").
Pk(id).
Attr("state", AdminStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*Admin), err
}
// ExistEnabledAdmin 检查管理员是否存在
func (this *AdminDAO) ExistEnabledAdmin(tx *dbs.Tx, adminId int64) (bool, error) {
return this.Query(tx).
@@ -115,7 +134,7 @@ func (this *AdminDAO) UpdateAdminPassword(tx *dbs.Tx, adminId int64, password st
if adminId <= 0 {
return errors.New("invalid adminId")
}
op := NewAdminOperator()
var op = NewAdminOperator()
op.Id = adminId
op.Password = stringutil.Md5(password)
err := this.Save(tx, op)
@@ -124,7 +143,7 @@ func (this *AdminDAO) UpdateAdminPassword(tx *dbs.Tx, adminId int64, password st
// CreateAdmin 创建管理员
func (this *AdminDAO) CreateAdmin(tx *dbs.Tx, username string, canLogin bool, password string, fullname string, isSuper bool, modulesJSON []byte) (int64, error) {
op := NewAdminOperator()
var op = NewAdminOperator()
op.IsOn = true
op.State = AdminStateEnabled
op.Username = username
@@ -149,7 +168,7 @@ func (this *AdminDAO) UpdateAdminInfo(tx *dbs.Tx, adminId int64, fullname string
if adminId <= 0 {
return errors.New("invalid adminId")
}
op := NewAdminOperator()
var op = NewAdminOperator()
op.Id = adminId
op.Fullname = fullname
err := this.Save(tx, op)
@@ -161,7 +180,7 @@ func (this *AdminDAO) UpdateAdmin(tx *dbs.Tx, adminId int64, username string, ca
if adminId <= 0 {
return errors.New("invalid adminId")
}
op := NewAdminOperator()
var op = NewAdminOperator()
op.Id = adminId
op.Fullname = fullname
op.Username = username
@@ -177,7 +196,19 @@ func (this *AdminDAO) UpdateAdmin(tx *dbs.Tx, adminId int64, username string, ca
}
op.IsOn = isOn
err := this.Save(tx, op)
return err
if err != nil {
return err
}
if !isOn {
// 删除AccessTokens
err = SharedAPIAccessTokenDAO.DeleteAccessTokens(tx, adminId, 0)
if err != nil {
return err
}
}
return nil
}
// CheckAdminUsername 检查用户名是否存在
@@ -198,7 +229,7 @@ func (this *AdminDAO) UpdateAdminLogin(tx *dbs.Tx, adminId int64, username strin
if adminId <= 0 {
return errors.New("invalid adminId")
}
op := NewAdminOperator()
var op = NewAdminOperator()
op.Id = adminId
op.Username = username
if len(password) > 0 {
@@ -213,7 +244,7 @@ func (this *AdminDAO) UpdateAdminModules(tx *dbs.Tx, adminId int64, allowModules
if adminId <= 0 {
return errors.New("invalid adminId")
}
op := NewAdminOperator()
var op = NewAdminOperator()
op.Id = adminId
op.Modules = allowModulesJSON
err := this.Save(tx, op)

View File

@@ -1,34 +1,36 @@
package models
import "github.com/iwind/TeaGo/dbs"
// Admin 管理员
type Admin struct {
Id uint32 `field:"id"` // ID
IsOn uint8 `field:"isOn"` // 是否启用
Username string `field:"username"` // 用户名
Password string `field:"password"` // 密码
Fullname string `field:"fullname"` // 全名
IsSuper uint8 `field:"isSuper"` // 是否为超级管理员
CreatedAt uint64 `field:"createdAt"` // 创建时间
UpdatedAt uint64 `field:"updatedAt"` // 修改时间
State uint8 `field:"state"` // 状态
Modules string `field:"modules"` // 允许的模块
CanLogin uint8 `field:"canLogin"` // 是否可以登录
Theme string `field:"theme"` // 模板设置
Id uint32 `field:"id"` // ID
IsOn bool `field:"isOn"` // 是否启用
Username string `field:"username"` // 用户名
Password string `field:"password"` // 密码
Fullname string `field:"fullname"` // 全名
IsSuper bool `field:"isSuper"` // 是否为超级管理员
CreatedAt uint64 `field:"createdAt"` // 创建时间
UpdatedAt uint64 `field:"updatedAt"` // 修改时间
State uint8 `field:"state"` // 状态
Modules dbs.JSON `field:"modules"` // 允许的模块
CanLogin bool `field:"canLogin"` // 是否可以登录
Theme string `field:"theme"` // 模板设置
}
type AdminOperator struct {
Id interface{} // ID
IsOn interface{} // 是否启用
Username interface{} // 用户名
Password interface{} // 密码
Fullname interface{} // 全名
IsSuper interface{} // 是否为超级管理员
CreatedAt interface{} // 创建时间
UpdatedAt interface{} // 修改时间
State interface{} // 状态
Modules interface{} // 允许的模块
CanLogin interface{} // 是否可以登录
Theme interface{} // 模板设置
Id any // ID
IsOn any // 是否启用
Username any // 用户名
Password any // 密码
Fullname any // 全名
IsSuper any // 是否为超级管理员
CreatedAt any // 创建时间
UpdatedAt any // 修改时间
State any // 状态
Modules any // 允许的模块
CanLogin any // 是否可以登录
Theme any // 模板设置
}
func NewAdminOperator() *AdminOperator {

View File

@@ -56,7 +56,7 @@ func (this *APIAccessTokenDAO) GenerateAccessToken(tx *dbs.Tx, adminId int64, us
token = rands.String(128) // TODO 增强安全性,将来使用 base64_encode(encrypt(salt+random)) 算法来代替
expiresAt = time.Now().Unix() + 7200
op := NewAPIAccessTokenOperator()
var op = NewAPIAccessTokenOperator()
if accessToken != nil {
op.Id = accessToken.(*APIAccessToken).Id
@@ -81,3 +81,16 @@ func (this *APIAccessTokenDAO) FindAccessToken(tx *dbs.Tx, token string) (*APIAc
}
return one.(*APIAccessToken), nil
}
// DeleteAccessTokens 删除用户的令牌
func (this *APIAccessTokenDAO) DeleteAccessTokens(tx *dbs.Tx, adminId int64, userId int64) error {
var query = this.Query(tx)
if adminId > 0 {
query.Attr("adminId", adminId)
} else if userId > 0 {
query.Attr("userId", userId)
} else {
return nil
}
return query.DeleteQuickly()
}

View File

@@ -3,6 +3,8 @@ package models
import (
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeAPI/internal/configs"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
_ "github.com/go-sql-driver/mysql"
@@ -49,16 +51,29 @@ func (this *APINodeDAO) EnableAPINode(tx *dbs.Tx, id int64) error {
Pk(id).
Set("state", APINodeStateEnabled).
Update()
return err
if err != nil {
return err
}
return this.NotifyUpdate(tx, id)
}
// DisableAPINode 禁用条目
func (this *APINodeDAO) DisableAPINode(tx *dbs.Tx, id int64) error {
func (this *APINodeDAO) DisableAPINode(tx *dbs.Tx, nodeId int64) error {
_, err := this.Query(tx).
Pk(id).
Pk(nodeId).
Set("state", APINodeStateDisabled).
Update()
return err
if err != nil {
return err
}
err = this.NotifyUpdate(tx, nodeId)
if err != nil {
return err
}
// 删除运行日志
return SharedNodeLogDAO.DeleteNodeLogs(tx, nodeconfigs.NodeRoleAPI, nodeId)
}
// FindEnabledAPINode 查找启用中的条目
@@ -119,7 +134,7 @@ func (this *APINodeDAO) CreateAPINode(tx *dbs.Tx, name string, description strin
return
}
op := NewAPINodeOperator()
var op = NewAPINodeOperator()
op.IsOn = isOn
op.UniqueId = uniqueId
op.Secret = secret
@@ -149,16 +164,33 @@ func (this *APINodeDAO) CreateAPINode(tx *dbs.Tx, name string, description strin
return
}
err = this.NotifyUpdate(tx, types.Int64(op.Id))
if err != nil {
remotelogs.Error("API_NODE_DAO", err.Error())
}
return types.Int64(op.Id), nil
}
// UpdateAPINode 修改API节点
func (this *APINodeDAO) UpdateAPINode(tx *dbs.Tx, nodeId int64, name string, description string, httpJSON []byte, httpsJSON []byte, restIsOn bool, restHTTPJSON []byte, restHTTPSJSON []byte, accessAddrsJSON []byte, isOn bool) error {
func (this *APINodeDAO) UpdateAPINode(tx *dbs.Tx, nodeId int64, name string, description string, httpJSON []byte, httpsJSON []byte, restIsOn bool, restHTTPJSON []byte, restHTTPSJSON []byte, accessAddrsJSON []byte, isOn bool, isPrimary bool) error {
if nodeId <= 0 {
return errors.New("invalid nodeId")
}
op := NewAPINodeOperator()
// 取消别的Primary
if isPrimary {
err := this.Query(tx).
Neq("id", nodeId).
Attr("isPrimary", true).
Set("isPrimary", false).
UpdateQuickly()
if err != nil {
return err
}
}
var op = NewAPINodeOperator()
op.Id = nodeId
op.Name = name
op.Description = description
@@ -191,8 +223,13 @@ func (this *APINodeDAO) UpdateAPINode(tx *dbs.Tx, nodeId int64, name string, des
op.AccessAddrs = "[]"
}
op.IsPrimary = isPrimary
err := this.Save(tx, op)
return err
if err != nil {
return err
}
return this.NotifyUpdate(tx, nodeId)
}
// FindAllEnabledAPINodes 列出所有可用API节点
@@ -294,23 +331,6 @@ func (this *APINodeDAO) UpdateAPINodeStatus(tx *dbs.Tx, apiNodeId int64, statusJ
return err
}
// 生成唯一ID
func (this *APINodeDAO) genUniqueId(tx *dbs.Tx) (string, error) {
for {
uniqueId := rands.HexString(32)
ok, err := this.Query(tx).
Attr("uniqueId", uniqueId).
Exist()
if err != nil {
return "", err
}
if ok {
continue
}
return uniqueId, nil
}
}
// CountAllLowerVersionNodes 计算所有节点中低于某个版本的节点数量
func (this *APINodeDAO) CountAllLowerVersionNodes(tx *dbs.Tx, version string) (int64, error) {
return this.Query(tx).
@@ -384,3 +404,114 @@ func (this *APINodeDAO) FindAllEnabledAPIAccessIPs(tx *dbs.Tx, cacheMap *utils.C
return result, nil
}
// CheckAPINodeIsPrimary 检查当前节点是否为Primary节点
func (this *APINodeDAO) CheckAPINodeIsPrimary(tx *dbs.Tx) (bool, error) {
config, err := configs.SharedAPIConfig()
if err != nil {
return false, err
}
isPrimary, err := this.Query(tx).
State(APINodeStateEnabled).
Attr("uniqueId", config.NodeId).
Attr("isPrimary", true).
Exist()
if err != nil {
return false, err
}
if isPrimary {
return true, nil
}
// 检查是否有别的Primary节点
count, err := this.Query(tx).
State(APINodeStateEnabled).
Attr("isOn", true).
Attr("isPrimary", true).
Count()
if err != nil {
return false, err
}
if count == 0 {
err = this.ResetPrimaryAPINode(tx)
if err != nil {
return false, err
}
return true, nil
}
return false, nil
}
// CheckAPINodeIsPrimaryWithoutErr 检查当前节点是否为Primary节点并忽略错误
func (this *APINodeDAO) CheckAPINodeIsPrimaryWithoutErr() bool {
b, err := this.CheckAPINodeIsPrimary(nil)
return b && err == nil
}
// ResetPrimaryAPINode 重置Primary节点
func (this *APINodeDAO) ResetPrimaryAPINode(tx *dbs.Tx) error {
// 当前是否有Primary节点
apiNode, err := this.Query(tx).
State(APINodeStateEnabled).
Attr("isOn", true).
Attr("isPrimary", true).
Find()
if err != nil {
return err
}
if apiNode == nil {
// 选择一个作为Primary
// TODO 将来需要考虑API节点离线的情况
apiNodeId, err := this.Query(tx).
State(APINodeStateEnabled).
Attr("isOn", true).
ResultPk().
FindInt64Col(0)
if err != nil {
return err
}
if apiNodeId > 0 {
err = this.Query(tx).
Pk(apiNodeId).
Set("isPrimary", true).
UpdateQuickly()
if err != nil {
return err
}
}
}
return nil
}
// NotifyUpdate 通知变更
func (this *APINodeDAO) NotifyUpdate(tx *dbs.Tx, apiNodeId int64) error {
// suppress IDE warning
_ = apiNodeId
err := this.ResetPrimaryAPINode(tx)
if err != nil {
return err
}
return nil
}
// 生成唯一ID
func (this *APINodeDAO) genUniqueId(tx *dbs.Tx) (string, error) {
for {
uniqueId := rands.HexString(32)
ok, err := this.Query(tx).
Attr("uniqueId", uniqueId).
Exist()
if err != nil {
return "", err
}
if ok {
continue
}
return uniqueId, nil
}
}

View File

@@ -34,6 +34,16 @@ func TestAPINodeDAO_FindAllEnabledAPIAccessIPs(t *testing.T) {
t.Log(NewAPINodeDAO().FindAllEnabledAPIAccessIPs(nil, cacheMap))
}
func TestAPINodeDAO_CheckAPINodeIsPrimary(t *testing.T) {
var dao = NewAPINodeDAO()
t.Log(dao.CheckAPINodeIsPrimary(nil))
}
func TestAPINodeDAO_ResetPrimaryAPINode(t *testing.T) {
var dao = NewAPINodeDAO()
t.Log(dao.ResetPrimaryAPINode(nil))
}
func BenchmarkAPINodeDAO_New(b *testing.B) {
runtime.GOMAXPROCS(1)
for i := 0; i < b.N; i++ {

View File

@@ -1,26 +1,29 @@
package models
// API节点
import "github.com/iwind/TeaGo/dbs"
// APINode API节点
type APINode struct {
Id uint32 `field:"id"` // ID
IsOn uint8 `field:"isOn"` // 是否启用
ClusterId uint32 `field:"clusterId"` // 专用集群ID
UniqueId string `field:"uniqueId"` // 唯一ID
Secret string `field:"secret"` // 密钥
Name string `field:"name"` // 名称
Description string `field:"description"` // 描述
Http string `field:"http"` // 监听的HTTP配置
Https string `field:"https"` // 监听的HTTPS配置
RestIsOn uint8 `field:"restIsOn"` // 是否开放REST
RestHTTP string `field:"restHTTP"` // REST HTTP配置
RestHTTPS string `field:"restHTTPS"` // REST HTTPS配置
AccessAddrs string `field:"accessAddrs"` // 外部访问地址
Order uint32 `field:"order"` // 排序
State uint8 `field:"state"` // 状态
CreatedAt uint64 `field:"createdAt"` // 创建时间
AdminId uint32 `field:"adminId"` // 管理员ID
Weight uint32 `field:"weight"` // 权重
Status string `field:"status"` // 运行状态
Id uint32 `field:"id"` // ID
IsOn bool `field:"isOn"` // 是否启用
ClusterId uint32 `field:"clusterId"` // 专用集群ID
UniqueId string `field:"uniqueId"` // 唯一ID
Secret string `field:"secret"` // 密钥
Name string `field:"name"` // 名称
Description string `field:"description"` // 描述
Http dbs.JSON `field:"http"` // 监听的HTTP配置
Https dbs.JSON `field:"https"` // 监听的HTTPS配置
RestIsOn uint8 `field:"restIsOn"` // 是否开放REST
RestHTTP dbs.JSON `field:"restHTTP"` // REST HTTP配置
RestHTTPS dbs.JSON `field:"restHTTPS"` // REST HTTPS配置
AccessAddrs dbs.JSON `field:"accessAddrs"` // 外部访问地址
Order uint32 `field:"order"` // 排序
State uint8 `field:"state"` // 状态
CreatedAt uint64 `field:"createdAt"` // 创建时间
AdminId uint32 `field:"adminId"` // 管理员ID
Weight uint32 `field:"weight"` // 权重
Status dbs.JSON `field:"status"` // 运行状态
IsPrimary bool `field:"isPrimary"` // 是否为主API节点
}
type APINodeOperator struct {
@@ -43,6 +46,7 @@ type APINodeOperator struct {
AdminId interface{} // 管理员ID
Weight interface{} // 权重
Status interface{} // 运行状态
IsPrimary interface{} // 是否为主API节点
}
func NewAPINodeOperator() *APINodeOperator {

View File

@@ -13,7 +13,7 @@ func (this *APINode) DecodeHTTP() (*serverconfigs.HTTPProtocolConfig, error) {
return nil, nil
}
config := &serverconfigs.HTTPProtocolConfig{}
err := json.Unmarshal([]byte(this.Http), config)
err := json.Unmarshal(this.Http, config)
if err != nil {
return nil, err
}
@@ -32,7 +32,7 @@ func (this *APINode) DecodeHTTPS(tx *dbs.Tx, cacheMap *utils.CacheMap) (*serverc
return nil, nil
}
config := &serverconfigs.HTTPSProtocolConfig{}
err := json.Unmarshal([]byte(this.Https), config)
err := json.Unmarshal(this.Https, config)
if err != nil {
return nil, err
}
@@ -43,9 +43,9 @@ func (this *APINode) DecodeHTTPS(tx *dbs.Tx, cacheMap *utils.CacheMap) (*serverc
}
if config.SSLPolicyRef != nil {
policyId := config.SSLPolicyRef.SSLPolicyId
var policyId = config.SSLPolicyRef.SSLPolicyId
if policyId > 0 {
sslPolicy, err := SharedSSLPolicyDAO.ComposePolicyConfig(tx, policyId, cacheMap)
sslPolicy, err := SharedSSLPolicyDAO.ComposePolicyConfig(tx, policyId, false, cacheMap)
if err != nil {
return nil, err
}
@@ -70,7 +70,7 @@ func (this *APINode) DecodeAccessAddrs() ([]*serverconfigs.NetworkAddressConfig,
}
addrConfigs := []*serverconfigs.NetworkAddressConfig{}
err := json.Unmarshal([]byte(this.AccessAddrs), &addrConfigs)
err := json.Unmarshal(this.AccessAddrs, &addrConfigs)
if err != nil {
return nil, err
}
@@ -105,7 +105,7 @@ func (this *APINode) DecodeRestHTTP() (*serverconfigs.HTTPProtocolConfig, error)
return nil, nil
}
config := &serverconfigs.HTTPProtocolConfig{}
err := json.Unmarshal([]byte(this.RestHTTP), config)
err := json.Unmarshal(this.RestHTTP, config)
if err != nil {
return nil, err
}
@@ -130,7 +130,7 @@ func (this *APINode) DecodeRestHTTPS(tx *dbs.Tx, cacheMap *utils.CacheMap) (*ser
return nil, nil
}
config := &serverconfigs.HTTPSProtocolConfig{}
err := json.Unmarshal([]byte(this.RestHTTPS), config)
err := json.Unmarshal(this.RestHTTPS, config)
if err != nil {
return nil, err
}
@@ -143,7 +143,7 @@ func (this *APINode) DecodeRestHTTPS(tx *dbs.Tx, cacheMap *utils.CacheMap) (*ser
if config.SSLPolicyRef != nil {
policyId := config.SSLPolicyRef.SSLPolicyId
if policyId > 0 {
sslPolicy, err := SharedSSLPolicyDAO.ComposePolicyConfig(tx, policyId, cacheMap)
sslPolicy, err := SharedSSLPolicyDAO.ComposePolicyConfig(tx, policyId, false, cacheMap)
if err != nil {
return nil, err
}

View File

@@ -77,7 +77,7 @@ func (this *ApiTokenDAO) FindEnabledTokenWithNodeCacheable(tx *dbs.Tx, nodeId st
State(ApiTokenStateEnabled).
Find()
if one != nil {
token := one.(*ApiToken)
token = one.(*ApiToken)
SharedCacheLocker.Lock()
apiTokenCacheMap[nodeId] = token
SharedCacheLocker.Unlock()
@@ -112,7 +112,7 @@ func (this *ApiTokenDAO) FindEnabledTokenWithRole(tx *dbs.Tx, role string) (*Api
// CreateAPIToken 保存API Token
func (this *ApiTokenDAO) CreateAPIToken(tx *dbs.Tx, nodeId string, secret string, role nodeconfigs.NodeRole) error {
op := NewApiTokenOperator()
var op = NewApiTokenOperator()
op.NodeId = nodeId
op.Secret = secret
op.Role = role

View File

@@ -1,13 +1,9 @@
package authority
import (
"encoding/json"
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type AuthorityKeyDAO dbs.DAO
@@ -33,63 +29,3 @@ func init() {
_, _ = SharedAuthorityKeyDAO.IsPlus(nil)
})
}
// UpdateKey 设置Key
func (this *AuthorityKeyDAO) UpdateKey(tx *dbs.Tx, value string, dayFrom string, dayTo string, hostname string, macAddresses []string, company string) error {
one, err := this.Query(tx).
AscPk().
Find()
if err != nil {
return err
}
op := NewAuthorityKeyOperator()
if one != nil {
op.Id = one.(*AuthorityKey).Id
}
op.Value = value
op.DayFrom = dayFrom
op.DayTo = dayTo
op.Hostname = hostname
if len(macAddresses) == 0 {
macAddresses = []string{}
}
macAddressesJSON, err := json.Marshal(macAddresses)
if err != nil {
return err
}
op.MacAddresses = macAddressesJSON
op.Company = company
op.UpdatedAt = time.Now().Unix()
return this.Save(tx, op)
}
// ReadKey 读取Key
func (this *AuthorityKeyDAO) ReadKey(tx *dbs.Tx) (key *AuthorityKey, err error) {
one, err := this.Query(tx).
AscPk().
Find()
if err != nil {
return nil, err
}
if one == nil {
return nil, nil
}
key = one.(*AuthorityKey)
// 顺便更新相关变量
if key.DayTo >= timeutil.Format("Y-m-d") {
teaconst.IsPlus = true
}
return
}
// ResetKey 重置Key
func (this *AuthorityKeyDAO) ResetKey(tx *dbs.Tx) error {
_, err := this.Query(tx).
Delete()
return err
}

View File

@@ -1,23 +0,0 @@
package authority
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
"testing"
)
func TestAuthorityKeyDAO_UpdateValue(t *testing.T) {
err := NewAuthorityKeyDAO().UpdateKey(nil, "12345678", "", "", "", []string{}, "")
if err != nil {
t.Fatal(err)
}
t.Log("ok")
}
func TestAuthorityKeyDAO_ReadValue(t *testing.T) {
value, err := NewAuthorityKeyDAO().ReadKey(nil)
if err != nil {
t.Fatal(err)
}
t.Log(value)
}

View File

@@ -1,15 +1,17 @@
package authority
import "github.com/iwind/TeaGo/dbs"
// AuthorityKey 企业版认证信息
type AuthorityKey struct {
Id uint32 `field:"id"` // ID
Value string `field:"value"` // Key值
DayFrom string `field:"dayFrom"` // 开始日期
DayTo string `field:"dayTo"` // 结束日期
Hostname string `field:"hostname"` // Hostname
MacAddresses string `field:"macAddresses"` // MAC地址
UpdatedAt uint64 `field:"updatedAt"` // 创建/修改时间
Company string `field:"company"` // 公司组织
Id uint32 `field:"id"` // ID
Value string `field:"value"` // Key值
DayFrom string `field:"dayFrom"` // 开始日期
DayTo string `field:"dayTo"` // 结束日期
Hostname string `field:"hostname"` // Hostname
MacAddresses dbs.JSON `field:"macAddresses"` // MAC地址
UpdatedAt uint64 `field:"updatedAt"` // 创建/修改时间
Company string `field:"company"` // 公司组织
}
type AuthorityKeyOperator struct {

View File

@@ -1,6 +1,7 @@
package authority
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
@@ -119,7 +120,7 @@ func (this *AuthorityNodeDAO) CreateAuthorityNode(tx *dbs.Tx, name string, descr
return
}
op := NewAuthorityNodeOperator()
var op = NewAuthorityNodeOperator()
op.IsOn = isOn
op.UniqueId = uniqueId
op.Secret = secret
@@ -140,7 +141,7 @@ func (this *AuthorityNodeDAO) UpdateAuthorityNode(tx *dbs.Tx, nodeId int64, name
return errors.New("invalid nodeId")
}
op := NewAuthorityNodeOperator()
var op = NewAuthorityNodeOperator()
op.Id = nodeId
op.Name = name
op.Description = description
@@ -188,13 +189,19 @@ func (this *AuthorityNodeDAO) GenUniqueId(tx *dbs.Tx) (string, error) {
}
// UpdateNodeStatus 更改节点状态
func (this *AuthorityNodeDAO) UpdateNodeStatus(tx *dbs.Tx, nodeId int64, statusJSON []byte) error {
if statusJSON == nil {
func (this *AuthorityNodeDAO) UpdateNodeStatus(tx *dbs.Tx, nodeId int64, nodeStatus *nodeconfigs.NodeStatus) error {
if nodeStatus == nil {
return nil
}
_, err := this.Query(tx).
nodeStatusJSON, err := json.Marshal(nodeStatus)
if err != nil {
return err
}
_, err = this.Query(tx).
Pk(nodeId).
Set("status", string(statusJSON)).
Set("status", nodeStatusJSON).
Update()
return err
}

View File

@@ -1,19 +1,21 @@
package authority
import "github.com/iwind/TeaGo/dbs"
// AuthorityNode 监控节点
type AuthorityNode struct {
Id uint32 `field:"id"` // ID
IsOn uint8 `field:"isOn"` // 是否启用
UniqueId string `field:"uniqueId"` // 唯一ID
Secret string `field:"secret"` // 密钥
Name string `field:"name"` // 名称
Description string `field:"description"` // 描述
Order uint32 `field:"order"` // 排序
State uint8 `field:"state"` // 状态
CreatedAt uint64 `field:"createdAt"` // 创建时间
AdminId uint32 `field:"adminId"` // 管理员ID
Weight uint32 `field:"weight"` // 权重
Status string `field:"status"` // 运行状态
Id uint32 `field:"id"` // ID
IsOn bool `field:"isOn"` // 是否启用
UniqueId string `field:"uniqueId"` // 唯一ID
Secret string `field:"secret"` // 密钥
Name string `field:"name"` // 名称
Description string `field:"description"` // 描述
Order uint32 `field:"order"` // 排序
State uint8 `field:"state"` // 状态
CreatedAt uint64 `field:"createdAt"` // 创建时间
AdminId uint32 `field:"adminId"` // 管理员ID
Weight uint32 `field:"weight"` // 权重
Status dbs.JSON `field:"status"` // 运行状态
}
type AuthorityNodeOperator struct {

View File

@@ -1,138 +0,0 @@
package models
import (
"encoding/json"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"strconv"
)
const (
ClientBrowserStateEnabled = 1 // 已启用
ClientBrowserStateDisabled = 0 // 已禁用
)
var clientBrowserNameAndIdCacheMap = map[string]int64{}
type ClientBrowserDAO dbs.DAO
func NewClientBrowserDAO() *ClientBrowserDAO {
return dbs.NewDAO(&ClientBrowserDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeClientBrowsers",
Model: new(ClientBrowser),
PkName: "id",
},
}).(*ClientBrowserDAO)
}
var SharedClientBrowserDAO *ClientBrowserDAO
func init() {
dbs.OnReady(func() {
SharedClientBrowserDAO = NewClientBrowserDAO()
})
}
// EnableClientBrowser 启用条目
func (this *ClientBrowserDAO) EnableClientBrowser(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
Set("state", ClientBrowserStateEnabled).
Update()
return err
}
// DisableClientBrowser 禁用条目
func (this *ClientBrowserDAO) DisableClientBrowser(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
Set("state", ClientBrowserStateDisabled).
Update()
return err
}
// FindEnabledClientBrowser 查找启用中的条目
func (this *ClientBrowserDAO) FindEnabledClientBrowser(tx *dbs.Tx, id int64) (*ClientBrowser, error) {
result, err := this.Query(tx).
Pk(id).
Attr("state", ClientBrowserStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*ClientBrowser), err
}
// FindClientBrowserName 根据主键查找名称
func (this *ClientBrowserDAO) FindClientBrowserName(tx *dbs.Tx, id uint32) (string, error) {
return this.Query(tx).
Pk(id).
Result("name").
FindStringCol("")
}
// FindBrowserIdWithNameCacheable 根据浏览器名称查找浏览器ID
func (this *ClientBrowserDAO) FindBrowserIdWithNameCacheable(tx *dbs.Tx, browserName string) (int64, error) {
SharedCacheLocker.RLock()
browserId, ok := clientBrowserNameAndIdCacheMap[browserName]
if ok {
SharedCacheLocker.RUnlock()
return browserId, nil
}
SharedCacheLocker.RUnlock()
browserId, err := this.Query(tx).
Where("JSON_CONTAINS(codes, :browserName)").
Param("browserName", strconv.Quote(browserName)). // 查询的需要是个JSON字符串所以这里加双引号
ResultPk().
FindInt64Col(0)
if err != nil {
return 0, err
}
if browserId > 0 {
// 只有找到的时候才放入缓存,以便于我们可以在不存在的时候创建一条新的记录
SharedCacheLocker.Lock()
clientBrowserNameAndIdCacheMap[browserName] = browserId
SharedCacheLocker.Unlock()
}
return browserId, nil
}
// CreateBrowser 创建浏览器
func (this *ClientBrowserDAO) CreateBrowser(tx *dbs.Tx, browserName string) (int64, error) {
var maxlength = 50
if len(browserName) > maxlength {
browserName = browserName[:50]
}
SharedCacheLocker.Lock()
defer SharedCacheLocker.Unlock()
// 检查是否已经创建
browserId, err := this.Query(tx).
Attr("name", browserName).
ResultPk().
FindInt64Col(0)
if err != nil {
return 0, err
}
if browserId > 0 {
return browserId, nil
}
op := NewClientBrowserOperator()
op.Name = browserName
codes := []string{browserName}
codesJSON, err := json.Marshal(codes)
if err != nil {
return 0, err
}
op.Codes = codesJSON
op.State = ClientBrowserStateEnabled
return this.SaveInt64(tx, op)
}

View File

@@ -1,5 +0,0 @@
package models
import (
_ "github.com/go-sql-driver/mysql"
)

View File

@@ -1,20 +0,0 @@
package models
// 终端浏览器信息
type ClientBrowser struct {
Id uint32 `field:"id"` // ID
Name string `field:"name"` // 浏览器名称
Codes string `field:"codes"` // 代号
State uint8 `field:"state"` // 状态
}
type ClientBrowserOperator struct {
Id interface{} // ID
Name interface{} // 浏览器名称
Codes interface{} // 代号
State interface{} // 状态
}
func NewClientBrowserOperator() *ClientBrowserOperator {
return &ClientBrowserOperator{}
}

View File

@@ -1,140 +0,0 @@
package models
import (
"encoding/json"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"strconv"
)
const (
ClientSystemStateEnabled = 1 // 已启用
ClientSystemStateDisabled = 0 // 已禁用
)
var clientSystemNameAndIdCacheMap = map[string]int64{} // system name => id
type ClientSystemDAO dbs.DAO
func NewClientSystemDAO() *ClientSystemDAO {
return dbs.NewDAO(&ClientSystemDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeClientSystems",
Model: new(ClientSystem),
PkName: "id",
},
}).(*ClientSystemDAO)
}
var SharedClientSystemDAO *ClientSystemDAO
func init() {
dbs.OnReady(func() {
SharedClientSystemDAO = NewClientSystemDAO()
})
}
// EnableClientSystem 启用条目
func (this *ClientSystemDAO) EnableClientSystem(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
Set("state", ClientSystemStateEnabled).
Update()
return err
}
// DisableClientSystem 禁用条目
func (this *ClientSystemDAO) DisableClientSystem(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
Set("state", ClientSystemStateDisabled).
Update()
return err
}
// FindEnabledClientSystem 查找启用中的条目
func (this *ClientSystemDAO) FindEnabledClientSystem(tx *dbs.Tx, id int64) (*ClientSystem, error) {
result, err := this.Query(tx).
Pk(id).
Attr("state", ClientSystemStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*ClientSystem), err
}
// FindClientSystemName 根据主键查找名称
func (this *ClientSystemDAO) FindClientSystemName(tx *dbs.Tx, id uint32) (string, error) {
return this.Query(tx).
Pk(id).
Result("name").
FindStringCol("")
}
// FindSystemIdWithNameCacheable 根据操作系统名称查找系统ID
func (this *ClientSystemDAO) FindSystemIdWithNameCacheable(tx *dbs.Tx, systemName string) (int64, error) {
SharedCacheLocker.RLock()
systemId, ok := clientSystemNameAndIdCacheMap[systemName]
if ok {
SharedCacheLocker.RUnlock()
return systemId, nil
}
SharedCacheLocker.RUnlock()
systemId, err := this.Query(tx).
Where("JSON_CONTAINS(codes, :systemName)").
Param("systemName", strconv.Quote(systemName)). // 查询的需要是个JSON字符串所以这里加双引号
ResultPk().
FindInt64Col(0)
if err != nil {
return 0, err
}
if systemId > 0 {
// 只有找到的时候才放入缓存,以便于我们可以在不存在的时候创建一条新的记录
SharedCacheLocker.Lock()
clientSystemNameAndIdCacheMap[systemName] = systemId
SharedCacheLocker.Unlock()
}
return systemId, nil
}
// CreateSystem 创建浏览器
func (this *ClientSystemDAO) CreateSystem(tx *dbs.Tx, systemName string) (int64, error) {
var maxlength = 50
if len(systemName) > maxlength {
systemName = systemName[:50]
}
SharedCacheLocker.Lock()
defer SharedCacheLocker.Unlock()
// 检查是否已经创建
systemId, err := this.Query(tx).
Attr("name", systemName).
ResultPk().
FindInt64Col(0)
if err != nil {
return 0, err
}
if systemId > 0 {
return systemId, nil
}
op := NewClientSystemOperator()
op.Name = systemName
codes := []string{systemName}
codesJSON, err := json.Marshal(codes)
if err != nil {
return 0, err
}
op.Codes = codesJSON
op.State = ClientSystemStateEnabled
return this.SaveInt64(tx, op)
}

View File

@@ -1,5 +0,0 @@
package models
import (
_ "github.com/go-sql-driver/mysql"
)

View File

@@ -1,20 +0,0 @@
package models
// 终端操作系统信息
type ClientSystem struct {
Id uint32 `field:"id"` // ID
Name string `field:"name"` // 系统名称
Codes string `field:"codes"` // 代号
State uint8 `field:"state"` //
}
type ClientSystemOperator struct {
Id interface{} // ID
Name interface{} // 系统名称
Codes interface{} // 代号
State interface{} //
}
func NewClientSystemOperator() *ClientSystemOperator {
return &ClientSystemOperator{}
}

View File

@@ -0,0 +1,98 @@
package clients
import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
)
type ClientAgentDAO dbs.DAO
func NewClientAgentDAO() *ClientAgentDAO {
return dbs.NewDAO(&ClientAgentDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeClientAgents",
Model: new(ClientAgent),
PkName: "id",
},
}).(*ClientAgentDAO)
}
var SharedClientAgentDAO *ClientAgentDAO
func init() {
dbs.OnReady(func() {
SharedClientAgentDAO = NewClientAgentDAO()
})
}
// FindClientAgentName 根据主键查找名称
func (this *ClientAgentDAO) FindClientAgentName(tx *dbs.Tx, id int64) (string, error) {
return this.Query(tx).
Pk(id).
Result("name").
FindStringCol("")
}
// FindAgent 查找Agent
func (this *ClientAgentDAO) FindAgent(tx *dbs.Tx, agentId int64) (*ClientAgent, error) {
if agentId <= 0 {
return nil, nil
}
one, err := this.Query(tx).
Pk(agentId).
Find()
if err != nil || one == nil {
return nil, err
}
return one.(*ClientAgent), nil
}
// FindAgentIdWithCode 根据代号查找ID
func (this *ClientAgentDAO) FindAgentIdWithCode(tx *dbs.Tx, code string) (int64, error) {
return this.Query(tx).
ResultPk().
Attr("code", code).
FindInt64Col(0)
}
// FindAgentNameWithCode 根据代号查找Agent名称
func (this *ClientAgentDAO) FindAgentNameWithCode(tx *dbs.Tx, code string) (string, error) {
return this.Query(tx).
Result("name").
Attr("code", code).
FindStringCol("")
}
// UpdateAgentCountIPs 修改Agent拥有的IP数量
func (this *ClientAgentDAO) UpdateAgentCountIPs(tx *dbs.Tx, agentId int64, countIPs int64) error {
return this.Query(tx).
Pk(agentId).
Set("countIPs", countIPs).
UpdateQuickly()
}
// FindAllAgents 查找所有Agents
func (this *ClientAgentDAO) FindAllAgents(tx *dbs.Tx) (result []*ClientAgent, err error) {
_, err = this.Query(tx).
Desc("order").
AscPk().
Slice(&result).
FindAll()
return
}
// FindAllNSAgents 查找所有DNS可以使用的Agents
func (this *ClientAgentDAO) FindAllNSAgents(tx *dbs.Tx) (result []*ClientAgent, err error) {
// 注意允许NS使用所有的Agent不管有没有IP数据
_, err = this.Query(tx).
Result("id", "name", "code").
Desc("order").
AscPk().
Slice(&result).
FindAll()
return
}

View File

@@ -0,0 +1,6 @@
package clients_test
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
)

View File

@@ -0,0 +1,105 @@
package clients
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
)
// TODO 需要定时对所有IP的PTR进行检查剔除已经变更的IP
type ClientAgentIPDAO dbs.DAO
func NewClientAgentIPDAO() *ClientAgentIPDAO {
return dbs.NewDAO(&ClientAgentIPDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeClientAgentIPs",
Model: new(ClientAgentIP),
PkName: "id",
},
}).(*ClientAgentIPDAO)
}
var SharedClientAgentIPDAO *ClientAgentIPDAO
func init() {
dbs.OnReady(func() {
SharedClientAgentIPDAO = NewClientAgentIPDAO()
})
}
// CreateIP 写入IP
func (this *ClientAgentIPDAO) CreateIP(tx *dbs.Tx, agentId int64, ip string, ptr string) error {
// 检查数据有效性
if agentId <= 0 || len(ip) == 0 {
return nil
}
// 限制ptr长度
if len(ptr) > 100 {
ptr = ptr[:100]
}
// 检查是否存在
exists, err := this.Query(tx).
Attr("agentId", agentId).
Attr("ip", ip).
Exist()
if err != nil {
return err
}
if exists {
return nil
}
var op = NewClientAgentIPOperator()
op.AgentId = agentId
op.IP = ip
op.Ptr = ptr
err = this.Save(tx, op)
if err != nil {
// 忽略duplicate错误
if models.CheckSQLDuplicateErr(err) {
return nil
}
return err
}
// 更新Agent IP数量
countIPs, err := this.CountAgentIPs(tx, agentId)
if err != nil {
return err
}
err = SharedClientAgentDAO.UpdateAgentCountIPs(tx, agentId, countIPs)
if err != nil {
return err
}
return nil
}
// ListIPsAfterId 列出某个ID之后的IP
func (this *ClientAgentIPDAO) ListIPsAfterId(tx *dbs.Tx, id int64, size int64) (result []*ClientAgentIP, err error) {
if id < 0 {
id = 0
}
_, err = this.Query(tx).
Result("id", "ip", "ptr", "agentId").
Gt("id", id).
AscPk().
Limit(size). // 限制单次读取个数
Slice(&result).
FindAll()
return
}
// CountAgentIPs 计算Agent IP数量
func (this *ClientAgentIPDAO) CountAgentIPs(tx *dbs.Tx, agentId int64) (int64, error) {
return this.Query(tx).
Attr("agentId", agentId).
Count()
}

View File

@@ -0,0 +1,16 @@
package clients_test
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models/clients"
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
"testing"
)
func TestClientAgentIPDAO_CreateIP(t *testing.T) {
var dao = clients.NewClientAgentIPDAO()
err := dao.CreateIP(nil, 1, "127.0.0.1", "")
if err != nil {
t.Fatal(err)
}
}

View File

@@ -0,0 +1,20 @@
package clients
// ClientAgentIP Agent IP
type ClientAgentIP struct {
Id uint64 `field:"id"` // ID
AgentId uint32 `field:"agentId"` // Agent ID
IP string `field:"ip"` // IP地址
Ptr string `field:"ptr"` // PTR值
}
type ClientAgentIPOperator struct {
Id any // ID
AgentId any // Agent ID
IP any // IP地址
Ptr any // PTR值
}
func NewClientAgentIPOperator() *ClientAgentIPOperator {
return &ClientAgentIPOperator{}
}

View File

@@ -0,0 +1 @@
package clients

View File

@@ -0,0 +1,24 @@
package clients
// ClientAgent Agent库
type ClientAgent struct {
Id uint32 `field:"id"` // ID
Name string `field:"name"` // 名称
Code string `field:"code"` // 代号
Description string `field:"description"` // 介绍
Order uint32 `field:"order"` // 排序
CountIPs uint32 `field:"countIPs"` // IP数量
}
type ClientAgentOperator struct {
Id any // ID
Name any // 名称
Code any // 代号
Description any // 介绍
Order any // 排序
CountIPs any // IP数量
}
func NewClientAgentOperator() *ClientAgentOperator {
return &ClientAgentOperator{}
}

View File

@@ -0,0 +1,6 @@
package clients
// NSRouteCode NS线路代号
func (this *ClientAgent) NSRouteCode() string {
return "agent:" + this.Code
}

View File

@@ -0,0 +1,155 @@
package clients
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils/ttlcache"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
const (
ClientBrowserStateEnabled = 1 // 已启用
ClientBrowserStateDisabled = 0 // 已禁用
)
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedClientBrowserDAO.Clean(nil, 7) // 只保留N天
if err != nil {
remotelogs.Error("SharedClientBrowserDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
type ClientBrowserDAO dbs.DAO
func NewClientBrowserDAO() *ClientBrowserDAO {
return dbs.NewDAO(&ClientBrowserDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeClientBrowsers",
Model: new(ClientBrowser),
PkName: "id",
},
}).(*ClientBrowserDAO)
}
var SharedClientBrowserDAO *ClientBrowserDAO
func init() {
dbs.OnReady(func() {
SharedClientBrowserDAO = NewClientBrowserDAO()
})
}
// EnableClientBrowser 启用条目
func (this *ClientBrowserDAO) EnableClientBrowser(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
Set("state", ClientBrowserStateEnabled).
Update()
return err
}
// DisableClientBrowser 禁用条目
func (this *ClientBrowserDAO) DisableClientBrowser(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
Set("state", ClientBrowserStateDisabled).
Update()
return err
}
// FindEnabledClientBrowser 查找启用中的条目
func (this *ClientBrowserDAO) FindEnabledClientBrowser(tx *dbs.Tx, id int64) (*ClientBrowser, error) {
result, err := this.Query(tx).
Pk(id).
Attr("state", ClientBrowserStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*ClientBrowser), err
}
// FindClientBrowserName 根据主键查找名称
func (this *ClientBrowserDAO) FindClientBrowserName(tx *dbs.Tx, id uint32) (string, error) {
return this.Query(tx).
Pk(id).
Result("name").
FindStringCol("")
}
// CreateBrowserIfNotExists 创建浏览器信息
func (this *ClientBrowserDAO) CreateBrowserIfNotExists(tx *dbs.Tx, browserName string) error {
const maxlength = 50
if len(browserName) > maxlength {
browserName = browserName[:50]
}
// 检查缓存
var cacheKey = "clientBrowser:" + browserName
var cacheItem = ttlcache.SharedCache.Read(cacheKey)
if cacheItem != nil {
return nil
}
// 检查是否已经存在
// 不需要加状态条件
browserId, err := this.Query(tx).
Attr("name", browserName).
ResultPk().
FindInt64Col(0)
if err != nil {
return err
}
if browserId > 0 {
// 加入缓存,但缓存时间不要过长,因为有别的操作在更新数据
ttlcache.SharedCache.Write(cacheKey, browserId, time.Now().Unix()+3600)
return this.Query(tx).
Pk(browserId).
Set("createdDay", timeutil.Format("Ymd")).
UpdateQuickly()
}
// 如果不存在,则创建之
var op = NewClientBrowserOperator()
op.Name = browserName
op.CreatedDay = timeutil.Format("Ymd")
op.State = ClientBrowserStateEnabled
browserId, err = this.SaveInt64(tx, op)
if err != nil && models.CheckSQLErrCode(err, 1062 /** duplicate entry **/) {
return nil
}
// 加入缓存,但缓存时间不要过长,因为有别的操作在更新数据
if browserId > 0 {
ttlcache.SharedCache.Write(cacheKey, browserId, time.Now().Unix()+3600)
}
return err
}
// Clean 清理
func (this *ClientBrowserDAO) Clean(tx *dbs.Tx, days int) error {
if days <= 0 {
days = 30
}
return this.Query(tx).
Lt("createdDay", timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days))).
DeleteQuickly()
}

View File

@@ -0,0 +1,33 @@
package clients_test
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models/clients"
_ "github.com/go-sql-driver/mysql"
"testing"
)
func TestClientBrowserDAO_CreateBrowser(t *testing.T) {
var dao = clients.NewClientBrowserDAO()
err := dao.CreateBrowserIfNotExists(nil, "Hello")
if err != nil {
t.Fatal(err)
}
err = dao.CreateBrowserIfNotExists(nil, "Hello")
if err != nil {
t.Fatal(err)
}
err = dao.CreateBrowserIfNotExists(nil, "Hello")
if err != nil {
t.Fatal(err)
}
}
func TestClientBrowserDAO_Clean(t *testing.T) {
var dao = clients.NewClientBrowserDAO()
err := dao.Clean(nil, 30)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -0,0 +1,24 @@
package clients
import "github.com/iwind/TeaGo/dbs"
// ClientBrowser 终端浏览器信息
type ClientBrowser struct {
Id uint64 `field:"id"` // ID
Name string `field:"name"` // 浏览器名称
Codes dbs.JSON `field:"codes"` // 代号
CreatedDay string `field:"createdDay"` // 创建日期YYYYMMDD
State uint8 `field:"state"` // 状态
}
type ClientBrowserOperator struct {
Id any // ID
Name any // 浏览器名称
Codes any // 代号
CreatedDay any // 创建日期YYYYMMDD
State any // 状态
}
func NewClientBrowserOperator() *ClientBrowserOperator {
return &ClientBrowserOperator{}
}

View File

@@ -0,0 +1 @@
package clients

View File

@@ -0,0 +1,154 @@
package clients
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils/ttlcache"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
const (
ClientSystemStateEnabled = 1 // 已启用
ClientSystemStateDisabled = 0 // 已禁用
)
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedClientSystemDAO.Clean(nil, 7) // 只保留N天
if err != nil {
remotelogs.Error("SharedClientSystemDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
type ClientSystemDAO dbs.DAO
func NewClientSystemDAO() *ClientSystemDAO {
return dbs.NewDAO(&ClientSystemDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeClientSystems",
Model: new(ClientSystem),
PkName: "id",
},
}).(*ClientSystemDAO)
}
var SharedClientSystemDAO *ClientSystemDAO
func init() {
dbs.OnReady(func() {
SharedClientSystemDAO = NewClientSystemDAO()
})
}
// EnableClientSystem 启用条目
func (this *ClientSystemDAO) EnableClientSystem(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
Set("state", ClientSystemStateEnabled).
Update()
return err
}
// DisableClientSystem 禁用条目
func (this *ClientSystemDAO) DisableClientSystem(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
Set("state", ClientSystemStateDisabled).
Update()
return err
}
// FindEnabledClientSystem 查找启用中的条目
func (this *ClientSystemDAO) FindEnabledClientSystem(tx *dbs.Tx, id int64) (*ClientSystem, error) {
result, err := this.Query(tx).
Pk(id).
Attr("state", ClientSystemStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*ClientSystem), err
}
// FindClientSystemName 根据主键查找名称
func (this *ClientSystemDAO) FindClientSystemName(tx *dbs.Tx, id uint32) (string, error) {
return this.Query(tx).
Pk(id).
Result("name").
FindStringCol("")
}
// CreateSystemIfNotExists 创建系统信息
func (this *ClientSystemDAO) CreateSystemIfNotExists(tx *dbs.Tx, systemName string) error {
const maxlength = 50
if len(systemName) > maxlength {
systemName = systemName[:50]
}
// 检查缓存
var cacheKey = "clientSystem:" + systemName
var cacheItem = ttlcache.SharedCache.Read(cacheKey)
if cacheItem != nil {
return nil
}
// 检查是否已经存在
// 不需要加状态条件
systemId, err := this.Query(tx).
Attr("name", systemName).
ResultPk().
FindInt64Col(0)
if err != nil {
return err
}
if systemId > 0 {
// 加入缓存,但缓存时间不要过长,因为有别的操作在更新数据
ttlcache.SharedCache.Write(cacheKey, systemId, time.Now().Unix()+3600)
return this.Query(tx).
Pk(systemId).
Set("createdDay", timeutil.Format("Ymd")).
UpdateQuickly()
}
var op = NewClientSystemOperator()
op.Name = systemName
op.CreatedDay = timeutil.Format("Ymd")
op.State = ClientSystemStateEnabled
systemId, err = this.SaveInt64(tx, op)
if err != nil && models.CheckSQLErrCode(err, 1062 /** duplicate entry **/) {
return nil
}
// 加入缓存,但缓存时间不要过长,因为有别的操作在更新数据
if systemId > 0 {
ttlcache.SharedCache.Write(cacheKey, systemId, time.Now().Unix()+3600)
}
return err
}
// Clean 清理
func (this *ClientSystemDAO) Clean(tx *dbs.Tx, days int) error {
if days <= 0 {
days = 30
}
return this.Query(tx).
Lt("createdDay", timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days))).
DeleteQuickly()
}

View File

@@ -0,0 +1,31 @@
package clients_test
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models/clients"
_ "github.com/go-sql-driver/mysql"
"testing"
)
func TestClientSystemDAO_CreateSystemIfNotExists(t *testing.T) {
var dao = clients.NewClientSystemDAO()
{
err := dao.CreateSystemIfNotExists(nil, "Mac OS X")
if err != nil {
t.Fatal(err)
}
}
{
err := dao.CreateSystemIfNotExists(nil, "Mac OS X 2")
if err != nil {
t.Fatal(err)
}
}
}
func TestClientSystemDAO_Clean(t *testing.T) {
var dao = clients.NewClientSystemDAO()
err := dao.Clean(nil, 30)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -0,0 +1,24 @@
package clients
import "github.com/iwind/TeaGo/dbs"
// ClientSystem 终端操作系统信息
type ClientSystem struct {
Id uint64 `field:"id"` // ID
Name string `field:"name"` // 系统名称
Codes dbs.JSON `field:"codes"` // 代号
CreatedDay string `field:"createdDay"` // 创建日期YYYYMMDD
State uint8 `field:"state"` // 状态
}
type ClientSystemOperator struct {
Id any // ID
Name any // 系统名称
Codes any // 代号
CreatedDay any // 创建日期YYYYMMDD
State any // 状态
}
func NewClientSystemOperator() *ClientSystemOperator {
return &ClientSystemOperator{}
}

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