Compare commits

...

225 Commits

Author SHA1 Message Date
刘祥超
a987837904 版本修改为1.3.7 2024-04-29 23:12:29 +08:00
刘祥超
4bcad223ca 版本号变更为1.3.6 2024-04-22 10:56:04 +08:00
刘祥超
b9e5005d05 升级相关依赖 2024-04-21 20:54:16 +08:00
刘祥超
fd9bdee6be 修复单例安装程序编译问题 2024-04-21 20:12:29 +08:00
刘祥超
4460956de6 在IP名单中搜索IP时同时也搜索“所有IP”类型的IP 2024-04-21 10:48:25 +08:00
刘祥超
1923c2706a 版本号修改为1.3.5 2024-04-20 22:46:56 +08:00
刘祥超
1470ec2b65 修复ttlcache可能缺失回收数据的问题 2024-04-20 22:22:24 +08:00
刘祥超
62fc9bcc68 增加edgeMtricSumStats表中total长度 2024-04-19 16:13:32 +08:00
刘祥超
8a08df8593 更新相关依赖库 2024-04-17 22:23:46 +08:00
刘祥超
bc7a1f37b6 版本号修改为1.3.4.4 2024-04-16 14:14:59 +08:00
刘祥超
f8a01e4639 版本号修改为1.3.4.3 2024-04-15 09:26:18 +08:00
刘祥超
8e4c00ef31 删除一直未实现的Unix协议相关内容 2024-04-14 17:11:58 +08:00
刘祥超
dc894828e0 源站增加快速停用/启用功能 2024-04-14 16:27:27 +08:00
刘祥超
7f6d8ba7b4 修复4位版本号导致无法自动升级SQL的问题 2024-04-14 11:47:10 +08:00
刘祥超
b8b56db83c 进程重启时,自动保存未保存的带宽统计数据到本地文件,以便于在重启后恢复 2024-04-13 17:14:58 +08:00
刘祥超
17f0821945 简化IP名单中创建IP操作/支持IP以CIDR方式显示 2024-04-13 16:48:24 +08:00
刘祥超
b5436a6e57 网站看板数据中增加当日独立IP和当日流量 2024-04-12 18:51:42 +08:00
刘祥超
46fe2d8369 优化套餐变更后网站限流状态 2024-04-12 11:35:52 +08:00
刘祥超
ff429c270d 节点配置中增加集群的密钥信息 2024-04-11 14:19:08 +08:00
刘祥超
92de19e359 版本号修改为1.3.4.2 2024-04-09 10:05:40 +08:00
刘祥超
4121c81a0a 将版本号修改为1.3.4.1 2024-04-08 14:57:05 +08:00
刘祥超
f639ab8342 WAF策略增加JSCookie动作选项 2024-04-07 14:21:01 +08:00
刘祥超
f17a8ab1d0 标记edgeIPItems中两个字段为弃用 2024-04-07 11:26:56 +08:00
刘祥超
12f677eb12 IP名单中的“全局封锁名单”文字改为“系统黑名单” 2024-04-06 16:13:17 +08:00
刘祥超
7595bdeb6b 用户系统增加IP检查功能 2024-04-06 15:23:19 +08:00
刘祥超
fc223af3f0 IP检查也支持范围搜索 2024-04-06 15:15:33 +08:00
刘祥超
ebe3632f07 支持搜索IPv6范围 2024-04-06 14:55:51 +08:00
刘祥超
930babc010 IP名单搜索IP时同时搜索范围 2024-04-06 10:31:03 +08:00
刘祥超
255e3a61e6 更好地支持IPv6 2024-04-06 10:21:52 +08:00
刘祥超
52155a23ab 集群设置增加自动硬盘TRIM选项 2024-04-04 17:04:53 +08:00
刘祥超
200f244c0c “磁盘”文字改为“硬盘” 2024-04-04 16:49:17 +08:00
刘祥超
ab5d7539ce 节点上传指标数据时只上传变更的部分 2024-04-03 08:15:20 +08:00
刘祥超
3e79840fe6 使用MMAP提升缓存读取性能 2024-03-29 18:32:31 +08:00
刘祥超
d03455e3b0 将版本号修改为1.3.4 2024-03-24 20:08:27 +08:00
刘祥超
af1cb14110 提升登录SESSION安全性 2024-03-18 12:43:13 +08:00
刘祥超
0feffa755e 节点SSH密码和私钥均以掩码方式显示 2024-03-18 10:51:47 +08:00
刘祥超
7cfbe2e473 DNS服务商中的密钥数据以掩码方式显示 2024-03-18 10:20:22 +08:00
刘祥超
7f63dc4565 查找省份对应ID时,自动尝试省略省、区之类的后缀 2024-03-15 15:08:05 +08:00
刘祥超
e90424f80a 翻译部分英文地名 2024-03-15 15:07:07 +08:00
刘祥超
6271125296 省份表增加线路字段 2024-03-14 20:42:13 +08:00
刘祥超
44ac4b83c5 智能DNS中国家/地区线路下支持省/州的细分 2024-03-14 20:12:04 +08:00
刘祥超
c75e2c55c6 优化代码 2024-03-10 16:26:03 +08:00
刘祥超
51a3029c09 在缓存任务键值中增加集群信息,以便于调试问题 2024-03-10 11:26:28 +08:00
刘祥超
580341d397 优化systemd服务配置 2024-03-08 19:00:27 +08:00
刘祥超
fb4bad0731 单例应用设置数据库自动清理 2024-03-04 11:32:47 +08:00
刘祥超
70efff2e6b 优化实例安装脚本 2024-03-03 17:14:29 +08:00
刘祥超
97c76ef22f 优化单例应用安装程序 2024-03-02 20:51:13 +08:00
刘祥超
e763095756 修复部分API返回格式错误 2024-02-24 09:52:47 +08:00
刘祥超
b7dc2738e2 增加单体应用初始化标识 2024-01-29 18:56:37 +08:00
刘祥超
3db826b578 增加通过管理员用户名查找管理员信息的API 2024-01-29 18:55:04 +08:00
刘祥超
dc8975e374 版本号修改为1.3.3.1 2024-01-29 17:58:36 +08:00
刘祥超
c0cbd7c607 实现单体实例安装工具 2024-01-29 17:57:01 +08:00
刘祥超
4d9f404bb0 优化SQL升级代码 2024-01-29 10:22:27 +08:00
刘祥超
06bb61804b 优化编译脚本 2024-01-22 18:51:22 +08:00
刘祥超
32c1442878 增加修改节点停用/启用状态API 2024-01-21 17:43:20 +08:00
刘祥超
b99652801d 版本号修改为1.3.3 2024-01-21 16:57:37 +08:00
刘祥超
be565a98b9 查询集群列表API增加ID排序 2024-01-21 16:57:17 +08:00
刘祥超
5195a380db WAF策略增加显示页面动作默认设置 2024-01-20 16:19:11 +08:00
刘祥超
8dbbabb0e8 修改版本号为1.3.2.2 2024-01-16 20:59:18 +08:00
刘祥超
bec4500746 版本号修改为1.3.2.1 2024-01-15 08:40:23 +08:00
刘祥超
66a31f599d 网站设置增加HLS加密功能(商业版本 2024-01-14 20:36:47 +08:00
刘祥超
534cfb2180 套餐增加文件最大上传尺寸设置 2024-01-13 19:32:48 +08:00
刘祥超
a9dc20ffbd 优化API错误提示 2024-01-12 12:11:13 +08:00
刘祥超
7f20ad32b6 调用API时找不到服务或方法时也提示JSON,防止小白开发者不知道如何获取响应状态 2024-01-12 11:51:06 +08:00
刘祥超
a3c0b43bc4 添加快捷添加和删除网站源站API 2024-01-12 11:50:10 +08:00
刘祥超
1f2c9a6b3a 增加删除一组网站API 2024-01-11 19:06:25 +08:00
刘祥超
194b0ec184 套餐可以设置带宽限制 2024-01-11 15:21:00 +08:00
刘祥超
c94895a7c4 增加用户系统文章相关管理 2024-01-09 10:20:52 +08:00
刘祥超
22d15bcb27 华为云DNS线路增加一组"运营商_地区“线路 2023-12-25 09:05:06 +08:00
刘祥超
361fb9b868 升级程序中的1.3.1.x改为1.3.2 2023-12-24 17:40:40 +08:00
刘祥超
2d675f4281 源码编译版本增加节点数限制 2023-12-24 11:28:41 +08:00
刘祥超
e19bbdf891 版本号修改为1.3.2 2023-12-24 11:14:39 +08:00
刘祥超
d48c0a2328 增加列出IP名单中的IP ID列表的API 2023-12-24 10:51:29 +08:00
刘祥超
a70b20cf13 增加请求脚本审核机制 2023-12-23 20:56:11 +08:00
刘祥超
eb83017ed4 修复一处编译错误 2023-12-22 16:46:37 +08:00
刘祥超
98ba31174b 套餐增加简介信息 2023-12-21 15:09:50 +08:00
刘祥超
aa28e84507 增加若干功能代号 2023-12-20 17:34:54 +08:00
刘祥超
da8fe918fe 更新SQL 2023-12-20 15:54:36 +08:00
刘祥超
2b26bed97c 增加若干API 2023-12-20 15:08:05 +08:00
刘祥超
5e50518bd9 限制ACME错误消息长度 2023-12-19 20:05:34 +08:00
刘祥超
e49db916f8 套餐增加Websocket连接数限制 2023-12-19 14:56:44 +08:00
刘祥超
16083fd0d7 增加多个台湾地区区县地址 2023-12-18 09:43:49 +08:00
刘祥超
e0e2729fef 版本号修改为1.3.1.2 2023-12-18 08:51:04 +08:00
刘祥超
9b95042936 缓存设置中可以设置缓存主域名,用来复用多域名下的缓存 2023-12-13 18:34:57 +08:00
刘祥超
44d45c53a1 增加保存管理员语言选择的API 2023-12-12 22:40:06 +08:00
刘祥超
c5fb340eb7 自动升级WAF策略中SQL注入检测和XSS注入检测 2023-12-12 17:15:21 +08:00
刘祥超
cbb61d2f0e 读取用户信息时同时返回语言设置 2023-12-12 11:49:05 +08:00
刘祥超
a143714370 WebP策略变化时只更新相关配置 2023-12-11 11:08:19 +08:00
刘祥超
0e1a98c5d8 将部分MB、GB...改成MiB、GiB... 2023-12-03 11:32:09 +08:00
刘祥超
707a9f8caf 优化代码 2023-11-29 16:58:11 +08:00
刘祥超
da391f565b 创建集群时默认生成子域名 2023-11-27 11:28:31 +08:00
刘祥超
78f396129f 阿里云线路显示完整的线路名称 2023-11-26 20:16:13 +08:00
刘祥超
e8b620aa1e 提交SQL 2023-11-24 10:24:29 +08:00
刘祥超
1019370f37 提交go.sum 2023-11-24 10:21:50 +08:00
刘祥超
cd7cff4f9c 修复一处编译错误 2023-11-24 10:20:16 +08:00
刘祥超
2888634fb0 将版本号修改为1.3.1 2023-11-23 17:24:09 +08:00
刘祥超
94defc3e0c 优化SSH认证sudo设置 2023-11-23 16:12:52 +08:00
刘祥超
9089ed2657 DNSPod改名为腾讯云DNSPod/DNSPod 支持腾讯云API密钥 2023-11-23 15:15:11 +08:00
刘祥超
b60bb5f6da 提交SQL 2023-11-19 09:11:07 +08:00
刘祥超
ff4ea41963 节点配置中增加节点IP信息 2023-11-18 12:09:47 +08:00
刘祥超
b7dccad449 实现用户系统手机号码绑定和登录(商业版) 2023-11-17 11:51:29 +08:00
刘祥超
7fead214d4 更新SQL 2023-11-15 19:10:18 +08:00
刘祥超
d9590ec605 创建反向代理时默认不自动重试50X/源站支持404内容自动重试其他源站 2023-11-15 19:05:43 +08:00
刘祥超
20b936580f 版本号修改为1.3.0 2023-11-14 14:47:32 +08:00
刘祥超
b7b43bc31f 限制访问日志中域名能写入的最大长度 2023-11-13 17:12:11 +08:00
刘祥超
6fd4f26755 自定义页面增加例外URL和限制URL设置 2023-11-13 10:46:12 +08:00
刘祥超
f15d114708 自定义页面增加“跳转URL”功能 2023-11-10 16:36:09 +08:00
刘祥超
fc24195b55 增加访问日志中域名长度 2023-11-10 09:56:17 +08:00
刘祥超
ed5de57244 去除一处多余的日志 2023-11-07 17:34:09 +08:00
刘祥超
4ce347738f 修复无法将OSS源站修改为http/https源站的问题 2023-11-04 08:28:08 +08:00
刘祥超
f6e725781c 优化节点阈值设置 2023-11-03 11:20:47 +08:00
刘祥超
55d70418cc 节点健康检查失败时增加节点名称和节点IP提示 2023-11-03 09:54:42 +08:00
刘祥超
7f5b070e36 优化商业版验证 2023-11-02 17:20:12 +08:00
刘祥超
993c7ee822 上传域名统计数据时限制域名长度不能超过64位 2023-11-02 17:19:56 +08:00
刘祥超
b5bb4e0df9 更新数据库 2023-10-30 19:04:23 +08:00
刘祥超
9f120fd0e0 访问日志存储策略增加“停止默认数据库存储”选项 2023-10-30 19:03:39 +08:00
刘祥超
77d614c9ea 实现网络数据包相关统计(商业版本) 2023-10-26 17:17:43 +08:00
刘祥超
531ec3c55d 优化域名解析文字提示 2023-10-17 15:54:08 +08:00
刘祥超
0d6c064194 将版本号修改为1.2.11 2023-10-17 13:49:39 +08:00
刘祥超
180e86c643 修复消息通知不能指定集群的Bug 2023-10-17 13:49:23 +08:00
刘祥超
86b04b2b6b 将临时的1.2.9.1升级程序版本号修改为1.2.10 2023-10-15 15:10:36 +08:00
刘祥超
7a5ec79ace 将版本号修改为1.2.10 2023-10-15 13:34:18 +08:00
刘祥超
7290ffd2cd 取消默认反向代理默认的50X重试 2023-10-15 09:40:39 +08:00
刘祥超
2f361c5bcc 优化消息任务相关代码 2023-10-15 09:39:46 +08:00
刘祥超
500d72aaf3 WAF记录IP动作中IP名单如果为空时,默认为全局黑名单 2023-10-15 09:34:20 +08:00
刘祥超
9fc391d1e8 删除不必要的代码 2023-10-14 18:15:54 +08:00
刘祥超
c86e3e2047 优化消息通知相关代码 2023-10-14 17:16:08 +08:00
刘祥超
7e72a90f53 优化消息发送相关代码/删除监控相关代码 2023-10-12 20:11:21 +08:00
刘祥超
7692fed38d 支持批量复制WAF设置 2023-10-09 19:52:51 +08:00
刘祥超
bdd7d2a181 申请证书任务列表区分管理员和用户 2023-10-09 16:18:32 +08:00
刘祥超
118c3f79e4 证书列表区分管理员和用户证书 2023-10-09 15:54:00 +08:00
刘祥超
804a33a002 访问日志列表搜索增加请求来源查询语法:referer:example.com 2023-10-08 17:52:53 +08:00
刘祥超
fe00588039 集群设置中增加“自动调节系统参数”选项 2023-10-08 15:08:28 +08:00
刘祥超
67aac200a7 修复常用网站、常用集群查询可能因为updatedAt过大导致的SQL错误 2023-09-22 16:41:44 +08:00
刘祥超
3e01ad4b68 节点配置中对父级节点进行排序,以保证查找的稳定性 2023-09-22 11:55:47 +08:00
刘祥超
b39690484e 将升级程序中的1.2.10改成1.2.9.1,方便在测试版本中也能升级 2023-09-18 17:02:54 +08:00
刘祥超
31a69ecb12 将全局设置的TCP相关设置移到“集群设置--网站设置”中 2023-09-18 16:55:45 +08:00
刘祥超
94b95beadf 将全局的通用设置--域名审核设置移到“集群设置--网站设置”中 2023-09-18 16:09:11 +08:00
刘祥超
6143f08cf2 IP名单删除任务完成后删除任务 2023-09-14 09:12:19 +08:00
刘祥超
73a5814fd6 版本号修改为1.2.9 2023-09-13 17:37:41 +08:00
刘祥超
448152d5c2 优化删除IP名单时操作 2023-09-13 17:16:00 +08:00
刘祥超
eedb3fb338 将节点版本号修改为1.2.9 2023-09-12 15:03:00 +08:00
刘祥超
06f6f68f3a 增加自动升级一处WAF规则 2023-09-12 14:59:07 +08:00
刘祥超
903e524e80 优化访客IP地址设置 2023-09-07 18:03:28 +08:00
刘祥超
fa6b4fcaee 套餐增加请求数(日/月)限制 2023-09-07 11:46:03 +08:00
刘祥超
67cc8e515f 修复一个测试用例 2023-09-06 18:19:25 +08:00
刘祥超
fa29817920 统计带宽计算增加最小样本数 2023-09-06 18:14:08 +08:00
刘祥超
794c3bc132 优化套餐升级程序 2023-09-06 18:01:41 +08:00
刘祥超
9e481d31ac 重新实现套餐相关功能 2023-09-06 16:30:47 +08:00
刘祥超
4ebc03af75 调用自定义HTTP DNS时增加action(值为GetDomains) 2023-08-28 16:28:08 +08:00
刘祥超
80e2face67 更新Agent IP库 2023-08-27 11:58:14 +08:00
刘祥超
815a5187d5 反向代理增加是否重试50X选项,默认为启用 2023-08-20 15:49:34 +08:00
刘祥超
1d7bc42fba 修复节点状态监控中磁盘空间可能为0的问题 2023-08-18 16:01:24 +08:00
刘祥超
1eb9cca793 将WAF策略中的默认省份封禁提示内容长度从255修改为65535 2023-08-14 12:54:11 +08:00
刘祥超
8766f5b1a9 修改版本号为1.2.8 2023-08-14 12:24:29 +08:00
刘祥超
823e42626d DNS任务增加失败重试 2023-08-13 15:26:59 +08:00
刘祥超
c5308cf41c 生成节点时去除停用的WAF规则集 2023-08-13 10:51:52 +08:00
刘祥超
3053157c6e 将节点的api.yaml改为api_node.yaml 2023-08-12 15:27:09 +08:00
刘祥超
d1ba141c65 优化错误处理相关代码 2023-08-11 16:13:33 +08:00
刘祥超
034ababead 静态分发增加例外URL、限制URL、排除隐藏文件等选项 2023-08-10 11:27:05 +08:00
刘祥超
f5450e37be WAF策略可以自定义默认的区域/省份封禁提示 2023-08-10 10:30:50 +08:00
刘祥超
549fca93e6 将版本号修改为1.2.7 2023-08-09 14:24:16 +08:00
刘祥超
efa0f33256 Update .golangci.yaml 2023-08-09 08:11:53 +08:00
刘祥超
977a12843c 添加golangci-lint配置 2023-08-08 18:36:24 +08:00
刘祥超
6de2834a8c 优化代码 2023-08-08 16:46:17 +08:00
刘祥超
51f91e1603 优化代码 2023-08-08 12:09:20 +08:00
刘祥超
d27b7c8fa1 允许用户调用获取缓存策略信息API 2023-08-07 19:55:57 +08:00
刘祥超
c5098c66af 缓存策略增加预热超时时间设置(默认20分钟) 2023-08-06 17:07:48 +08:00
刘祥超
c2635b0d04 修复默认WAF策略模板中分组不能默认关闭的问题 2023-08-02 17:15:26 +08:00
刘祥超
41a1a6a2e5 更新SQL 2023-08-02 17:02:39 +08:00
刘祥超
e437117e69 WAF策略增加“最多检查内容尺寸“选项 2023-08-02 16:59:38 +08:00
刘祥超
fdc8f78229 优化CC配置 2023-08-01 19:50:01 +08:00
刘祥超
2f78d76a1a 修复系统服务相关代码可能不执行的问题 2023-08-01 16:19:05 +08:00
刘祥超
742f2f0216 启动时自动创建相关软链接 2023-08-01 10:47:13 +08:00
刘祥超
89a606329f 修复自定义页面无法保存的问题 2023-07-31 09:46:00 +08:00
刘祥超
3bba79d14c 优化统计 2023-07-31 09:45:48 +08:00
刘祥超
9f9787e30f 版本号更改为1.2.6 2023-07-28 09:27:08 +08:00
刘祥超
529016d4d5 版本号更改为1.2.5 2023-07-26 15:30:37 +08:00
刘祥超
63942bfb08 将版本号修改为1.2.4 2023-07-26 10:19:02 +08:00
刘祥超
f4e4f32f9c 修复SysLocker无法写入新Key的问题 2023-07-26 10:18:52 +08:00
刘祥超
0a3c740502 版本号修改为1.2.3 2023-07-25 13:17:59 +08:00
刘祥超
9a3438e066 优化IP名单使用IP搜索查询速度 2023-07-25 12:26:12 +08:00
刘祥超
814b82e1b6 优化TOA相关代码 2023-07-24 15:33:44 +08:00
刘祥超
89cfd175cd 优化TOA相关API 2023-07-24 09:56:43 +08:00
刘祥超
860816719e 单个节点所在多个集群共用一个缓存策略时只加载其中一个 2023-07-20 16:54:34 +08:00
刘祥超
caa936f0ac 大幅提升SysLocker自增性能 2023-07-20 14:25:42 +08:00
刘祥超
97836a89eb 优化代码 2023-07-19 18:49:23 +08:00
刘祥超
84483dce61 版本号更改为1.2.2 2023-07-18 14:33:53 +08:00
刘祥超
a4eb7a47f3 更新SQL 2023-07-16 19:12:10 +08:00
刘祥超
20c84d7fe5 手动同步集群任务后把所有相关任务标记为已完成 2023-07-14 10:04:44 +08:00
刘祥超
9d5acd2b36 优化代码 2023-07-12 17:10:33 +08:00
刘祥超
7508f6b92b 增加页面优化相关API 2023-07-11 19:46:00 +08:00
刘祥超
379030fe71 版本号改为1.2.1 2023-07-09 17:38:09 +08:00
刘祥超
10027eea20 缓存策略移除“容纳Key数量”选项 2023-07-08 18:50:58 +08:00
刘祥超
69f25a176b 提交SQL 2023-07-07 18:52:37 +08:00
刘祥超
ac19f06b6c 网站列表增加QPS和攻击QPS信息 2023-07-07 18:51:36 +08:00
刘祥超
87a81f59c7 修复查询网站日流量统计时可能不兼容MySQL8的问题 2023-07-07 17:35:23 +08:00
刘祥超
7389e5e54b 远程安装时可以覆盖运行中的文件 2023-07-07 15:59:51 +08:00
刘祥超
e6792b8188 优化自定义页面设置,页面URL不再支持填写本地文件 2023-07-07 11:48:48 +08:00
刘祥超
a037546cfa 优化代码 2023-07-07 09:52:53 +08:00
刘祥超
8efaacf1ef 国家/地区、省份等相关表增加真实ID字段,防止数据表被用户修改时无法对应 2023-07-07 09:52:46 +08:00
刘祥超
a38dd1cef8 “集群设置 -- 网站设置”增加“允许记录访问日志”选项 2023-07-05 15:29:11 +08:00
刘祥超
77521112d0 试用 executils.LookPath()代替 exec.LookPath() 2023-07-05 11:34:52 +08:00
刘祥超
4f9a5d238c 优化本地mysql服务自动启动逻辑 2023-07-05 11:14:51 +08:00
刘祥超
d5fb39ed50 更新TeaGo库 2023-07-05 09:25:27 +08:00
刘祥超
58a84083ae 优化自增锁性能 2023-07-04 22:02:17 +08:00
刘祥超
9f564a4739 重写规则API支持用户操作 2023-07-04 18:31:12 +08:00
刘祥超
c20accbf58 减少在自增锁中生成的sql statements 2023-07-04 14:42:14 +08:00
刘祥超
3f21b3148e 优化代码 2023-07-03 17:12:24 +08:00
刘祥超
74e909a501 增加清空节点同步任务、清空DNS同步任务API 2023-07-02 17:29:19 +08:00
刘祥超
4150ee1b47 优化自增锁算法 2023-07-02 15:27:49 +08:00
刘祥超
df04de2151 修复测试用例 2023-07-02 15:25:13 +08:00
刘祥超
4dc5d9aa7e 修复自动生成的用户没有绑定集群、用户名不规范的问题 2023-07-02 14:30:46 +08:00
刘祥超
0ef7e6ccd8 增加部分数据清理周期设置 2023-07-01 17:54:40 +08:00
刘祥超
ed2b831e5a 查找当前API节点版本中增加角色 2023-07-01 15:09:54 +08:00
刘祥超
5d392ecd43 优化代码 2023-06-30 19:06:55 +08:00
刘祥超
ea147d7506 优化代码 2023-06-30 19:01:47 +08:00
刘祥超
b45136c2c8 优化代码 2023-06-30 18:54:45 +08:00
刘祥超
530e1513ec 日志API增加多语言代号参数 2023-06-30 18:10:11 +08:00
刘祥超
6c60677b72 添加多语言最基础代码 2023-06-28 09:11:20 +08:00
刘祥超
a1bec5e578 创建Web配置时自动设置访客IP获取方式为“直接获取” 2023-06-23 17:05:52 +08:00
刘祥超
9dece058d9 优化查询所有集群性能 2023-06-23 16:23:21 +08:00
刘祥超
89df6ae6bf 优化集群列表性能 2023-06-23 16:15:22 +08:00
刘祥超
85b6e6428c 源站支持HTTP/2 2023-06-23 11:44:02 +08:00
刘祥超
0df204a1df 初始化时修改默认生成的用户名,并将用户自动关联到默认集群 2023-06-21 11:51:07 +08:00
278 changed files with 43563 additions and 10589 deletions

75
.golangci.yaml Normal file
View File

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

View File

@@ -115,7 +115,11 @@ function build() {
fi fi
# building api node # building api node
env GOOS="$OS" GOARCH="$ARCH" go build -trimpath -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/$NAME" "$ROOT"/../cmd/edge-api/main.go
if [ ! -f "${DIST}/bin/${NAME}" ]; then
echo "build failed!"
exit
fi
# delete hidden files # delete hidden files
find "$DIST" -name ".DS_Store" -delete find "$DIST" -name ".DS_Store" -delete

View File

@@ -12,5 +12,5 @@ dbs:
fields: fields:
bool: [ "uamIsOn", "followPort", "requestHostExcludingPort", "autoRemoteStart", "autoInstallNftables", "enableIPLists", "detectAgents", "checkingPorts", "enableRecordHealthCheck", "offlineIsNotified", "http2Enabled", "http3Enabled" ] bool: [ "uamIsOn", "followPort", "requestHostExcludingPort", "autoRemoteStart", "autoInstallNftables", "enableIPLists", "detectAgents", "checkingPorts", "enableRecordHealthCheck", "offlineIsNotified", "http2Enabled", "http3Enabled", "enableHTTP2", "retry50X", "retry40X", "autoSystemTuning", "disableDefaultDB", "autoTrimDisks" ]

View File

@@ -1,3 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
go run `dirname $0`/../cmd/sql-dump/main.go -dir=`dirname $0` # generate 'internal/setup/sql.json' file
CWD="$(dirname "$0")"
go run "${CWD}"/../cmd/sql-dump/main.go -dir="${CWD}"

View File

@@ -0,0 +1,2 @@
edge-instance-installer*
prepare.sh

View File

@@ -0,0 +1,45 @@
#!/usr/bin/env bash
function build() {
ROOT=$(dirname "$0")
OS="${1}"
ARCH="${2}"
TAG="${3}"
if [ -z "$OS" ]; then
echo "usage: build.sh OS ARCH"
exit
fi
if [ -z "$ARCH" ]; then
echo "usage: build.sh OS ARCH"
exit
fi
VERSION=$(lookup_version "${ROOT}/../../internal/const/const.go")
TARGET_NAME="edge-instance-installer-${OS}-${ARCH}-v${VERSION}"
env GOOS=linux GOARCH="${ARCH}" go build -tags="${TAG}" -trimpath -ldflags="-s -w" -o "${TARGET_NAME}" main.go
if [ -f "${TARGET_NAME}" ]; then
cp "${TARGET_NAME}" "${ROOT}/../../../EdgeAdmin/docker/instance/edge-instance/assets"
fi
echo "[done]"
}
function lookup_version() {
FILE=$1
VERSION_DATA=$(cat "$FILE")
re="Version[ ]+=[ ]+\"([0-9.]+)\""
if [[ $VERSION_DATA =~ $re ]]; then
VERSION=${BASH_REMATCH[1]}
echo "$VERSION"
else
echo "could not match version"
exit
fi
}
build "$1" "$2" "$3"

View File

@@ -0,0 +1,97 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package main
import (
"fmt"
"github.com/TeaOSLab/EdgeAPI/internal/instances"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/lists"
"log"
"os"
)
func main() {
var verbose = lists.ContainsString(os.Args, "-v")
var dbHost = "127.0.0.1"
var dbPassword = "123456"
var dbName = "edges"
envDBHost, _ := os.LookupEnv("EDGE_DB_HOST")
if len(envDBHost) > 0 {
dbHost = envDBHost
if verbose {
log.Println("env EDGE_DB_HOST=" + envDBHost)
}
}
envDBPassword, _ := os.LookupEnv("EDGE_DB_PASSWORD")
if len(envDBPassword) > 0 {
dbPassword = envDBPassword
if verbose {
log.Println("env EDGE_DB_PASSWORD=" + envDBPassword)
}
}
envDBName, _ := os.LookupEnv("EDGE_DB_NAME")
if len(envDBName) > 0 {
dbName = envDBName
if verbose {
log.Println("env EDGE_DB_NAME=" + envDBName)
}
}
var isTesting = lists.ContainsString(os.Args, "-test") || lists.ContainsString(os.Args, "--test")
if isTesting {
fmt.Println("testing mode ...")
}
var instance = instances.NewInstance(instances.Options{
IsTesting: isTesting,
Verbose: verbose,
Cacheable: false,
WorkDir: "",
SrcDir: "/usr/local/goedge/src",
DB: struct {
Host string
Port int
Username string
Password string
Name string
}{
Host: dbHost,
Port: 3306,
Username: "root",
Password: dbPassword,
Name: dbName,
},
AdminNode: struct {
Port int
}{
Port: 7788,
},
APINode: struct {
HTTPPort int
RestHTTPPort int
}{
HTTPPort: 8001,
RestHTTPPort: 8002,
},
Node: struct{ HTTPPort int }{
HTTPPort: 80,
},
UserNode: struct {
HTTPPort int
}{
HTTPPort: 7799,
},
})
err := instance.SetupAll()
if err != nil {
fmt.Println("[ERROR]setup failed: " + err.Error())
return
}
fmt.Println("ok")
}

View File

@@ -52,7 +52,7 @@ func main() {
return return
} }
unzip := helpers.NewUnzip(zipPath, targetPath) var unzip = helpers.NewUnzip(zipPath, targetPath)
err := unzip.Run() err := unzip.Run()
if err != nil { if err != nil {
stderr("ERROR: " + err.Error()) stderr("ERROR: " + err.Error())

45
go.mod
View File

@@ -1,55 +1,70 @@
module github.com/TeaOSLab/EdgeAPI module github.com/TeaOSLab/EdgeAPI
go 1.18 go 1.21
replace github.com/TeaOSLab/EdgeCommon => ../EdgeCommon replace github.com/TeaOSLab/EdgeCommon => ../EdgeCommon
require ( require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0
github.com/TeaOSLab/EdgeCommon v0.0.0-00010101000000-000000000000 github.com/TeaOSLab/EdgeCommon v0.0.0-00010101000000-000000000000
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 github.com/aliyun/alibaba-cloud-sdk-go v1.62.587
github.com/andybalholm/brotli v1.0.4 github.com/andybalholm/brotli v1.0.4
github.com/aws/aws-sdk-go v1.40.45
github.com/cespare/xxhash v1.1.0 github.com/cespare/xxhash v1.1.0
github.com/cespare/xxhash/v2 v2.1.1 github.com/fsnotify/fsnotify v1.6.0
github.com/go-acme/lego/v4 v4.10.2 github.com/go-acme/lego/v4 v4.10.2
github.com/go-sql-driver/mysql v1.7.0 github.com/go-sql-driver/mysql v1.7.0
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
github.com/iwind/TeaGo v0.0.0-20230304012706-c1f4a4e27470 github.com/iwind/TeaGo v0.0.0-20240312020455-6f20b5121caf
github.com/iwind/gosock v0.0.0-20220505115348-f88412125a62 github.com/iwind/gosock v0.0.0-20220505115348-f88412125a62
github.com/miekg/dns v1.1.50 github.com/miekg/dns v1.1.50
github.com/mozillazg/go-pinyin v0.18.0 github.com/mozillazg/go-pinyin v0.18.0
github.com/pkg/sftp v1.12.0 github.com/pkg/sftp v1.12.0
github.com/shirou/gopsutil/v3 v3.22.2 github.com/shirou/gopsutil/v3 v3.22.2
github.com/smartwalle/alipay/v3 v3.1.7 github.com/smartwalle/alipay/v3 v3.2.20
golang.org/x/crypto v0.5.0 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.801
golang.org/x/net v0.8.0 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.801
golang.org/x/sys v0.8.0 github.com/volcengine/volc-sdk-golang v1.0.124
google.golang.org/grpc v1.45.0 golang.org/x/crypto v0.22.0
golang.org/x/net v0.24.0
golang.org/x/sys v0.19.0
google.golang.org/grpc v1.62.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.0 // indirect github.com/cenkalti/backoff/v4 v4.2.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-ole/go-ole v1.2.6 // indirect
github.com/golang/protobuf v1.5.2 // indirect github.com/golang-jwt/jwt/v5 v5.0.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/kr/fs v0.1.0 // indirect github.com/kr/fs v0.1.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // 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/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/smartwalle/crypto4go v1.0.2 // indirect github.com/smartwalle/ncrypto v1.0.4 // indirect
github.com/smartwalle/ngx v1.0.9 // indirect
github.com/smartwalle/nsign v1.0.9 // indirect
github.com/technoweenie/multipartstreamer v1.0.1 // indirect github.com/technoweenie/multipartstreamer v1.0.1 // indirect
github.com/tklauser/go-sysconf v0.3.9 // indirect github.com/tklauser/go-sysconf v0.3.9 // indirect
github.com/tklauser/numcpus v0.3.0 // indirect github.com/tklauser/numcpus v0.3.0 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/mod v0.8.0 // indirect golang.org/x/mod v0.8.0 // indirect
golang.org/x/text v0.8.0 // indirect golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.6.0 // indirect golang.org/x/tools v0.6.0 // indirect
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240228224816-df926f6c8641 // indirect
google.golang.org/protobuf v1.28.0 // indirect google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/ini.v1 v1.66.6 // indirect gopkg.in/ini.v1 v1.66.6 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect
) )

759
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -130,6 +130,9 @@ func TestGenerate_EAB(t *testing.T) {
} else { } else {
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
} }
if err != nil {
t.Fatal(err)
}
myUser.Registration = reg myUser.Registration = reg
request := certificate.ObtainRequest{ request := certificate.ObtainRequest{

View File

@@ -1,6 +1,7 @@
package acme package acme
import ( import (
"fmt"
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients" "github.com/TeaOSLab/EdgeAPI/internal/dnsclients"
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/dnstypes" "github.com/TeaOSLab/EdgeAPI/internal/dnsclients/dnstypes"
"github.com/TeaOSLab/EdgeAPI/internal/errors" "github.com/TeaOSLab/EdgeAPI/internal/errors"
@@ -45,7 +46,7 @@ func (this *DNSProvider) Present(domain, token, keyAuth string) error {
if !wasDeleted { if !wasDeleted {
records, err := this.raw.QueryRecords(this.dnsDomain, recordName, dnstypes.RecordTypeTXT) records, err := this.raw.QueryRecords(this.dnsDomain, recordName, dnstypes.RecordTypeTXT)
if err != nil { if err != nil {
return errors.New("query DNS record failed: " + err.Error()) return fmt.Errorf("query DNS record failed: %w", err)
} }
for _, record := range records { for _, record := range records {
err = this.raw.DeleteRecord(this.dnsDomain, record) err = this.raw.DeleteRecord(this.dnsDomain, record)
@@ -67,7 +68,7 @@ func (this *DNSProvider) Present(domain, token, keyAuth string) error {
Route: this.raw.DefaultRoute(), Route: this.raw.DefaultRoute(),
}) })
if err != nil { if err != nil {
return errors.New("create DNS record failed: " + err.Error()) return fmt.Errorf("create DNS record failed: %w", err)
} }
return nil return nil

View File

@@ -1,6 +1,7 @@
package acme package acme
import ( import (
"fmt"
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const" teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
"github.com/TeaOSLab/EdgeAPI/internal/errors" "github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/go-acme/lego/v4/certcrypto" "github.com/go-acme/lego/v4/certcrypto"
@@ -92,26 +93,26 @@ func (this *Request) runDNS() (certData []byte, keyData []byte, err error) {
// 注册用户 // 注册用户
var resource = this.task.User.GetRegistration() var resource = this.task.User.GetRegistration()
if resource != nil { if resource != nil {
resource, err = client.Registration.QueryRegistration() _, err = client.Registration.QueryRegistration()
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
} else { } else {
if this.task.Provider.RequireEAB { if this.task.Provider.RequireEAB {
resource, err := client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ resource, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
TermsOfServiceAgreed: true, TermsOfServiceAgreed: true,
Kid: this.task.Account.EABKid, Kid: this.task.Account.EABKid,
HmacEncoded: this.task.Account.EABKey, HmacEncoded: this.task.Account.EABKey,
}) })
if err != nil { if err != nil {
return nil, nil, errors.New("register user failed: " + err.Error()) return nil, nil, fmt.Errorf("register user failed: %w", err)
} }
err = this.task.User.Register(resource) err = this.task.User.Register(resource)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
} else { } else {
resource, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) resource, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -134,7 +135,7 @@ func (this *Request) runDNS() (certData []byte, keyData []byte, err error) {
} }
certResource, err := client.Certificate.Obtain(request) certResource, err := client.Certificate.Obtain(request)
if err != nil { if err != nil {
return nil, nil, errors.New("obtain cert failed: " + err.Error()) return nil, nil, fmt.Errorf("obtain cert failed: %w", err)
} }
return certResource.Certificate, certResource.PrivateKey, nil return certResource.Certificate, certResource.PrivateKey, nil
@@ -165,26 +166,26 @@ func (this *Request) runHTTP() (certData []byte, keyData []byte, err error) {
// 注册用户 // 注册用户
var resource = this.task.User.GetRegistration() var resource = this.task.User.GetRegistration()
if resource != nil { if resource != nil {
resource, err = client.Registration.QueryRegistration() _, err = client.Registration.QueryRegistration()
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
} else { } else {
if this.task.Provider.RequireEAB { if this.task.Provider.RequireEAB {
resource, err := client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ resource, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
TermsOfServiceAgreed: true, TermsOfServiceAgreed: true,
Kid: this.task.Account.EABKid, Kid: this.task.Account.EABKid,
HmacEncoded: this.task.Account.EABKey, HmacEncoded: this.task.Account.EABKey,
}) })
if err != nil { if err != nil {
return nil, nil, errors.New("register user failed: " + err.Error()) return nil, nil, fmt.Errorf("register user failed: %w", err)
} }
err = this.task.User.Register(resource) err = this.task.User.Register(resource)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
} else { } else {
resource, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) resource, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@@ -1,6 +1,7 @@
package apps package apps
import ( import (
"errors"
"fmt" "fmt"
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const" teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
"github.com/iwind/TeaGo/logs" "github.com/iwind/TeaGo/logs"
@@ -9,8 +10,10 @@ import (
"github.com/iwind/gosock/pkg/gosock" "github.com/iwind/gosock/pkg/gosock"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"runtime" "runtime"
"strconv" "strconv"
"strings"
"time" "time"
) )
@@ -184,13 +187,16 @@ func (this *AppCmd) runStart() {
return return
} }
cmd := exec.Command(os.Args[0]) var cmd = exec.Command(this.exe())
err := cmd.Start() err := cmd.Start()
if err != nil { if err != nil {
fmt.Println(this.product+" start failed:", err.Error()) fmt.Println(this.product+" start failed:", err.Error())
return return
} }
// create symbolic links
_ = this.createSymLinks()
fmt.Println(this.product+" started ok, pid:", cmd.Process.Pid) fmt.Println(this.product+" started ok, pid:", cmd.Process.Pid)
} }
@@ -237,3 +243,58 @@ func (this *AppCmd) getPID() int {
} }
return maps.NewMap(reply.Params).GetInt("pid") return maps.NewMap(reply.Params).GetInt("pid")
} }
func (this *AppCmd) exe() string {
var exe, _ = os.Executable()
if len(exe) == 0 {
exe = os.Args[0]
}
return exe
}
// 创建软链接
func (this *AppCmd) createSymLinks() error {
if runtime.GOOS != "linux" {
return nil
}
var exe, _ = os.Executable()
if len(exe) == 0 {
return nil
}
var errorList = []string{}
// bin
{
var target = "/usr/bin/" + teaconst.ProcessName
old, _ := filepath.EvalSymlinks(target)
if old != exe {
_ = os.Remove(target)
err := os.Symlink(exe, target)
if err != nil {
errorList = append(errorList, err.Error())
}
}
}
// log
{
var realPath = filepath.Dir(filepath.Dir(exe)) + "/logs/run.log"
var target = "/var/log/" + teaconst.ProcessName + ".log"
old, _ := filepath.EvalSymlinks(target)
if old != realPath {
_ = os.Remove(target)
err := os.Symlink(realPath, target)
if err != nil {
errorList = append(errorList, err.Error())
}
}
}
if len(errorList) > 0 {
return errors.New(strings.Join(errorList, "\n"))
}
return nil
}

View File

@@ -1,12 +1,14 @@
package teaconst package teaconst
const ( const (
Version = "1.2.0" Version = "1.3.7"
ProductName = "Edge API" ProductName = "Edge API"
ProcessName = "edge-api" ProcessName = "edge-api"
ProductNameZH = "Edge" ProductNameZH = "Edge"
GlobalProductName = "GoEdge"
Role = "api" Role = "api"
EncryptKey = "8f983f4d69b83aaa0d74b21a212f6967" EncryptKey = "8f983f4d69b83aaa0d74b21a212f6967"
@@ -18,8 +20,5 @@ const (
// 其他节点版本号,用来检测是否有需要升级的节点 // 其他节点版本号,用来检测是否有需要升级的节点
NodeVersion = "1.2.0" NodeVersion = "1.3.7"
// SQLVersion SQL版本号
SQLVersion = "11"
) )

View File

@@ -0,0 +1,9 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package teaconst
const (
// DefaultMaxNodes 节点数限制
DefaultMaxNodes int32 = 50
)

View File

@@ -2,6 +2,7 @@ package acme
import ( import (
"bytes" "bytes"
"context"
"encoding/json" "encoding/json"
acmeutils "github.com/TeaOSLab/EdgeAPI/internal/acme" acmeutils "github.com/TeaOSLab/EdgeAPI/internal/acme"
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const" teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
@@ -106,9 +107,17 @@ func (this *ACMETaskDAO) DisableAllTasksWithCertId(tx *dbs.Tx, certId int64) err
} }
// CountAllEnabledACMETasks 计算所有任务数量 // CountAllEnabledACMETasks 计算所有任务数量
func (this *ACMETaskDAO) CountAllEnabledACMETasks(tx *dbs.Tx, userId int64, isAvailable bool, isExpired bool, expiringDays int64, keyword string) (int64, error) { func (this *ACMETaskDAO) CountAllEnabledACMETasks(tx *dbs.Tx, userId int64, isAvailable bool, isExpired bool, expiringDays int64, keyword string, userOnly bool) (int64, error) {
var query = this.Query(tx) var query = this.Query(tx)
query.Attr("userId", userId) // 这个条件必须加上 if userId > 0 {
query.Attr("userId", userId)
} else {
if userOnly {
query.Gt("userId", 0)
} else {
query.Attr("userId", 0)
}
}
if isAvailable || isExpired || expiringDays > 0 { if isAvailable || isExpired || expiringDays > 0 {
query.Gt("certId", 0) query.Gt("certId", 0)
@@ -138,9 +147,17 @@ func (this *ACMETaskDAO) CountAllEnabledACMETasks(tx *dbs.Tx, userId int64, isAv
} }
// ListEnabledACMETasks 列出单页任务 // ListEnabledACMETasks 列出单页任务
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) { func (this *ACMETaskDAO) ListEnabledACMETasks(tx *dbs.Tx, userId int64, isAvailable bool, isExpired bool, expiringDays int64, keyword string, userOnly bool, offset int64, size int64) (result []*ACMETask, err error) {
var query = this.Query(tx) var query = this.Query(tx)
query.Attr("userId", userId) // 这个条件必须加上 if userId > 0 {
query.Attr("userId", userId)
} else {
if userOnly {
query.Gt("userId", 0)
} else {
query.Attr("userId", 0)
}
}
if isAvailable || isExpired || expiringDays > 0 { if isAvailable || isExpired || expiringDays > 0 {
query.Gt("certId", 0) query.Gt("certId", 0)
@@ -228,8 +245,8 @@ func (this *ACMETaskDAO) UpdateACMETask(tx *dbs.Tx, acmeTaskId int64, acmeUserId
return err return err
} }
// CheckACMETask 检查权限 // CheckUserACMETask 检查用户权限
func (this *ACMETaskDAO) CheckACMETask(tx *dbs.Tx, userId int64, acmeTaskId int64) (bool, error) { func (this *ACMETaskDAO) CheckUserACMETask(tx *dbs.Tx, userId int64, acmeTaskId int64) (bool, error) {
var query = this.Query(tx) var query = this.Query(tx)
if userId > 0 { if userId > 0 {
query.Attr("userId", userId) query.Attr("userId", userId)
@@ -241,6 +258,15 @@ func (this *ACMETaskDAO) CheckACMETask(tx *dbs.Tx, userId int64, acmeTaskId int6
Exist() Exist()
} }
// FindACMETaskUserId 查找任务所属用户ID
func (this *ACMETaskDAO) FindACMETaskUserId(tx *dbs.Tx, taskId int64) (userId int64, err error) {
return this.Query(tx).
Pk(taskId).
Result("userId").
FindInt64Col(0)
}
// UpdateACMETaskCert 设置任务关联的证书 // UpdateACMETaskCert 设置任务关联的证书
func (this *ACMETaskDAO) UpdateACMETaskCert(tx *dbs.Tx, taskId int64, certId int64) error { func (this *ACMETaskDAO) UpdateACMETaskCert(tx *dbs.Tx, taskId int64, certId int64) error {
if taskId <= 0 { if taskId <= 0 {
@@ -434,7 +460,7 @@ func (this *ACMETaskDAO) runTaskWithoutLog(tx *dbs.Tx, taskId int64) (isOk bool,
CertData: certData, CertData: certData,
KeyData: keyData, KeyData: keyData,
} }
err = sslConfig.Init(nil) err = sslConfig.Init(context.Background())
if err != nil { if err != nil {
errMsg = "证书生成成功,但是分析证书信息时发生错误:" + err.Error() errMsg = "证书生成成功,但是分析证书信息时发生错误:" + err.Error()
return return

View File

@@ -1,6 +1,7 @@
package acme package acme
import ( import (
"github.com/TeaOSLab/EdgeAPI/internal/utils"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/dbs"
@@ -31,7 +32,7 @@ func init() {
func (this *ACMETaskLogDAO) CreateACMETaskLog(tx *dbs.Tx, taskId int64, isOk bool, errMsg string) error { func (this *ACMETaskLogDAO) CreateACMETaskLog(tx *dbs.Tx, taskId int64, isOk bool, errMsg string) error {
var op = NewACMETaskLogOperator() var op = NewACMETaskLogOperator()
op.TaskId = taskId op.TaskId = taskId
op.Error = errMsg op.Error = utils.LimitString(errMsg, 1024)
op.IsOk = isOk op.IsOk = isOk
err := this.Save(tx, op) err := this.Save(tx, op)
return err return err

View File

@@ -130,6 +130,19 @@ func (this *AdminDAO) FindAdminIdWithUsername(tx *dbs.Tx, username string) (int6
return int64(one.(*Admin).Id), nil return int64(one.(*Admin).Id), nil
} }
// FindAdminWithUsername 根据用户名查询管理员信息
func (this *AdminDAO) FindAdminWithUsername(tx *dbs.Tx, username string) (*Admin, error) {
one, err := this.Query(tx).
Attr("username", username).
State(AdminStateEnabled).
ResultPk().
Find()
if err != nil || one == nil {
return nil, err
}
return one.(*Admin), nil
}
// UpdateAdminPassword 更改管理员密码 // UpdateAdminPassword 更改管理员密码
func (this *AdminDAO) UpdateAdminPassword(tx *dbs.Tx, adminId int64, password string) error { func (this *AdminDAO) UpdateAdminPassword(tx *dbs.Tx, adminId int64, password string) error {
if adminId <= 0 { if adminId <= 0 {
@@ -212,7 +225,7 @@ func (this *AdminDAO) UpdateAdmin(tx *dbs.Tx, adminId int64, username string, ca
return nil return nil
} }
// CheckAdminUsername 检查用户名是否存在 // CheckAdminUsername 检查管理员用户名是否存在
func (this *AdminDAO) CheckAdminUsername(tx *dbs.Tx, adminId int64, username string) (bool, error) { func (this *AdminDAO) CheckAdminUsername(tx *dbs.Tx, adminId int64, username string) (bool, error) {
query := this.Query(tx). query := this.Query(tx).
State(AdminStateEnabled). State(AdminStateEnabled).
@@ -260,7 +273,7 @@ func (this *AdminDAO) FindAllAdminModules(tx *dbs.Tx) (result []*Admin, err erro
_, err = this.Query(tx). _, err = this.Query(tx).
State(AdminStateEnabled). State(AdminStateEnabled).
Attr("isOn", true). Attr("isOn", true).
Result("id", "modules", "isSuper", "fullname", "theme"). Result("id", "modules", "isSuper", "fullname", "theme", "lang").
Slice(&result). Slice(&result).
FindAll() FindAll()
return return
@@ -313,6 +326,14 @@ func (this *AdminDAO) UpdateAdminTheme(tx *dbs.Tx, adminId int64, theme string)
UpdateQuickly() UpdateQuickly()
} }
// UpdateAdminLang 设置管理员语言
func (this *AdminDAO) UpdateAdminLang(tx *dbs.Tx, adminId int64, langCode string) error {
return this.Query(tx).
Pk(adminId).
Set("lang", langCode).
UpdateQuickly()
}
// CheckSuperAdmin 检查管理员是否为超级管理员 // CheckSuperAdmin 检查管理员是否为超级管理员
func (this *AdminDAO) CheckSuperAdmin(tx *dbs.Tx, adminId int64) (bool, error) { func (this *AdminDAO) CheckSuperAdmin(tx *dbs.Tx, adminId int64) (bool, error) {
if adminId <= 0 { if adminId <= 0 {

View File

@@ -2,6 +2,22 @@ package models
import "github.com/iwind/TeaGo/dbs" import "github.com/iwind/TeaGo/dbs"
const (
AdminField_Id dbs.FieldName = "id" // ID
AdminField_IsOn dbs.FieldName = "isOn" // 是否启用
AdminField_Username dbs.FieldName = "username" // 用户名
AdminField_Password dbs.FieldName = "password" // 密码
AdminField_Fullname dbs.FieldName = "fullname" // 全名
AdminField_IsSuper dbs.FieldName = "isSuper" // 是否为超级管理员
AdminField_CreatedAt dbs.FieldName = "createdAt" // 创建时间
AdminField_UpdatedAt dbs.FieldName = "updatedAt" // 修改时间
AdminField_State dbs.FieldName = "state" // 状态
AdminField_Modules dbs.FieldName = "modules" // 允许的模块
AdminField_CanLogin dbs.FieldName = "canLogin" // 是否可以登录
AdminField_Theme dbs.FieldName = "theme" // 模板设置
AdminField_Lang dbs.FieldName = "lang" // 语言代号
)
// Admin 管理员 // Admin 管理员
type Admin struct { type Admin struct {
Id uint32 `field:"id"` // ID Id uint32 `field:"id"` // ID
@@ -16,6 +32,7 @@ type Admin struct {
Modules dbs.JSON `field:"modules"` // 允许的模块 Modules dbs.JSON `field:"modules"` // 允许的模块
CanLogin bool `field:"canLogin"` // 是否可以登录 CanLogin bool `field:"canLogin"` // 是否可以登录
Theme string `field:"theme"` // 模板设置 Theme string `field:"theme"` // 模板设置
Lang string `field:"lang"` // 语言代号
} }
type AdminOperator struct { type AdminOperator struct {
@@ -31,6 +48,7 @@ type AdminOperator struct {
Modules any // 允许的模块 Modules any // 允许的模块
CanLogin any // 是否可以登录 CanLogin any // 是否可以登录
Theme any // 模板设置 Theme any // 模板设置
Lang any // 语言代号
} }
func NewAdminOperator() *AdminOperator { func NewAdminOperator() *AdminOperator {

View File

@@ -1,6 +1,7 @@
package models package models
import ( import (
"context"
"encoding/json" "encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/utils" "github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
@@ -37,7 +38,7 @@ func (this *APINode) DecodeHTTPS(tx *dbs.Tx, cacheMap *utils.CacheMap) (*serverc
return nil, err return nil, err
} }
err = config.Init(nil) err = config.Init(context.TODO())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -55,7 +56,7 @@ func (this *APINode) DecodeHTTPS(tx *dbs.Tx, cacheMap *utils.CacheMap) (*serverc
} }
} }
err = config.Init(nil) err = config.Init(context.TODO())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -135,7 +136,7 @@ func (this *APINode) DecodeRestHTTPS(tx *dbs.Tx, cacheMap *utils.CacheMap) (*ser
return nil, err return nil, err
} }
err = config.Init(nil) err = config.Init(context.TODO())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -153,7 +154,7 @@ func (this *APINode) DecodeRestHTTPS(tx *dbs.Tx, cacheMap *utils.CacheMap) (*ser
} }
} }
err = config.Init(nil) err = config.Init(context.TODO())
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

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

View File

@@ -2,6 +2,18 @@ package authority
import "github.com/iwind/TeaGo/dbs" import "github.com/iwind/TeaGo/dbs"
const (
AuthorityKeyField_Id dbs.FieldName = "id" // ID
AuthorityKeyField_Value dbs.FieldName = "value" // Key值
AuthorityKeyField_DayFrom dbs.FieldName = "dayFrom" // 开始日期
AuthorityKeyField_DayTo dbs.FieldName = "dayTo" // 结束日期
AuthorityKeyField_Hostname dbs.FieldName = "hostname" // Hostname
AuthorityKeyField_MacAddresses dbs.FieldName = "macAddresses" // MAC地址
AuthorityKeyField_UpdatedAt dbs.FieldName = "updatedAt" // 创建/修改时间
AuthorityKeyField_Company dbs.FieldName = "company" // 公司组织
AuthorityKeyField_RequestCode dbs.FieldName = "requestCode" // 申请码
)
// AuthorityKey 企业版认证信息 // AuthorityKey 企业版认证信息
type AuthorityKey struct { type AuthorityKey struct {
Id uint32 `field:"id"` // ID Id uint32 `field:"id"` // ID
@@ -12,17 +24,19 @@ type AuthorityKey struct {
MacAddresses dbs.JSON `field:"macAddresses"` // MAC地址 MacAddresses dbs.JSON `field:"macAddresses"` // MAC地址
UpdatedAt uint64 `field:"updatedAt"` // 创建/修改时间 UpdatedAt uint64 `field:"updatedAt"` // 创建/修改时间
Company string `field:"company"` // 公司组织 Company string `field:"company"` // 公司组织
RequestCode string `field:"requestCode"` // 申请码
} }
type AuthorityKeyOperator struct { type AuthorityKeyOperator struct {
Id interface{} // ID Id any // ID
Value interface{} // Key值 Value any // Key值
DayFrom interface{} // 开始日期 DayFrom any // 开始日期
DayTo interface{} // 结束日期 DayTo any // 结束日期
Hostname interface{} // Hostname Hostname any // Hostname
MacAddresses interface{} // MAC地址 MacAddresses any // MAC地址
UpdatedAt interface{} // 创建/修改时间 UpdatedAt any // 创建/修改时间
Company interface{} // 公司组织 Company any // 公司组织
RequestCode any // 申请码
} }
func NewAuthorityKeyOperator() *AuthorityKeyOperator { func NewAuthorityKeyOperator() *AuthorityKeyOperator {

View File

@@ -61,11 +61,12 @@ func (this *DNSTaskDAO) CreateDNSTask(tx *dbs.Tx, clusterId int64, serverId int6
"error": "", "error": "",
"version": time.Now().UnixNano(), "version": time.Now().UnixNano(),
}, maps.Map{ }, maps.Map{
"updatedAt": time.Now().Unix(), "updatedAt": time.Now().Unix(),
"isDone": false, "isDone": false,
"isOk": false, "isOk": false,
"error": "", "error": "",
"version": time.Now().UnixNano(), "version": time.Now().UnixNano(),
"countFails": 0,
}) })
if err != nil { if err != nil {
return err return err
@@ -108,7 +109,7 @@ func (this *DNSTaskDAO) CreateDomainTask(tx *dbs.Tx, domainId int64, taskType DN
// FindAllDoingTasks 查找所有正在执行的任务 // FindAllDoingTasks 查找所有正在执行的任务
func (this *DNSTaskDAO) FindAllDoingTasks(tx *dbs.Tx) (result []*DNSTask, err error) { func (this *DNSTaskDAO) FindAllDoingTasks(tx *dbs.Tx) (result []*DNSTask, err error) {
_, err = this.Query(tx). _, err = this.Query(tx).
Attr("isDone", 0). Where("(isDone=0 OR (isDone=1 AND isOk=0 AND countFails<3))"). // 3 = retry times
Asc("version"). Asc("version").
AscPk(). AscPk().
Slice(&result). Slice(&result).
@@ -155,6 +156,12 @@ func (this *DNSTaskDAO) DeleteDNSTask(tx *dbs.Tx, taskId int64) error {
return err return err
} }
// DeleteAllDNSTasks 删除所有任务
func (this *DNSTaskDAO) DeleteAllDNSTasks(tx *dbs.Tx) error {
return this.Query(tx).
DeleteQuickly()
}
// UpdateDNSTaskError 设置任务错误 // UpdateDNSTaskError 设置任务错误
func (this *DNSTaskDAO) UpdateDNSTaskError(tx *dbs.Tx, taskId int64, err string) error { func (this *DNSTaskDAO) UpdateDNSTaskError(tx *dbs.Tx, taskId int64, err string) error {
if taskId <= 0 { if taskId <= 0 {
@@ -165,6 +172,7 @@ func (this *DNSTaskDAO) UpdateDNSTaskError(tx *dbs.Tx, taskId int64, err string)
op.IsDone = true op.IsDone = true
op.Error = err op.Error = err
op.IsOk = false op.IsOk = false
op.CountFails = dbs.SQL("countFails+1")
return this.Save(tx, op) return this.Save(tx, op)
} }
@@ -191,10 +199,33 @@ func (this *DNSTaskDAO) UpdateDNSTaskDone(tx *dbs.Tx, taskId int64, taskVersion
op.Id = taskId op.Id = taskId
op.IsDone = true op.IsDone = true
op.IsOk = true op.IsOk = true
op.CountFails = 0
op.Error = "" op.Error = ""
return this.Save(tx, op) return this.Save(tx, op)
} }
// GenerateVersion 生成最新的版本号
func (this *DNSTaskDAO) GenerateVersion() int64 {
return time.Now().UnixNano()
}
// UpdateClusterDNSTasksDone 设置所有集群任务完成
func (this *DNSTaskDAO) UpdateClusterDNSTasksDone(tx *dbs.Tx, clusterId int64, maxVersion int64) error {
if clusterId <= 0 || maxVersion <= 0 {
return nil
}
return this.Query(tx).
Attr("clusterId", clusterId).
Attr("isOk", false).
Lte("version", maxVersion).
Set("isDone", true).
Set("isOk", true).
Set("error", "").
Set("countFails", 0).
UpdateQuickly()
}
// DeleteDNSTasksWithClusterId 删除集群相关任务 // DeleteDNSTasksWithClusterId 删除集群相关任务
func (this *DNSTaskDAO) DeleteDNSTasksWithClusterId(tx *dbs.Tx, clusterId int64) error { func (this *DNSTaskDAO) DeleteDNSTasksWithClusterId(tx *dbs.Tx, clusterId int64) error {
if clusterId <= 0 { if clusterId <= 0 {

View File

@@ -1,17 +1,28 @@
package dns package dns_test
import ( import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models/dns"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap" _ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/dbs"
"testing" "testing"
"time"
) )
func TestDNSTaskDAO_CreateDNSTask(t *testing.T) { func TestDNSTaskDAO_CreateDNSTask(t *testing.T) {
dbs.NotifyReady() dbs.NotifyReady()
err := SharedDNSTaskDAO.CreateDNSTask(nil, 1, 2, 3, 0, "cdn", "taskType") err := dns.SharedDNSTaskDAO.CreateDNSTask(nil, 1, 2, 3, 0, "cdn", "taskType")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
t.Log("ok") t.Log("ok")
} }
func TestDNSTaskDAO_UpdateClusterDNSTasksDone(t *testing.T) {
var dao = dns.NewDNSTaskDAO()
var tx *dbs.Tx
err := dao.UpdateClusterDNSTasksDone(tx, 46, time.Now().UnixNano())
if err != nil {
t.Fatal(err)
}
}

View File

@@ -1,5 +1,23 @@
package dns package dns
import "github.com/iwind/TeaGo/dbs"
const (
DNSTaskField_Id dbs.FieldName = "id" // ID
DNSTaskField_ClusterId dbs.FieldName = "clusterId" // 集群ID
DNSTaskField_ServerId dbs.FieldName = "serverId" // 服务ID
DNSTaskField_NodeId dbs.FieldName = "nodeId" // 节点ID
DNSTaskField_DomainId dbs.FieldName = "domainId" // 域名ID
DNSTaskField_RecordName dbs.FieldName = "recordName" // 记录名
DNSTaskField_Type dbs.FieldName = "type" // 任务类型
DNSTaskField_UpdatedAt dbs.FieldName = "updatedAt" // 更新时间
DNSTaskField_IsDone dbs.FieldName = "isDone" // 是否已完成
DNSTaskField_IsOk dbs.FieldName = "isOk" // 是否成功
DNSTaskField_Error dbs.FieldName = "error" // 错误信息
DNSTaskField_Version dbs.FieldName = "version" // 版本
DNSTaskField_CountFails dbs.FieldName = "countFails" // 尝试失败次数
)
// DNSTask DNS更新任务 // DNSTask DNS更新任务
type DNSTask struct { type DNSTask struct {
Id uint64 `field:"id"` // ID Id uint64 `field:"id"` // ID
@@ -14,6 +32,7 @@ type DNSTask struct {
IsOk bool `field:"isOk"` // 是否成功 IsOk bool `field:"isOk"` // 是否成功
Error string `field:"error"` // 错误信息 Error string `field:"error"` // 错误信息
Version uint64 `field:"version"` // 版本 Version uint64 `field:"version"` // 版本
CountFails uint32 `field:"countFails"` // 尝试失败次数
} }
type DNSTaskOperator struct { type DNSTaskOperator struct {
@@ -29,6 +48,7 @@ type DNSTaskOperator struct {
IsOk any // 是否成功 IsOk any // 是否成功
Error any // 错误信息 Error any // 错误信息
Version any // 版本 Version any // 版本
CountFails any // 尝试失败次数
} }
func NewDNSTaskOperator() *DNSTaskOperator { func NewDNSTaskOperator() *DNSTaskOperator {

View File

@@ -3,6 +3,7 @@
package dnsutils package dnsutils
import ( import (
"fmt"
"github.com/TeaOSLab/EdgeAPI/internal/db/models" "github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/dns" "github.com/TeaOSLab/EdgeAPI/internal/db/models/dns"
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients" "github.com/TeaOSLab/EdgeAPI/internal/dnsclients"
@@ -217,7 +218,7 @@ func FindDefaultDomainRoute(tx *dbs.Tx, domain *dns.DNSDomain) (string, error) {
} }
paramsMap, err := provider.DecodeAPIParams() paramsMap, err := provider.DecodeAPIParams()
if err != nil { if err != nil {
return "", errors.New("decode provider params failed: " + err.Error()) return "", fmt.Errorf("decode provider params failed: %w", err)
} }
var dnsProvider = dnsclients.FindProvider(provider.Type, int64(provider.Id)) var dnsProvider = dnsclients.FindProvider(provider.Type, int64(provider.Id))
if dnsProvider == nil { if dnsProvider == nil {

View File

@@ -7,8 +7,8 @@ import (
"github.com/TeaOSLab/EdgeAPI/internal/errors" "github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman" "github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs" "github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeAPI/internal/zero" "github.com/TeaOSLab/EdgeAPI/internal/zero"
"github.com/TeaOSLab/EdgeCommon/pkg/iputils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
@@ -232,7 +232,7 @@ Loop:
// CreateHTTPAccessLog 写入单条访问日志 // CreateHTTPAccessLog 写入单条访问日志
func (this *HTTPAccessLogDAO) CreateHTTPAccessLog(tx *dbs.Tx, dao *HTTPAccessLogDAO, accessLog *pb.HTTPAccessLog) error { func (this *HTTPAccessLogDAO) CreateHTTPAccessLog(tx *dbs.Tx, dao *HTTPAccessLogDAO, accessLog *pb.HTTPAccessLog) error {
var day = "" var day string
// 注意:如果你修改了 TimeISO8601 的逻辑,这里也需要同步修改 // 注意:如果你修改了 TimeISO8601 的逻辑,这里也需要同步修改
if len(accessLog.TimeISO8601) > 10 { if len(accessLog.TimeISO8601) > 10 {
day = strings.ReplaceAll(accessLog.TimeISO8601[:10], "-", "") day = strings.ReplaceAll(accessLog.TimeISO8601[:10], "-", "")
@@ -245,7 +245,7 @@ func (this *HTTPAccessLogDAO) CreateHTTPAccessLog(tx *dbs.Tx, dao *HTTPAccessLog
return err return err
} }
fields := map[string]interface{}{} var fields = map[string]any{}
fields["serverId"] = accessLog.ServerId fields["serverId"] = accessLog.ServerId
fields["nodeId"] = accessLog.NodeId fields["nodeId"] = accessLog.NodeId
fields["status"] = accessLog.Status fields["status"] = accessLog.Status
@@ -265,7 +265,11 @@ func (this *HTTPAccessLogDAO) CreateHTTPAccessLog(tx *dbs.Tx, dao *HTTPAccessLog
fields["remoteAddr"] = accessLog.RemoteAddr fields["remoteAddr"] = accessLog.RemoteAddr
} }
if tableDef.HasDomain { if tableDef.HasDomain {
fields["domain"] = accessLog.Host if len(accessLog.Host) > 128 {
fields["domain"] = accessLog.Host[:128]
} else {
fields["domain"] = accessLog.Host
}
} }
content, err := json.Marshal(accessLog) content, err := json.Marshal(accessLog)
@@ -461,6 +465,7 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx,
var protoReg = regexp.MustCompile(`proto:(\S+)`) var protoReg = regexp.MustCompile(`proto:(\S+)`)
var schemeReg = regexp.MustCompile(`scheme:(\S+)`) var schemeReg = regexp.MustCompile(`scheme:(\S+)`)
var methodReg = regexp.MustCompile(`(?:method|requestMethod):(\S+)`) var methodReg = regexp.MustCompile(`(?:method|requestMethod):(\S+)`)
var refererReg = regexp.MustCompile(`referer:(\S+)`)
var count = len(tableQueries) var count = len(tableQueries)
var wg = &sync.WaitGroup{} var wg = &sync.WaitGroup{}
@@ -515,14 +520,14 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx,
// keyword // keyword
if len(ip) > 0 { if len(ip) > 0 {
// TODO 支持IP范围 // TODO 支持IPv6范围
if tableQuery.hasRemoteAddrField { if tableQuery.hasRemoteAddrField {
// IP格式 // IP格式
if strings.Contains(ip, ",") || strings.Contains(ip, "-") { if strings.Contains(ip, ",") || strings.Contains(ip, "-") {
rangeConfig, err := shared.ParseIPRange(ip) rangeConfig, err := shared.ParseIPRange(ip)
if err == nil { if err == nil {
if len(rangeConfig.IPFrom) > 0 && len(rangeConfig.IPTo) > 0 { if len(rangeConfig.IPFrom) > 0 && len(rangeConfig.IPTo) > 0 {
query.Between("INET_ATON(remoteAddr)", utils.IP2Long(rangeConfig.IPFrom), utils.IP2Long(rangeConfig.IPTo)) query.Between("INET_ATON(remoteAddr)", iputils.ToLong(rangeConfig.IPFrom), iputils.ToLong(rangeConfig.IPTo))
} }
} }
} else { } else {
@@ -575,7 +580,7 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx,
if len(pieces) == 1 || len(pieces[1]) == 0 || pieces[0] == pieces[1] { if len(pieces) == 1 || len(pieces[1]) == 0 || pieces[0] == pieces[1] {
query.Attr("remoteAddr", pieces[0]) query.Attr("remoteAddr", pieces[0])
} else { } else {
query.Between("INET_ATON(remoteAddr)", utils.IP2Long(pieces[0]), utils.IP2Long(pieces[1])) query.Between("INET_ATON(remoteAddr)", iputils.ToLong(pieces[0]), iputils.ToLong(pieces[1]))
} }
} else if statusRangeReg.MatchString(keyword) { // status:200-400 } else if statusRangeReg.MatchString(keyword) { // status:200-400
isSpecialKeyword = true isSpecialKeyword = true
@@ -613,6 +618,11 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx,
var matches = methodReg.FindStringSubmatch(keyword) var matches = methodReg.FindStringSubmatch(keyword)
query.Where("JSON_EXTRACT(content, '$.requestMethod')=:keyword"). query.Where("JSON_EXTRACT(content, '$.requestMethod')=:keyword").
Param("keyword", strings.ToUpper(matches[1])) Param("keyword", strings.ToUpper(matches[1]))
} else if refererReg.MatchString(keyword) {
isSpecialKeyword = true
var matches = refererReg.FindStringSubmatch(keyword)
query.Where("JSON_EXTRACT(content, '$.referer') LIKE :keyword").
Param("keyword", dbutils.QuoteLike(matches[1]))
} }
if !isSpecialKeyword { if !isSpecialKeyword {
if regexp.MustCompile(`^ip:.+`).MatchString(keyword) { if regexp.MustCompile(`^ip:.+`).MatchString(keyword) {
@@ -857,8 +867,4 @@ func (this *HTTPAccessLogDAO) SetupQueue() {
oldAccessLogQueue = accessLogQueue oldAccessLogQueue = accessLogQueue
accessLogQueue = make(chan *pb.HTTPAccessLog, config.MaxLength) accessLogQueue = make(chan *pb.HTTPAccessLog, config.MaxLength)
} }
if Tea.IsTesting() {
remotelogs.Println("HTTP_ACCESS_LOG_QUEUE", "change queue "+string(configJSON))
}
} }

View File

@@ -41,7 +41,7 @@ func (this *HTTPAccessLogManager) FindTableNames(db *dbs.DB, day string) ([]stri
for _, prefix := range []string{"edgeHTTPAccessLogs_" + day + "%", "edgehttpaccesslogs_" + day + "%"} { for _, prefix := range []string{"edgeHTTPAccessLogs_" + day + "%", "edgehttpaccesslogs_" + day + "%"} {
ones, columnNames, err := db.FindPreparedOnes(`SHOW TABLES LIKE '` + prefix + `'`) ones, columnNames, err := db.FindPreparedOnes(`SHOW TABLES LIKE '` + prefix + `'`)
if err != nil { if err != nil {
return nil, errors.New("query table names error: " + err.Error()) return nil, fmt.Errorf("query table names error: %w", err)
} }
var columnName = columnNames[0] var columnName = columnNames[0]
@@ -88,7 +88,7 @@ func (this *HTTPAccessLogManager) FindTables(db *dbs.DB, day string) ([]*httpAcc
for _, prefix := range []string{"edgeHTTPAccessLogs_" + day + "%", "edgehttpaccesslogs_" + day + "%"} { for _, prefix := range []string{"edgeHTTPAccessLogs_" + day + "%", "edgehttpaccesslogs_" + day + "%"} {
ones, columnNames, err := db.FindPreparedOnes(`SHOW TABLES LIKE '` + prefix + `'`) ones, columnNames, err := db.FindPreparedOnes(`SHOW TABLES LIKE '` + prefix + `'`)
if err != nil { if err != nil {
return nil, errors.New("query table names error: " + err.Error()) return nil, fmt.Errorf("query table names error: %w", err)
} }
var columnName = columnNames[0] var columnName = columnNames[0]
@@ -239,7 +239,7 @@ func (this *HTTPAccessLogManager) FindLastTable(db *dbs.DB, day string, force bo
// CreateTable 创建访问日志表格 // CreateTable 创建访问日志表格
func (this *HTTPAccessLogManager) CreateTable(db *dbs.DB, tableName string) error { func (this *HTTPAccessLogManager) CreateTable(db *dbs.DB, tableName string) error {
_, err := db.Exec("CREATE TABLE `" + tableName + "` (\n `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',\n `serverId` int(11) unsigned DEFAULT '0' COMMENT '服务ID',\n `nodeId` int(11) unsigned DEFAULT '0' COMMENT '节点ID',\n `status` int(3) unsigned DEFAULT '0' COMMENT '状态码',\n `createdAt` bigint(11) unsigned DEFAULT '0' COMMENT '创建时间',\n `content` json DEFAULT NULL COMMENT '日志内容',\n `requestId` varchar(128) DEFAULT NULL COMMENT '请求ID',\n `firewallPolicyId` int(11) unsigned DEFAULT '0' COMMENT 'WAF策略ID',\n `firewallRuleGroupId` int(11) unsigned DEFAULT '0' COMMENT 'WAF分组ID',\n `firewallRuleSetId` int(11) unsigned DEFAULT '0' COMMENT 'WAF集ID',\n `firewallRuleId` int(11) unsigned DEFAULT '0' COMMENT 'WAF规则ID',\n `remoteAddr` varchar(64) DEFAULT NULL COMMENT 'IP地址',\n `domain` varchar(128) DEFAULT NULL COMMENT '域名',\n `requestBody` mediumblob COMMENT '请求内容',\n `responseBody` mediumblob COMMENT '响应内容',\n PRIMARY KEY (`id`),\n KEY `serverId` (`serverId`),\n KEY `nodeId` (`nodeId`),\n KEY `serverId_status` (`serverId`,`status`),\n KEY `requestId` (`requestId`),\n KEY `firewallPolicyId` (`firewallPolicyId`),\n KEY `firewallRuleGroupId` (`firewallRuleGroupId`),\n KEY `firewallRuleSetId` (`firewallRuleSetId`),\n KEY `firewallRuleId` (`firewallRuleId`),\n KEY `remoteAddr` (`remoteAddr`),\n KEY `domain` (`domain`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='访问日志';") _, err := db.Exec("CREATE TABLE `" + tableName + "` (\n `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',\n `serverId` int(11) unsigned DEFAULT '0' COMMENT '服务ID',\n `nodeId` int(11) unsigned DEFAULT '0' COMMENT '节点ID',\n `status` int(3) unsigned DEFAULT '0' COMMENT '状态码',\n `createdAt` bigint(11) unsigned DEFAULT '0' COMMENT '创建时间',\n `content` json DEFAULT NULL COMMENT '日志内容',\n `requestId` varchar(128) DEFAULT NULL COMMENT '请求ID',\n `firewallPolicyId` int(11) unsigned DEFAULT '0' COMMENT 'WAF策略ID',\n `firewallRuleGroupId` int(11) unsigned DEFAULT '0' COMMENT 'WAF分组ID',\n `firewallRuleSetId` int(11) unsigned DEFAULT '0' COMMENT 'WAF集ID',\n `firewallRuleId` int(11) unsigned DEFAULT '0' COMMENT 'WAF规则ID',\n `remoteAddr` varchar(64) DEFAULT NULL COMMENT 'IP地址',\n `domain` varchar(255) DEFAULT NULL COMMENT '域名',\n `requestBody` mediumblob COMMENT '请求内容',\n `responseBody` mediumblob COMMENT '响应内容',\n PRIMARY KEY (`id`),\n KEY `serverId` (`serverId`),\n KEY `nodeId` (`nodeId`),\n KEY `serverId_status` (`serverId`,`status`),\n KEY `requestId` (`requestId`),\n KEY `firewallPolicyId` (`firewallPolicyId`),\n KEY `firewallRuleGroupId` (`firewallRuleGroupId`),\n KEY `firewallRuleSetId` (`firewallRuleSetId`),\n KEY `firewallRuleId` (`firewallRuleId`),\n KEY `remoteAddr` (`remoteAddr`),\n KEY `domain` (`domain`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='访问日志';")
if err != nil { if err != nil {
if CheckSQLErrCode(err, 1050) { // Error 1050: Table 'xxx' already exists if CheckSQLErrCode(err, 1050) { // Error 1050: Table 'xxx' already exists
return nil return nil
@@ -373,7 +373,7 @@ func (this *HTTPAccessLogManager) findTableWithoutCache(db *dbs.DB, day string,
var lastInt64Id = types.Int64(lastId) var lastInt64Id = types.Int64(lastId)
if accessLogRowsPerTable > 0 && lastInt64Id >= accessLogRowsPerTable { if accessLogRowsPerTable > 0 && lastInt64Id >= accessLogRowsPerTable {
// create next partial table // create next partial table
var nextTableName = "" var nextTableName string
if accessLogTableMainReg.MatchString(lastTableName) { if accessLogTableMainReg.MatchString(lastTableName) {
nextTableName = prefix + "_0001" nextTableName = prefix + "_0001"
} else if accessLogTablePartialReg.MatchString(lastTableName) { } else if accessLogTablePartialReg.MatchString(lastTableName) {

View File

@@ -107,7 +107,7 @@ func (this *HTTPAccessLogPolicyDAO) FindAllEnabledAndOnPolicies(tx *dbs.Tx) (res
} }
// CreatePolicy 创建策略 // CreatePolicy 创建策略
func (this *HTTPAccessLogPolicyDAO) CreatePolicy(tx *dbs.Tx, name string, policyType string, optionsJSON []byte, condsJSON []byte, isPublic bool, firewallOnly bool) (policyId int64, err error) { func (this *HTTPAccessLogPolicyDAO) CreatePolicy(tx *dbs.Tx, name string, policyType string, optionsJSON []byte, condsJSON []byte, isPublic bool, firewallOnly bool, disableDefaultDB bool) (policyId int64, err error) {
var op = NewHTTPAccessLogPolicyOperator() var op = NewHTTPAccessLogPolicyOperator()
op.Name = name op.Name = name
op.Type = policyType op.Type = policyType
@@ -120,12 +120,13 @@ func (this *HTTPAccessLogPolicyDAO) CreatePolicy(tx *dbs.Tx, name string, policy
op.IsPublic = isPublic op.IsPublic = isPublic
op.IsOn = true op.IsOn = true
op.FirewallOnly = firewallOnly op.FirewallOnly = firewallOnly
op.DisableDefaultDB = disableDefaultDB
op.State = HTTPAccessLogPolicyStateEnabled op.State = HTTPAccessLogPolicyStateEnabled
return this.SaveInt64(tx, op) return this.SaveInt64(tx, op)
} }
// UpdatePolicy 修改策略 // UpdatePolicy 修改策略
func (this *HTTPAccessLogPolicyDAO) UpdatePolicy(tx *dbs.Tx, policyId int64, name string, optionsJSON []byte, condsJSON []byte, isPublic bool, firewallOnly bool, isOn bool) error { func (this *HTTPAccessLogPolicyDAO) UpdatePolicy(tx *dbs.Tx, policyId int64, name string, optionsJSON []byte, condsJSON []byte, isPublic bool, firewallOnly bool, disableDefaultDB bool, isOn bool) error {
if policyId <= 0 { if policyId <= 0 {
return errors.New("invalid policyId") return errors.New("invalid policyId")
} }
@@ -159,6 +160,7 @@ func (this *HTTPAccessLogPolicyDAO) UpdatePolicy(tx *dbs.Tx, policyId int64, nam
op.IsPublic = isPublic op.IsPublic = isPublic
op.FirewallOnly = firewallOnly op.FirewallOnly = firewallOnly
op.DisableDefaultDB = disableDefaultDB
op.IsOn = isOn op.IsOn = isOn
return this.Save(tx, op) return this.Save(tx, op)
} }

View File

@@ -2,39 +2,59 @@ package models
import "github.com/iwind/TeaGo/dbs" import "github.com/iwind/TeaGo/dbs"
const (
HTTPAccessLogPolicyField_Id dbs.FieldName = "id" // ID
HTTPAccessLogPolicyField_TemplateId dbs.FieldName = "templateId" // 模版ID
HTTPAccessLogPolicyField_AdminId dbs.FieldName = "adminId" // 管理员ID
HTTPAccessLogPolicyField_UserId dbs.FieldName = "userId" // 用户ID
HTTPAccessLogPolicyField_State dbs.FieldName = "state" // 状态
HTTPAccessLogPolicyField_CreatedAt dbs.FieldName = "createdAt" // 创建时间
HTTPAccessLogPolicyField_Name dbs.FieldName = "name" // 名称
HTTPAccessLogPolicyField_IsOn dbs.FieldName = "isOn" // 是否启用
HTTPAccessLogPolicyField_Type dbs.FieldName = "type" // 存储类型
HTTPAccessLogPolicyField_Options dbs.FieldName = "options" // 存储选项
HTTPAccessLogPolicyField_Conds dbs.FieldName = "conds" // 请求条件
HTTPAccessLogPolicyField_IsPublic dbs.FieldName = "isPublic" // 是否为公用
HTTPAccessLogPolicyField_FirewallOnly dbs.FieldName = "firewallOnly" // 是否只记录防火墙相关
HTTPAccessLogPolicyField_Version dbs.FieldName = "version" // 版本号
HTTPAccessLogPolicyField_DisableDefaultDB dbs.FieldName = "disableDefaultDB" // 是否停止默认数据库存储
)
// HTTPAccessLogPolicy 访问日志策略 // HTTPAccessLogPolicy 访问日志策略
type HTTPAccessLogPolicy struct { type HTTPAccessLogPolicy struct {
Id uint32 `field:"id"` // ID Id uint32 `field:"id"` // ID
TemplateId uint32 `field:"templateId"` // 模版ID TemplateId uint32 `field:"templateId"` // 模版ID
AdminId uint32 `field:"adminId"` // 管理员ID AdminId uint32 `field:"adminId"` // 管理员ID
UserId uint32 `field:"userId"` // 用户ID UserId uint32 `field:"userId"` // 用户ID
State uint8 `field:"state"` // 状态 State uint8 `field:"state"` // 状态
CreatedAt uint64 `field:"createdAt"` // 创建时间 CreatedAt uint64 `field:"createdAt"` // 创建时间
Name string `field:"name"` // 名称 Name string `field:"name"` // 名称
IsOn bool `field:"isOn"` // 是否启用 IsOn bool `field:"isOn"` // 是否启用
Type string `field:"type"` // 存储类型 Type string `field:"type"` // 存储类型
Options dbs.JSON `field:"options"` // 存储选项 Options dbs.JSON `field:"options"` // 存储选项
Conds dbs.JSON `field:"conds"` // 请求条件 Conds dbs.JSON `field:"conds"` // 请求条件
IsPublic bool `field:"isPublic"` // 是否为公用 IsPublic bool `field:"isPublic"` // 是否为公用
FirewallOnly uint8 `field:"firewallOnly"` // 是否只记录防火墙相关 FirewallOnly uint8 `field:"firewallOnly"` // 是否只记录防火墙相关
Version uint32 `field:"version"` // 版本号 Version uint32 `field:"version"` // 版本号
DisableDefaultDB bool `field:"disableDefaultDB"` // 是否停止默认数据库存储
} }
type HTTPAccessLogPolicyOperator struct { type HTTPAccessLogPolicyOperator struct {
Id interface{} // ID Id any // ID
TemplateId interface{} // 模版ID TemplateId any // 模版ID
AdminId interface{} // 管理员ID AdminId any // 管理员ID
UserId interface{} // 用户ID UserId any // 用户ID
State interface{} // 状态 State any // 状态
CreatedAt interface{} // 创建时间 CreatedAt any // 创建时间
Name interface{} // 名称 Name any // 名称
IsOn interface{} // 是否启用 IsOn any // 是否启用
Type interface{} // 存储类型 Type any // 存储类型
Options interface{} // 存储选项 Options any // 存储选项
Conds interface{} // 请求条件 Conds any // 请求条件
IsPublic interface{} // 是否为公用 IsPublic any // 是否为公用
FirewallOnly interface{} // 是否只记录防火墙相关 FirewallOnly any // 是否只记录防火墙相关
Version interface{} // 版本号 Version any // 版本号
DisableDefaultDB any // 是否停止默认数据库存储
} }
func NewHTTPAccessLogPolicyOperator() *HTTPAccessLogPolicyOperator { func NewHTTPAccessLogPolicyOperator() *HTTPAccessLogPolicyOperator {

View File

@@ -96,7 +96,7 @@ func (this *HTTPCachePolicyDAO) FindAllEnabledCachePolicies(tx *dbs.Tx) (result
} }
// CreateCachePolicy 创建缓存策略 // CreateCachePolicy 创建缓存策略
func (this *HTTPCachePolicyDAO) CreateCachePolicy(tx *dbs.Tx, isOn bool, name string, description string, capacityJSON []byte, maxKeys int64, maxSizeJSON []byte, storageType string, storageOptionsJSON []byte, syncCompressionCache bool) (int64, error) { func (this *HTTPCachePolicyDAO) CreateCachePolicy(tx *dbs.Tx, isOn bool, name string, description string, capacityJSON []byte, maxSizeJSON []byte, storageType string, storageOptionsJSON []byte, syncCompressionCache bool, fetchTimeoutJSON []byte) (int64, error) {
var op = NewHTTPCachePolicyOperator() var op = NewHTTPCachePolicyOperator()
op.State = HTTPCachePolicyStateEnabled op.State = HTTPCachePolicyStateEnabled
op.IsOn = isOn op.IsOn = isOn
@@ -105,7 +105,6 @@ func (this *HTTPCachePolicyDAO) CreateCachePolicy(tx *dbs.Tx, isOn bool, name st
if len(capacityJSON) > 0 { if len(capacityJSON) > 0 {
op.Capacity = capacityJSON op.Capacity = capacityJSON
} }
op.MaxKeys = maxKeys
if len(maxSizeJSON) > 0 { if len(maxSizeJSON) > 0 {
op.MaxSize = maxSizeJSON op.MaxSize = maxSizeJSON
} }
@@ -115,6 +114,10 @@ func (this *HTTPCachePolicyDAO) CreateCachePolicy(tx *dbs.Tx, isOn bool, name st
} }
op.SyncCompressionCache = syncCompressionCache op.SyncCompressionCache = syncCompressionCache
if len(fetchTimeoutJSON) > 0 {
op.FetchTimeout = fetchTimeoutJSON
}
// 默认的缓存条件 // 默认的缓存条件
cacheRef := &serverconfigs.HTTPCacheRef{ cacheRef := &serverconfigs.HTTPCacheRef{
IsOn: true, IsOn: true,
@@ -171,7 +174,8 @@ func (this *HTTPCachePolicyDAO) CreateDefaultCachePolicy(tx *dbs.Tx, name string
} }
var storageOptions = &serverconfigs.HTTPFileCacheStorage{ var storageOptions = &serverconfigs.HTTPFileCacheStorage{
Dir: "/opt/cache", Dir: "/opt/cache",
EnableMMAP: true,
MemoryPolicy: &serverconfigs.HTTPCachePolicy{ MemoryPolicy: &serverconfigs.HTTPCachePolicy{
Capacity: &shared.SizeCapacity{ Capacity: &shared.SizeCapacity{
Count: 1, Count: 1,
@@ -184,7 +188,7 @@ func (this *HTTPCachePolicyDAO) CreateDefaultCachePolicy(tx *dbs.Tx, name string
return 0, err return 0, err
} }
policyId, err := this.CreateCachePolicy(tx, true, "\""+name+"\"缓存策略", "默认创建的缓存策略", capacityJSON, 0, maxSizeJSON, serverconfigs.CachePolicyStorageFile, storageOptionsJSON, false) policyId, err := this.CreateCachePolicy(tx, true, "\""+name+"\"缓存策略", "默认创建的缓存策略", capacityJSON, maxSizeJSON, serverconfigs.CachePolicyStorageFile, storageOptionsJSON, false, nil)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -192,7 +196,7 @@ func (this *HTTPCachePolicyDAO) CreateDefaultCachePolicy(tx *dbs.Tx, name string
} }
// UpdateCachePolicy 修改缓存策略 // UpdateCachePolicy 修改缓存策略
func (this *HTTPCachePolicyDAO) UpdateCachePolicy(tx *dbs.Tx, policyId int64, isOn bool, name string, description string, capacityJSON []byte, maxKeys int64, maxSizeJSON []byte, storageType string, storageOptionsJSON []byte, syncCompressionCache bool) error { func (this *HTTPCachePolicyDAO) UpdateCachePolicy(tx *dbs.Tx, policyId int64, isOn bool, name string, description string, capacityJSON []byte, maxSizeJSON []byte, storageType string, storageOptionsJSON []byte, syncCompressionCache bool, fetchTimeoutJSON []byte) error {
if policyId <= 0 { if policyId <= 0 {
return errors.New("invalid policyId") return errors.New("invalid policyId")
} }
@@ -205,7 +209,6 @@ func (this *HTTPCachePolicyDAO) UpdateCachePolicy(tx *dbs.Tx, policyId int64, is
if len(capacityJSON) > 0 { if len(capacityJSON) > 0 {
op.Capacity = capacityJSON op.Capacity = capacityJSON
} }
op.MaxKeys = maxKeys
if len(maxSizeJSON) > 0 { if len(maxSizeJSON) > 0 {
op.MaxSize = maxSizeJSON op.MaxSize = maxSizeJSON
} }
@@ -214,6 +217,9 @@ func (this *HTTPCachePolicyDAO) UpdateCachePolicy(tx *dbs.Tx, policyId int64, is
op.Options = storageOptionsJSON op.Options = storageOptionsJSON
} }
op.SyncCompressionCache = syncCompressionCache op.SyncCompressionCache = syncCompressionCache
if len(fetchTimeoutJSON) > 0 {
op.FetchTimeout = fetchTimeoutJSON
}
err := this.Save(tx, op) err := this.Save(tx, op)
if err != nil { if err != nil {
return err return err
@@ -239,7 +245,7 @@ func (this *HTTPCachePolicyDAO) ComposeCachePolicy(tx *dbs.Tx, policyId int64, c
if policy == nil { if policy == nil {
return nil, nil return nil, nil
} }
config := &serverconfigs.HTTPCachePolicy{} var config = &serverconfigs.HTTPCachePolicy{}
config.Id = int64(policy.Id) config.Id = int64(policy.Id)
config.IsOn = policy.IsOn config.IsOn = policy.IsOn
config.Name = policy.Name config.Name = policy.Name
@@ -248,7 +254,7 @@ func (this *HTTPCachePolicyDAO) ComposeCachePolicy(tx *dbs.Tx, policyId int64, c
// capacity // capacity
if IsNotNull(policy.Capacity) { if IsNotNull(policy.Capacity) {
capacityConfig := &shared.SizeCapacity{} var capacityConfig = &shared.SizeCapacity{}
err = json.Unmarshal(policy.Capacity, capacityConfig) err = json.Unmarshal(policy.Capacity, capacityConfig)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -256,11 +262,9 @@ func (this *HTTPCachePolicyDAO) ComposeCachePolicy(tx *dbs.Tx, policyId int64, c
config.Capacity = capacityConfig config.Capacity = capacityConfig
} }
config.MaxKeys = types.Int64(policy.MaxKeys)
// max size // max size
if IsNotNull(policy.MaxSize) { if IsNotNull(policy.MaxSize) {
maxSizeConfig := &shared.SizeCapacity{} var maxSizeConfig = &shared.SizeCapacity{}
err = json.Unmarshal(policy.MaxSize, maxSizeConfig) err = json.Unmarshal(policy.MaxSize, maxSizeConfig)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -272,7 +276,7 @@ func (this *HTTPCachePolicyDAO) ComposeCachePolicy(tx *dbs.Tx, policyId int64, c
// options // options
if IsNotNull(policy.Options) { if IsNotNull(policy.Options) {
m := map[string]interface{}{} var m = map[string]any{}
err = json.Unmarshal(policy.Options, &m) err = json.Unmarshal(policy.Options, &m)
if err != nil { if err != nil {
return nil, errors.Wrap(err) return nil, errors.Wrap(err)
@@ -282,7 +286,7 @@ func (this *HTTPCachePolicyDAO) ComposeCachePolicy(tx *dbs.Tx, policyId int64, c
// refs // refs
if IsNotNull(policy.Refs) { if IsNotNull(policy.Refs) {
refs := []*serverconfigs.HTTPCacheRef{} var refs = []*serverconfigs.HTTPCacheRef{}
err = json.Unmarshal(policy.Refs, &refs) err = json.Unmarshal(policy.Refs, &refs)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -290,6 +294,16 @@ func (this *HTTPCachePolicyDAO) ComposeCachePolicy(tx *dbs.Tx, policyId int64, c
config.CacheRefs = refs config.CacheRefs = refs
} }
// fetch timeout
if IsNotNull(policy.FetchTimeout) {
var timeoutDuration = &shared.TimeDuration{}
err = json.Unmarshal(policy.FetchTimeout, timeoutDuration)
if err != nil {
return nil, err
}
config.FetchTimeout = timeoutDuration
}
if cacheMap != nil { if cacheMap != nil {
cacheMap.Put(cacheKey, config) cacheMap.Put(cacheKey, config)
} }

View File

@@ -2,6 +2,26 @@ package models
import "github.com/iwind/TeaGo/dbs" import "github.com/iwind/TeaGo/dbs"
const (
HTTPCachePolicyField_Id dbs.FieldName = "id" // ID
HTTPCachePolicyField_AdminId dbs.FieldName = "adminId" // 管理员ID
HTTPCachePolicyField_UserId dbs.FieldName = "userId" // 用户ID
HTTPCachePolicyField_TemplateId dbs.FieldName = "templateId" // 模版ID
HTTPCachePolicyField_IsOn dbs.FieldName = "isOn" // 是否启用
HTTPCachePolicyField_Name dbs.FieldName = "name" // 名称
HTTPCachePolicyField_Capacity dbs.FieldName = "capacity" // 容量数据
HTTPCachePolicyField_MaxKeys dbs.FieldName = "maxKeys" // 最多Key值
HTTPCachePolicyField_MaxSize dbs.FieldName = "maxSize" // 最大缓存内容尺寸
HTTPCachePolicyField_Type dbs.FieldName = "type" // 存储类型
HTTPCachePolicyField_Options dbs.FieldName = "options" // 存储选项
HTTPCachePolicyField_CreatedAt dbs.FieldName = "createdAt" // 创建时间
HTTPCachePolicyField_State dbs.FieldName = "state" // 状态
HTTPCachePolicyField_Description dbs.FieldName = "description" // 描述
HTTPCachePolicyField_Refs dbs.FieldName = "refs" // 默认的缓存设置
HTTPCachePolicyField_SyncCompressionCache dbs.FieldName = "syncCompressionCache" // 是否同步写入压缩缓存
HTTPCachePolicyField_FetchTimeout dbs.FieldName = "fetchTimeout" // 预热超时时间
)
// HTTPCachePolicy HTTP缓存策略 // HTTPCachePolicy HTTP缓存策略
type HTTPCachePolicy struct { type HTTPCachePolicy struct {
Id uint32 `field:"id"` // ID Id uint32 `field:"id"` // ID
@@ -20,25 +40,27 @@ type HTTPCachePolicy struct {
Description string `field:"description"` // 描述 Description string `field:"description"` // 描述
Refs dbs.JSON `field:"refs"` // 默认的缓存设置 Refs dbs.JSON `field:"refs"` // 默认的缓存设置
SyncCompressionCache uint8 `field:"syncCompressionCache"` // 是否同步写入压缩缓存 SyncCompressionCache uint8 `field:"syncCompressionCache"` // 是否同步写入压缩缓存
FetchTimeout dbs.JSON `field:"fetchTimeout"` // 预热超时时间
} }
type HTTPCachePolicyOperator struct { type HTTPCachePolicyOperator struct {
Id interface{} // ID Id any // ID
AdminId interface{} // 管理员ID AdminId any // 管理员ID
UserId interface{} // 用户ID UserId any // 用户ID
TemplateId interface{} // 模版ID TemplateId any // 模版ID
IsOn interface{} // 是否启用 IsOn any // 是否启用
Name interface{} // 名称 Name any // 名称
Capacity interface{} // 容量数据 Capacity any // 容量数据
MaxKeys interface{} // 最多Key值 MaxKeys any // 最多Key值
MaxSize interface{} // 最大缓存内容尺寸 MaxSize any // 最大缓存内容尺寸
Type interface{} // 存储类型 Type any // 存储类型
Options interface{} // 存储选项 Options any // 存储选项
CreatedAt interface{} // 创建时间 CreatedAt any // 创建时间
State interface{} // 状态 State any // 状态
Description interface{} // 描述 Description any // 描述
Refs interface{} // 默认的缓存设置 Refs any // 默认的缓存设置
SyncCompressionCache interface{} // 是否同步写入压缩缓存 SyncCompressionCache any // 是否同步写入压缩缓存
FetchTimeout any // 预热超时时间
} }
func NewHTTPCachePolicyOperator() *HTTPCachePolicyOperator { func NewHTTPCachePolicyOperator() *HTTPCachePolicyOperator {

View File

@@ -33,7 +33,7 @@ func init() {
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour) var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() { goman.New(func() {
for range ticker.C { for range ticker.C {
err := SharedHTTPCacheTaskDAO.Clean(nil, 30) // 只保留N天 err := SharedHTTPCacheTaskDAO.CleanDefaultDays(nil, 30) // 只保留N天
if err != nil { if err != nil {
remotelogs.Error("HTTPCacheTaskDAO", "clean expired data failed: "+err.Error()) remotelogs.Error("HTTPCacheTaskDAO", "clean expired data failed: "+err.Error())
} }
@@ -228,8 +228,8 @@ func (this *HTTPCacheTaskDAO) CheckUserTask(tx *dbs.Tx, userId int64, taskId int
return nil return nil
} }
// Clean 清理以往的任务 // CleanDays 清理N天以前的任务
func (this *HTTPCacheTaskDAO) Clean(tx *dbs.Tx, days int) error { func (this *HTTPCacheTaskDAO) CleanDays(tx *dbs.Tx, days int) error {
if days <= 0 { if days <= 0 {
days = 30 days = 30
} }
@@ -248,6 +248,23 @@ func (this *HTTPCacheTaskDAO) Clean(tx *dbs.Tx, days int) error {
return err return err
} }
// CleanDefaultDays 清除任务
func (this *HTTPCacheTaskDAO) CleanDefaultDays(tx *dbs.Tx, defaultDays int) error {
databaseConfig, err := SharedSysSettingDAO.ReadDatabaseConfig(tx)
if err != nil {
return err
}
if databaseConfig != nil && databaseConfig.HTTPCacheTask.Clean.Days > 0 {
defaultDays = databaseConfig.HTTPCacheTask.Clean.Days
}
if defaultDays <= 0 {
defaultDays = 30
}
return this.CleanDays(tx, defaultDays)
}
// NotifyChange 发送通知 // NotifyChange 发送通知
func (this *HTTPCacheTaskDAO) NotifyChange(tx *dbs.Tx, taskId int64) error { func (this *HTTPCacheTaskDAO) NotifyChange(tx *dbs.Tx, taskId int64) error {
// TODO // TODO

View File

@@ -11,7 +11,7 @@ import (
func TestHTTPCacheTaskDAO_Clean(t *testing.T) { func TestHTTPCacheTaskDAO_Clean(t *testing.T) {
dbs.NotifyReady() dbs.NotifyReady()
err := models.SharedHTTPCacheTaskDAO.Clean(nil, 30) err := models.SharedHTTPCacheTaskDAO.CleanDays(nil, 30)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@@ -134,7 +134,7 @@ func (this *HTTPFirewallPolicyDAO) CreateFirewallPolicy(tx *dbs.Tx, userId int64
if userId <= 0 && serverGroupId <= 0 && serverId <= 0 { if userId <= 0 && serverGroupId <= 0 && serverId <= 0 {
// synFlood // synFlood
var synFloodConfig = firewallconfigs.DefaultSYNFloodConfig() var synFloodConfig = firewallconfigs.NewSYNFloodConfig()
synFloodJSON, err := json.Marshal(synFloodConfig) synFloodJSON, err := json.Marshal(synFloodConfig)
if err != nil { if err != nil {
return 0, err return 0, err
@@ -142,20 +142,36 @@ func (this *HTTPFirewallPolicyDAO) CreateFirewallPolicy(tx *dbs.Tx, userId int64
op.SynFlood = synFloodJSON op.SynFlood = synFloodJSON
// block options // block options
var blockOptions = firewallconfigs.DefaultHTTPFirewallBlockAction() var blockOptions = firewallconfigs.NewHTTPFirewallBlockAction()
blockOptionsJSON, err := json.Marshal(blockOptions) blockOptionsJSON, err := json.Marshal(blockOptions)
if err != nil { if err != nil {
return 0, err return 0, err
} }
op.BlockOptions = blockOptionsJSON op.BlockOptions = blockOptionsJSON
// page options
var pageOptions = firewallconfigs.NewHTTPFirewallPageAction()
pageOptionsJSON, err := json.Marshal(pageOptions)
if err != nil {
return 0, err
}
op.PageOptions = pageOptionsJSON
// captcha options // captcha options
var captchaOptions = firewallconfigs.DefaultHTTPFirewallCaptchaAction() var captchaOptions = firewallconfigs.NewHTTPFirewallCaptchaAction()
captchaOptionsJSON, err := json.Marshal(captchaOptions) captchaOptionsJSON, err := json.Marshal(captchaOptions)
if err != nil { if err != nil {
return 0, err return 0, err
} }
op.CaptchaOptions = captchaOptionsJSON op.CaptchaOptions = captchaOptionsJSON
// jscookie options
var jsCookieOptions = firewallconfigs.NewHTTPFirewallJavascriptCookieAction()
jsCookieOptionsJSON, err := json.Marshal(jsCookieOptions)
if err != nil {
return 0, err
}
op.JsCookieOptions = jsCookieOptionsJSON
} }
err := this.Save(tx, op) err := this.Save(tx, op)
@@ -172,16 +188,18 @@ func (this *HTTPFirewallPolicyDAO) CreateDefaultFirewallPolicy(tx *dbs.Tx, name
// 初始化 // 初始化
var groupCodes = []string{} var groupCodes = []string{}
templatePolicy := firewallconfigs.HTTPFirewallTemplate() var templatePolicy = firewallconfigs.HTTPFirewallTemplate()
for _, group := range templatePolicy.AllRuleGroups() { for _, group := range templatePolicy.AllRuleGroups() {
groupCodes = append(groupCodes, group.Code) if group.IsOn {
groupCodes = append(groupCodes, group.Code)
}
} }
var inboundConfig = &firewallconfigs.HTTPFirewallInboundConfig{IsOn: true} var inboundConfig = &firewallconfigs.HTTPFirewallInboundConfig{IsOn: true}
var outboundConfig = &firewallconfigs.HTTPFirewallOutboundConfig{IsOn: true} var outboundConfig = &firewallconfigs.HTTPFirewallOutboundConfig{IsOn: true}
if templatePolicy.Inbound != nil { if templatePolicy.Inbound != nil {
for _, group := range templatePolicy.Inbound.Groups { for _, group := range templatePolicy.Inbound.Groups {
isOn := lists.ContainsString(groupCodes, group.Code) var isOn = lists.ContainsString(groupCodes, group.Code)
group.IsOn = isOn group.IsOn = isOn
groupId, err := SharedHTTPFirewallRuleGroupDAO.CreateGroupFromConfig(tx, group) groupId, err := SharedHTTPFirewallRuleGroupDAO.CreateGroupFromConfig(tx, group)
@@ -196,7 +214,7 @@ func (this *HTTPFirewallPolicyDAO) CreateDefaultFirewallPolicy(tx *dbs.Tx, name
} }
if templatePolicy.Outbound != nil { if templatePolicy.Outbound != nil {
for _, group := range templatePolicy.Outbound.Groups { for _, group := range templatePolicy.Outbound.Groups {
isOn := lists.ContainsString(groupCodes, group.Code) var isOn = lists.ContainsString(groupCodes, group.Code)
group.IsOn = isOn group.IsOn = isOn
groupId, err := SharedHTTPFirewallRuleGroupDAO.CreateGroupFromConfig(tx, group) groupId, err := SharedHTTPFirewallRuleGroupDAO.CreateGroupFromConfig(tx, group)
@@ -277,6 +295,31 @@ func (this *HTTPFirewallPolicyDAO) UpdateFirewallPolicyInbound(tx *dbs.Tx, polic
return this.NotifyUpdate(tx, policyId) return this.NotifyUpdate(tx, policyId)
} }
// UpdateFirewallPolicyInboundRegion 修改入站封禁区域设置
func (this *HTTPFirewallPolicyDAO) UpdateFirewallPolicyInboundRegion(tx *dbs.Tx, policyId int64, regionConfig *firewallconfigs.HTTPFirewallRegionConfig) error {
var inboundConfig = &firewallconfigs.HTTPFirewallInboundConfig{IsOn: true}
inboundJSON, err := this.Query(tx).
Pk(policyId).
Result("inbound").
FindJSONCol()
if err != nil {
return err
}
if IsNotNull(inboundJSON) {
err = json.Unmarshal(inboundJSON, inboundConfig)
if err != nil {
return err
}
}
inboundConfig.Region = regionConfig
newInboundJSON, err := json.Marshal(inboundConfig)
if err != nil {
return err
}
return this.UpdateFirewallPolicyInbound(tx, policyId, newInboundJSON)
}
// UpdateFirewallPolicy 修改策略 // UpdateFirewallPolicy 修改策略
func (this *HTTPFirewallPolicyDAO) UpdateFirewallPolicy(tx *dbs.Tx, func (this *HTTPFirewallPolicyDAO) UpdateFirewallPolicy(tx *dbs.Tx,
policyId int64, policyId int64,
@@ -286,11 +329,16 @@ func (this *HTTPFirewallPolicyDAO) UpdateFirewallPolicy(tx *dbs.Tx,
inboundJSON []byte, inboundJSON []byte,
outboundJSON []byte, outboundJSON []byte,
blockOptionsJSON []byte, blockOptionsJSON []byte,
pageOptionsJSON []byte,
captchaOptionsJSON []byte, captchaOptionsJSON []byte,
jsCookieOptionsJSON []byte,
mode firewallconfigs.FirewallMode, mode firewallconfigs.FirewallMode,
useLocalFirewall bool, useLocalFirewall bool,
synFloodConfig *firewallconfigs.SYNFloodConfig, synFloodConfig *firewallconfigs.SYNFloodConfig,
logConfig *firewallconfigs.HTTPFirewallPolicyLogConfig) error { logConfig *firewallconfigs.HTTPFirewallPolicyLogConfig,
maxRequestBodySize int64,
denyCountryHTML string,
denyProvinceHTML string) error {
if policyId <= 0 { if policyId <= 0 {
return errors.New("invalid policyId") return errors.New("invalid policyId")
} }
@@ -313,9 +361,15 @@ func (this *HTTPFirewallPolicyDAO) UpdateFirewallPolicy(tx *dbs.Tx,
if IsNotNull(blockOptionsJSON) { if IsNotNull(blockOptionsJSON) {
op.BlockOptions = blockOptionsJSON op.BlockOptions = blockOptionsJSON
} }
if IsNotNull(pageOptionsJSON) {
op.PageOptions = pageOptionsJSON
}
if IsNotNull(captchaOptionsJSON) { if IsNotNull(captchaOptionsJSON) {
op.CaptchaOptions = captchaOptionsJSON op.CaptchaOptions = captchaOptionsJSON
} }
if IsNotNull(jsCookieOptionsJSON) {
op.JsCookieOptions = jsCookieOptionsJSON
}
if synFloodConfig != nil { if synFloodConfig != nil {
synFloodConfigJSON, err := json.Marshal(synFloodConfig) synFloodConfigJSON, err := json.Marshal(synFloodConfig)
@@ -338,6 +392,10 @@ func (this *HTTPFirewallPolicyDAO) UpdateFirewallPolicy(tx *dbs.Tx,
} }
op.UseLocalFirewall = useLocalFirewall op.UseLocalFirewall = useLocalFirewall
op.MaxRequestBodySize = maxRequestBodySize
op.DenyCountryHTML = denyCountryHTML
op.DenyProvinceHTML = denyProvinceHTML
err := this.Save(tx, op) err := this.Save(tx, op)
if err != nil { if err != nil {
return err return err
@@ -390,7 +448,7 @@ func (this *HTTPFirewallPolicyDAO) ListEnabledFirewallPolicies(tx *dbs.Tx, clust
} }
// ComposeFirewallPolicy 组合策略配置 // ComposeFirewallPolicy 组合策略配置
func (this *HTTPFirewallPolicyDAO) ComposeFirewallPolicy(tx *dbs.Tx, policyId int64, cacheMap *utils.CacheMap) (*firewallconfigs.HTTPFirewallPolicy, error) { func (this *HTTPFirewallPolicyDAO) ComposeFirewallPolicy(tx *dbs.Tx, policyId int64, forNode bool, cacheMap *utils.CacheMap) (*firewallconfigs.HTTPFirewallPolicy, error) {
if cacheMap == nil { if cacheMap == nil {
cacheMap = utils.NewCacheMap() cacheMap = utils.NewCacheMap()
} }
@@ -410,10 +468,14 @@ func (this *HTTPFirewallPolicyDAO) ComposeFirewallPolicy(tx *dbs.Tx, policyId in
var config = &firewallconfigs.HTTPFirewallPolicy{} var config = &firewallconfigs.HTTPFirewallPolicy{}
config.Id = int64(policy.Id) config.Id = int64(policy.Id)
config.ServerId = int64(policy.ServerId)
config.IsOn = policy.IsOn config.IsOn = policy.IsOn
config.Name = policy.Name config.Name = policy.Name
config.Description = policy.Description config.Description = policy.Description
config.UseLocalFirewall = policy.UseLocalFirewall == 1 config.UseLocalFirewall = policy.UseLocalFirewall == 1
config.MaxRequestBodySize = int64(policy.MaxRequestBodySize)
config.DenyCountryHTML = policy.DenyCountryHTML
config.DenyProvinceHTML = policy.DenyProvinceHTML
if len(policy.Mode) == 0 { if len(policy.Mode) == 0 {
policy.Mode = firewallconfigs.FirewallModeDefend policy.Mode = firewallconfigs.FirewallModeDefend
@@ -421,18 +483,18 @@ func (this *HTTPFirewallPolicyDAO) ComposeFirewallPolicy(tx *dbs.Tx, policyId in
config.Mode = policy.Mode config.Mode = policy.Mode
// Inbound // Inbound
inbound := &firewallconfigs.HTTPFirewallInboundConfig{} var inbound = &firewallconfigs.HTTPFirewallInboundConfig{}
if IsNotNull(policy.Inbound) { if IsNotNull(policy.Inbound) {
err = json.Unmarshal(policy.Inbound, inbound) err = json.Unmarshal(policy.Inbound, inbound)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(inbound.GroupRefs) > 0 { if len(inbound.GroupRefs) > 0 {
resultGroupRefs := []*firewallconfigs.HTTPFirewallRuleGroupRef{} var resultGroupRefs = []*firewallconfigs.HTTPFirewallRuleGroupRef{}
resultGroups := []*firewallconfigs.HTTPFirewallRuleGroup{} var resultGroups = []*firewallconfigs.HTTPFirewallRuleGroup{}
for _, groupRef := range inbound.GroupRefs { for _, groupRef := range inbound.GroupRefs {
groupConfig, err := SharedHTTPFirewallRuleGroupDAO.ComposeFirewallRuleGroup(tx, groupRef.GroupId) groupConfig, err := SharedHTTPFirewallRuleGroupDAO.ComposeFirewallRuleGroup(tx, groupRef.GroupId, forNode)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -449,18 +511,18 @@ func (this *HTTPFirewallPolicyDAO) ComposeFirewallPolicy(tx *dbs.Tx, policyId in
config.Inbound = inbound config.Inbound = inbound
// Outbound // Outbound
outbound := &firewallconfigs.HTTPFirewallOutboundConfig{} var outbound = &firewallconfigs.HTTPFirewallOutboundConfig{}
if IsNotNull(policy.Outbound) { if IsNotNull(policy.Outbound) {
err = json.Unmarshal(policy.Outbound, outbound) err = json.Unmarshal(policy.Outbound, outbound)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(outbound.GroupRefs) > 0 { if len(outbound.GroupRefs) > 0 {
resultGroupRefs := []*firewallconfigs.HTTPFirewallRuleGroupRef{} var resultGroupRefs = []*firewallconfigs.HTTPFirewallRuleGroupRef{}
resultGroups := []*firewallconfigs.HTTPFirewallRuleGroup{} var resultGroups = []*firewallconfigs.HTTPFirewallRuleGroup{}
for _, groupRef := range outbound.GroupRefs { for _, groupRef := range outbound.GroupRefs {
groupConfig, err := SharedHTTPFirewallRuleGroupDAO.ComposeFirewallRuleGroup(tx, groupRef.GroupId) groupConfig, err := SharedHTTPFirewallRuleGroupDAO.ComposeFirewallRuleGroup(tx, groupRef.GroupId, forNode)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -478,7 +540,7 @@ func (this *HTTPFirewallPolicyDAO) ComposeFirewallPolicy(tx *dbs.Tx, policyId in
// Block动作配置 // Block动作配置
if IsNotNull(policy.BlockOptions) { if IsNotNull(policy.BlockOptions) {
var blockAction = &firewallconfigs.HTTPFirewallBlockAction{} var blockAction = firewallconfigs.NewHTTPFirewallBlockAction()
err = json.Unmarshal(policy.BlockOptions, blockAction) err = json.Unmarshal(policy.BlockOptions, blockAction)
if err != nil { if err != nil {
return config, err return config, err
@@ -486,9 +548,19 @@ func (this *HTTPFirewallPolicyDAO) ComposeFirewallPolicy(tx *dbs.Tx, policyId in
config.BlockOptions = blockAction config.BlockOptions = blockAction
} }
// Page动作配置
if IsNotNull(policy.PageOptions) {
var pageAction = firewallconfigs.NewHTTPFirewallPageAction()
err = json.Unmarshal(policy.PageOptions, pageAction)
if err != nil {
return config, err
}
config.PageOptions = pageAction
}
// Captcha动作配置 // Captcha动作配置
if IsNotNull(policy.CaptchaOptions) { if IsNotNull(policy.CaptchaOptions) {
var captchaAction = &firewallconfigs.HTTPFirewallCaptchaAction{} var captchaAction = firewallconfigs.NewHTTPFirewallCaptchaAction()
err = json.Unmarshal(policy.CaptchaOptions, captchaAction) err = json.Unmarshal(policy.CaptchaOptions, captchaAction)
if err != nil { if err != nil {
return config, err return config, err
@@ -496,6 +568,16 @@ func (this *HTTPFirewallPolicyDAO) ComposeFirewallPolicy(tx *dbs.Tx, policyId in
config.CaptchaOptions = captchaAction config.CaptchaOptions = captchaAction
} }
// JSCookie动作配置
if IsNotNull(policy.JsCookieOptions) {
var jsCookieAction = firewallconfigs.NewHTTPFirewallJavascriptCookieAction()
err = json.Unmarshal(policy.JsCookieOptions, jsCookieAction)
if err != nil {
return config, err
}
config.JSCookieOptions = jsCookieAction
}
// syn flood // syn flood
if IsNotNull(policy.SynFlood) { if IsNotNull(policy.SynFlood) {
var synFloodConfig = &firewallconfigs.SYNFloodConfig{} var synFloodConfig = &firewallconfigs.SYNFloodConfig{}
@@ -630,6 +712,19 @@ func (this *HTTPFirewallPolicyDAO) FindFirewallPolicyIdsWithServerId(tx *dbs.Tx,
return result, nil return result, nil
} }
// FindServerIdWithFirewallPolicyId 根据策略查找网站ID
func (this *HTTPFirewallPolicyDAO) FindServerIdWithFirewallPolicyId(tx *dbs.Tx, policyId int64) (serverId int64, err error) {
if policyId <= 0 {
return
}
serverId, err = this.Query(tx).
Pk(policyId).
Result("serverId").
FindInt64Col(0)
return
}
// NotifyUpdate 通知更新 // NotifyUpdate 通知更新
func (this *HTTPFirewallPolicyDAO) NotifyUpdate(tx *dbs.Tx, policyId int64) error { func (this *HTTPFirewallPolicyDAO) NotifyUpdate(tx *dbs.Tx, policyId int64) error {
webIds, err := SharedHTTPWebDAO.FindAllWebIdsWithHTTPFirewallPolicyId(tx, policyId) webIds, err := SharedHTTPWebDAO.FindAllWebIdsWithHTTPFirewallPolicyId(tx, policyId)

View File

@@ -2,49 +2,86 @@ package models
import "github.com/iwind/TeaGo/dbs" import "github.com/iwind/TeaGo/dbs"
const (
HTTPFirewallPolicyField_Id dbs.FieldName = "id" // ID
HTTPFirewallPolicyField_TemplateId dbs.FieldName = "templateId" // 模版ID
HTTPFirewallPolicyField_AdminId dbs.FieldName = "adminId" // 管理员ID
HTTPFirewallPolicyField_UserId dbs.FieldName = "userId" // 用户ID
HTTPFirewallPolicyField_ServerId dbs.FieldName = "serverId" // 服务ID
HTTPFirewallPolicyField_GroupId dbs.FieldName = "groupId" // 服务分组ID
HTTPFirewallPolicyField_State dbs.FieldName = "state" // 状态
HTTPFirewallPolicyField_CreatedAt dbs.FieldName = "createdAt" // 创建时间
HTTPFirewallPolicyField_IsOn dbs.FieldName = "isOn" // 是否启用
HTTPFirewallPolicyField_Name dbs.FieldName = "name" // 名称
HTTPFirewallPolicyField_Description dbs.FieldName = "description" // 描述
HTTPFirewallPolicyField_Inbound dbs.FieldName = "inbound" // 入站规则
HTTPFirewallPolicyField_Outbound dbs.FieldName = "outbound" // 出站规则
HTTPFirewallPolicyField_BlockOptions dbs.FieldName = "blockOptions" // BLOCK动作选项
HTTPFirewallPolicyField_PageOptions dbs.FieldName = "pageOptions" // PAGE动作选项
HTTPFirewallPolicyField_CaptchaOptions dbs.FieldName = "captchaOptions" // 验证码动作选项
HTTPFirewallPolicyField_JsCookieOptions dbs.FieldName = "jsCookieOptions" // JSCookie动作选项
HTTPFirewallPolicyField_Mode dbs.FieldName = "mode" // 模式
HTTPFirewallPolicyField_UseLocalFirewall dbs.FieldName = "useLocalFirewall" // 是否自动使用本地防火墙
HTTPFirewallPolicyField_SynFlood dbs.FieldName = "synFlood" // SynFlood防御设置
HTTPFirewallPolicyField_Log dbs.FieldName = "log" // 日志配置
HTTPFirewallPolicyField_MaxRequestBodySize dbs.FieldName = "maxRequestBodySize" // 可以检查的最大请求内容尺寸
HTTPFirewallPolicyField_DenyCountryHTML dbs.FieldName = "denyCountryHTML" // 区域封禁提示
HTTPFirewallPolicyField_DenyProvinceHTML dbs.FieldName = "denyProvinceHTML" // 省份封禁提示
)
// HTTPFirewallPolicy HTTP防火墙 // HTTPFirewallPolicy HTTP防火墙
type HTTPFirewallPolicy struct { type HTTPFirewallPolicy struct {
Id uint32 `field:"id"` // ID Id uint32 `field:"id"` // ID
TemplateId uint32 `field:"templateId"` // 模版ID TemplateId uint32 `field:"templateId"` // 模版ID
AdminId uint32 `field:"adminId"` // 管理员ID AdminId uint32 `field:"adminId"` // 管理员ID
UserId uint32 `field:"userId"` // 用户ID UserId uint32 `field:"userId"` // 用户ID
ServerId uint32 `field:"serverId"` // 服务ID ServerId uint32 `field:"serverId"` // 服务ID
GroupId uint32 `field:"groupId"` // 服务分组ID GroupId uint32 `field:"groupId"` // 服务分组ID
State uint8 `field:"state"` // 状态 State uint8 `field:"state"` // 状态
CreatedAt uint64 `field:"createdAt"` // 创建时间 CreatedAt uint64 `field:"createdAt"` // 创建时间
IsOn bool `field:"isOn"` // 是否启用 IsOn bool `field:"isOn"` // 是否启用
Name string `field:"name"` // 名称 Name string `field:"name"` // 名称
Description string `field:"description"` // 描述 Description string `field:"description"` // 描述
Inbound dbs.JSON `field:"inbound"` // 入站规则 Inbound dbs.JSON `field:"inbound"` // 入站规则
Outbound dbs.JSON `field:"outbound"` // 出站规则 Outbound dbs.JSON `field:"outbound"` // 出站规则
BlockOptions dbs.JSON `field:"blockOptions"` // BLOCK选项 BlockOptions dbs.JSON `field:"blockOptions"` // BLOCK动作选项
CaptchaOptions dbs.JSON `field:"captchaOptions"` // 验证码选项 PageOptions dbs.JSON `field:"pageOptions"` // PAGE动作选项
Mode string `field:"mode"` // 模式 CaptchaOptions dbs.JSON `field:"captchaOptions"` // 验证码动作选项
UseLocalFirewall uint8 `field:"useLocalFirewall"` // 是否自动使用本地防火墙 JsCookieOptions dbs.JSON `field:"jsCookieOptions"` // JSCookie动作选项
SynFlood dbs.JSON `field:"synFlood"` // SynFlood防御设置 Mode string `field:"mode"` // 模式
Log dbs.JSON `field:"log"` // 日志配置 UseLocalFirewall uint8 `field:"useLocalFirewall"` // 是否自动使用本地防火墙
SynFlood dbs.JSON `field:"synFlood"` // SynFlood防御设置
Log dbs.JSON `field:"log"` // 日志配置
MaxRequestBodySize uint32 `field:"maxRequestBodySize"` // 可以检查的最大请求内容尺寸
DenyCountryHTML string `field:"denyCountryHTML"` // 区域封禁提示
DenyProvinceHTML string `field:"denyProvinceHTML"` // 省份封禁提示
} }
type HTTPFirewallPolicyOperator struct { type HTTPFirewallPolicyOperator struct {
Id interface{} // ID Id any // ID
TemplateId interface{} // 模版ID TemplateId any // 模版ID
AdminId interface{} // 管理员ID AdminId any // 管理员ID
UserId interface{} // 用户ID UserId any // 用户ID
ServerId interface{} // 服务ID ServerId any // 服务ID
GroupId interface{} // 服务分组ID GroupId any // 服务分组ID
State interface{} // 状态 State any // 状态
CreatedAt interface{} // 创建时间 CreatedAt any // 创建时间
IsOn interface{} // 是否启用 IsOn any // 是否启用
Name interface{} // 名称 Name any // 名称
Description interface{} // 描述 Description any // 描述
Inbound interface{} // 入站规则 Inbound any // 入站规则
Outbound interface{} // 出站规则 Outbound any // 出站规则
BlockOptions interface{} // BLOCK选项 BlockOptions any // BLOCK动作选项
CaptchaOptions interface{} // 验证码选项 PageOptions any // PAGE动作选项
Mode interface{} // 模式 CaptchaOptions any // 验证码动作选项
UseLocalFirewall interface{} // 是否自动使用本地防火墙 JsCookieOptions any // JSCookie动作选项
SynFlood interface{} // SynFlood防御设置 Mode any // 模式
Log interface{} // 日志配置 UseLocalFirewall any // 是否自动使用本地防火墙
SynFlood any // SynFlood防御设置
Log any // 日志配置
MaxRequestBodySize any // 可以检查的最大请求内容尺寸
DenyCountryHTML any // 区域封禁提示
DenyProvinceHTML any // 省份封禁提示
} }
func NewHTTPFirewallPolicyOperator() *HTTPFirewallPolicyOperator { func NewHTTPFirewallPolicyOperator() *HTTPFirewallPolicyOperator {

View File

@@ -81,7 +81,7 @@ func (this *HTTPFirewallRuleGroupDAO) FindHTTPFirewallRuleGroupName(tx *dbs.Tx,
} }
// ComposeFirewallRuleGroup 组合配置 // ComposeFirewallRuleGroup 组合配置
func (this *HTTPFirewallRuleGroupDAO) ComposeFirewallRuleGroup(tx *dbs.Tx, groupId int64) (*firewallconfigs.HTTPFirewallRuleGroup, error) { func (this *HTTPFirewallRuleGroupDAO) ComposeFirewallRuleGroup(tx *dbs.Tx, groupId int64, forNode bool) (*firewallconfigs.HTTPFirewallRuleGroup, error) {
group, err := this.FindEnabledHTTPFirewallRuleGroup(tx, groupId) group, err := this.FindEnabledHTTPFirewallRuleGroup(tx, groupId)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -89,7 +89,7 @@ func (this *HTTPFirewallRuleGroupDAO) ComposeFirewallRuleGroup(tx *dbs.Tx, group
if group == nil { if group == nil {
return nil, nil return nil, nil
} }
config := &firewallconfigs.HTTPFirewallRuleGroup{} var config = &firewallconfigs.HTTPFirewallRuleGroup{}
config.Id = int64(group.Id) config.Id = int64(group.Id)
config.IsOn = group.IsOn config.IsOn = group.IsOn
config.Name = group.Name config.Name = group.Name
@@ -98,17 +98,17 @@ func (this *HTTPFirewallRuleGroupDAO) ComposeFirewallRuleGroup(tx *dbs.Tx, group
config.IsTemplate = group.IsTemplate config.IsTemplate = group.IsTemplate
if IsNotNull(group.Sets) { if IsNotNull(group.Sets) {
setRefs := []*firewallconfigs.HTTPFirewallRuleSetRef{} var setRefs = []*firewallconfigs.HTTPFirewallRuleSetRef{}
err = json.Unmarshal(group.Sets, &setRefs) err = json.Unmarshal(group.Sets, &setRefs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, setRef := range setRefs { for _, setRef := range setRefs {
setConfig, err := SharedHTTPFirewallRuleSetDAO.ComposeFirewallRuleSet(tx, setRef.SetId) setConfig, err := SharedHTTPFirewallRuleSetDAO.ComposeFirewallRuleSet(tx, setRef.SetId, forNode)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if setConfig != nil { if setConfig != nil && (!forNode || setConfig.IsOn) {
config.SetRefs = append(config.SetRefs, setRef) config.SetRefs = append(config.SetRefs, setRef)
config.Sets = append(config.Sets, setConfig) config.Sets = append(config.Sets, setConfig)
} }

View File

@@ -84,7 +84,7 @@ func (this *HTTPFirewallRuleSetDAO) FindHTTPFirewallRuleSetName(tx *dbs.Tx, id i
} }
// ComposeFirewallRuleSet 组合配置 // ComposeFirewallRuleSet 组合配置
func (this *HTTPFirewallRuleSetDAO) ComposeFirewallRuleSet(tx *dbs.Tx, setId int64) (*firewallconfigs.HTTPFirewallRuleSet, error) { func (this *HTTPFirewallRuleSetDAO) ComposeFirewallRuleSet(tx *dbs.Tx, setId int64, forNode bool) (*firewallconfigs.HTTPFirewallRuleSet, error) {
set, err := this.FindEnabledHTTPFirewallRuleSet(tx, setId) set, err := this.FindEnabledHTTPFirewallRuleSet(tx, setId)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -92,7 +92,7 @@ func (this *HTTPFirewallRuleSetDAO) ComposeFirewallRuleSet(tx *dbs.Tx, setId int
if set == nil { if set == nil {
return nil, nil return nil, nil
} }
config := &firewallconfigs.HTTPFirewallRuleSet{} var config = &firewallconfigs.HTTPFirewallRuleSet{}
config.Id = int64(set.Id) config.Id = int64(set.Id)
config.IsOn = set.IsOn config.IsOn = set.IsOn
config.Name = set.Name config.Name = set.Name
@@ -102,7 +102,7 @@ func (this *HTTPFirewallRuleSetDAO) ComposeFirewallRuleSet(tx *dbs.Tx, setId int
config.IgnoreLocal = set.IgnoreLocal == 1 config.IgnoreLocal = set.IgnoreLocal == 1
if IsNotNull(set.Rules) { if IsNotNull(set.Rules) {
ruleRefs := []*firewallconfigs.HTTPFirewallRuleRef{} var ruleRefs = []*firewallconfigs.HTTPFirewallRuleRef{}
err = json.Unmarshal(set.Rules, &ruleRefs) err = json.Unmarshal(set.Rules, &ruleRefs)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -128,6 +128,29 @@ func (this *HTTPFirewallRuleSetDAO) ComposeFirewallRuleSet(tx *dbs.Tx, setId int
config.Actions = actionConfigs config.Actions = actionConfigs
} }
// 检查各个选项
for _, actionConfig := range actionConfigs {
if actionConfig.Code == firewallconfigs.HTTPFirewallActionRecordIP { // 记录IP动作
if actionConfig.Options != nil {
var ipListId = actionConfig.Options.GetInt64("ipListId")
if ipListId <= 0 { // default list id
if forNode {
actionConfig.Options["ipListId"] = firewallconfigs.GlobalListId
}
actionConfig.Options["ipListIsDeleted"] = false
} else {
exists, err := SharedIPListDAO.ExistsEnabledIPList(tx, ipListId)
if err != nil {
return nil, err
}
if !exists {
actionConfig.Options["ipListIsDeleted"] = true
}
}
}
}
}
return config, nil return config, nil
} }
@@ -212,6 +235,28 @@ func (this *HTTPFirewallRuleSetDAO) FindEnabledRuleSetIdWithRuleId(tx *dbs.Tx, r
FindInt64Col(0) FindInt64Col(0)
} }
// FindAllEnabledRuleSetIdsWithIPListId 根据IP名单ID查找对应动作的WAF规则集
func (this *HTTPFirewallRuleSetDAO) FindAllEnabledRuleSetIdsWithIPListId(tx *dbs.Tx, ipListId int64) (setIds []int64, err error) {
ones, err := this.Query(tx).
State(HTTPFirewallRuleStateEnabled).
Where("JSON_CONTAINS(actions, :jsonQuery)").
Param("jsonQuery", maps.Map{
"code": firewallconfigs.HTTPFirewallActionRecordIP,
"options": maps.Map{
"ipListId": ipListId,
},
}.AsJSON()).
ResultPk().
FindAll()
if err != nil {
return nil, err
}
for _, one := range ones {
setIds = append(setIds, int64(one.(*HTTPFirewallRuleSet).Id))
}
return
}
// CheckUserRuleSet 检查用户 // CheckUserRuleSet 检查用户
func (this *HTTPFirewallRuleSetDAO) CheckUserRuleSet(tx *dbs.Tx, userId int64, setId int64) error { func (this *HTTPFirewallRuleSetDAO) CheckUserRuleSet(tx *dbs.Tx, userId int64, setId int64) error {
groupId, err := SharedHTTPFirewallRuleGroupDAO.FindRuleGroupIdWithRuleSetId(tx, setId) groupId, err := SharedHTTPFirewallRuleGroupDAO.FindRuleGroupIdWithRuleSetId(tx, setId)

View File

@@ -77,7 +77,7 @@ func (this *HTTPPageDAO) FindEnabledHTTPPage(tx *dbs.Tx, id int64) (*HTTPPage, e
} }
// CreatePage 创建Page // CreatePage 创建Page
func (this *HTTPPageDAO) CreatePage(tx *dbs.Tx, userId int64, statusList []string, bodyType shared.BodyType, url string, body string, newStatus int) (pageId int64, err error) { func (this *HTTPPageDAO) CreatePage(tx *dbs.Tx, userId int64, statusList []string, bodyType serverconfigs.HTTPPageBodyType, url string, body string, newStatus int, exceptURLPatterns []*shared.URLPattern, onlyURLPatterns []*shared.URLPattern) (pageId int64, err error) {
var op = NewHTTPPageOperator() var op = NewHTTPPageOperator()
op.UserId = userId op.UserId = userId
op.IsOn = true op.IsOn = true
@@ -94,6 +94,29 @@ func (this *HTTPPageDAO) CreatePage(tx *dbs.Tx, userId int64, statusList []strin
op.Url = url op.Url = url
op.Body = body op.Body = body
op.NewStatus = newStatus op.NewStatus = newStatus
{
if exceptURLPatterns == nil {
exceptURLPatterns = []*shared.URLPattern{}
}
exceptURLPatternsJSON, err := json.Marshal(exceptURLPatterns)
if err != nil {
return 0, err
}
op.ExceptURLPatterns = exceptURLPatternsJSON
}
{
if onlyURLPatterns == nil {
onlyURLPatterns = []*shared.URLPattern{}
}
onlyURLPatternsJSON, err := json.Marshal(onlyURLPatterns)
if err != nil {
return 0, err
}
op.OnlyURLPatterns = onlyURLPatternsJSON
}
err = this.Save(tx, op) err = this.Save(tx, op)
if err != nil { if err != nil {
return 0, err return 0, err
@@ -103,7 +126,7 @@ func (this *HTTPPageDAO) CreatePage(tx *dbs.Tx, userId int64, statusList []strin
} }
// UpdatePage 修改Page // UpdatePage 修改Page
func (this *HTTPPageDAO) UpdatePage(tx *dbs.Tx, pageId int64, statusList []string, bodyType shared.BodyType, url string, body string, newStatus int) error { func (this *HTTPPageDAO) UpdatePage(tx *dbs.Tx, pageId int64, statusList []string, bodyType serverconfigs.HTTPPageBodyType, url string, body string, newStatus int, exceptURLPatterns []*shared.URLPattern, onlyURLPatterns []*shared.URLPattern) error {
if pageId <= 0 { if pageId <= 0 {
return errors.New("invalid pageId") return errors.New("invalid pageId")
} }
@@ -126,6 +149,29 @@ func (this *HTTPPageDAO) UpdatePage(tx *dbs.Tx, pageId int64, statusList []strin
op.Url = url op.Url = url
op.Body = body op.Body = body
op.NewStatus = newStatus op.NewStatus = newStatus
{
if exceptURLPatterns == nil {
exceptURLPatterns = []*shared.URLPattern{}
}
exceptURLPatternsJSON, err := json.Marshal(exceptURLPatterns)
if err != nil {
return err
}
op.ExceptURLPatterns = exceptURLPatternsJSON
}
{
if onlyURLPatterns == nil {
onlyURLPatterns = []*shared.URLPattern{}
}
onlyURLPatternsJSON, err := json.Marshal(onlyURLPatterns)
if err != nil {
return err
}
op.OnlyURLPatterns = onlyURLPatternsJSON
}
err = this.Save(tx, op) err = this.Save(tx, op)
if err != nil { if err != nil {
return err return err
@@ -156,6 +202,14 @@ func (this *HTTPPageDAO) ClonePage(tx *dbs.Tx, fromPageId int64) (newPageId int6
op.Body = page.Body op.Body = page.Body
op.BodyType = page.BodyType op.BodyType = page.BodyType
op.State = page.State op.State = page.State
if len(page.ExceptURLPatterns) > 0 {
op.ExceptURLPatterns = page.ExceptURLPatterns
}
if len(page.OnlyURLPatterns) > 0 {
op.OnlyURLPatterns = page.OnlyURLPatterns
}
return this.SaveInt64(tx, op) return this.SaveInt64(tx, op)
} }
@@ -179,7 +233,7 @@ func (this *HTTPPageDAO) ComposePageConfig(tx *dbs.Tx, pageId int64, cacheMap *u
return nil, nil return nil, nil
} }
config := &serverconfigs.HTTPPageConfig{} var config = &serverconfigs.HTTPPageConfig{}
config.Id = int64(page.Id) config.Id = int64(page.Id)
config.IsOn = page.IsOn config.IsOn = page.IsOn
config.NewStatus = int(page.NewStatus) config.NewStatus = int(page.NewStatus)
@@ -188,7 +242,7 @@ func (this *HTTPPageDAO) ComposePageConfig(tx *dbs.Tx, pageId int64, cacheMap *u
config.BodyType = page.BodyType config.BodyType = page.BodyType
if len(page.BodyType) == 0 { if len(page.BodyType) == 0 {
page.BodyType = shared.BodyTypeURL page.BodyType = serverconfigs.HTTPPageBodyTypeURL
} }
if len(page.StatusList) > 0 { if len(page.StatusList) > 0 {
@@ -202,6 +256,28 @@ func (this *HTTPPageDAO) ComposePageConfig(tx *dbs.Tx, pageId int64, cacheMap *u
} }
} }
if len(page.ExceptURLPatterns) > 0 {
var exceptURLPatterns = []*shared.URLPattern{}
err = json.Unmarshal(page.ExceptURLPatterns, &exceptURLPatterns)
if err != nil {
return nil, err
}
if len(exceptURLPatterns) > 0 {
config.ExceptURLPatterns = exceptURLPatterns
}
}
if len(page.OnlyURLPatterns) > 0 {
var onlyURLPatterns = []*shared.URLPattern{}
err = json.Unmarshal(page.OnlyURLPatterns, &onlyURLPatterns)
if err != nil {
return nil, err
}
if len(onlyURLPatterns) > 0 {
config.OnlyURLPatterns = onlyURLPatterns
}
}
if cacheMap != nil { if cacheMap != nil {
cacheMap.Put(cacheKey, config) cacheMap.Put(cacheKey, config)
} }

View File

@@ -2,33 +2,53 @@ package models
import "github.com/iwind/TeaGo/dbs" import "github.com/iwind/TeaGo/dbs"
const (
HTTPPageField_Id dbs.FieldName = "id" // ID
HTTPPageField_AdminId dbs.FieldName = "adminId" // 管理员ID
HTTPPageField_UserId dbs.FieldName = "userId" // 用户ID
HTTPPageField_IsOn dbs.FieldName = "isOn" // 是否启用
HTTPPageField_StatusList dbs.FieldName = "statusList" // 状态列表
HTTPPageField_Url dbs.FieldName = "url" // 页面URL
HTTPPageField_NewStatus dbs.FieldName = "newStatus" // 新状态码
HTTPPageField_State dbs.FieldName = "state" // 状态
HTTPPageField_CreatedAt dbs.FieldName = "createdAt" // 创建时间
HTTPPageField_Body dbs.FieldName = "body" // 页面内容
HTTPPageField_BodyType dbs.FieldName = "bodyType" // 内容类型
HTTPPageField_ExceptURLPatterns dbs.FieldName = "exceptURLPatterns" // 例外URL
HTTPPageField_OnlyURLPatterns dbs.FieldName = "onlyURLPatterns" // 限制URL
)
// HTTPPage 特殊页面 // HTTPPage 特殊页面
type HTTPPage struct { type HTTPPage struct {
Id uint32 `field:"id"` // ID Id uint32 `field:"id"` // ID
AdminId uint32 `field:"adminId"` // 管理员ID AdminId uint32 `field:"adminId"` // 管理员ID
UserId uint32 `field:"userId"` // 用户ID UserId uint32 `field:"userId"` // 用户ID
IsOn bool `field:"isOn"` // 是否启用 IsOn bool `field:"isOn"` // 是否启用
StatusList dbs.JSON `field:"statusList"` // 状态列表 StatusList dbs.JSON `field:"statusList"` // 状态列表
Url string `field:"url"` // 页面URL Url string `field:"url"` // 页面URL
NewStatus int32 `field:"newStatus"` // 新状态码 NewStatus int32 `field:"newStatus"` // 新状态码
State uint8 `field:"state"` // 状态 State uint8 `field:"state"` // 状态
CreatedAt uint64 `field:"createdAt"` // 创建时间 CreatedAt uint64 `field:"createdAt"` // 创建时间
Body string `field:"body"` // 页面内容 Body string `field:"body"` // 页面内容
BodyType string `field:"bodyType"` // 内容类型 BodyType string `field:"bodyType"` // 内容类型
ExceptURLPatterns dbs.JSON `field:"exceptURLPatterns"` // 例外URL
OnlyURLPatterns dbs.JSON `field:"onlyURLPatterns"` // 限制URL
} }
type HTTPPageOperator struct { type HTTPPageOperator struct {
Id interface{} // ID Id any // ID
AdminId interface{} // 管理员ID AdminId any // 管理员ID
UserId interface{} // 用户ID UserId any // 用户ID
IsOn interface{} // 是否启用 IsOn any // 是否启用
StatusList interface{} // 状态列表 StatusList any // 状态列表
Url interface{} // 页面URL Url any // 页面URL
NewStatus interface{} // 新状态码 NewStatus any // 新状态码
State interface{} // 状态 State any // 状态
CreatedAt interface{} // 创建时间 CreatedAt any // 创建时间
Body interface{} // 页面内容 Body any // 页面内容
BodyType interface{} // 内容类型 BodyType any // 内容类型
ExceptURLPatterns any // 例外URL
OnlyURLPatterns any // 限制URL
} }
func NewHTTPPageOperator() *HTTPPageOperator { func NewHTTPPageOperator() *HTTPPageOperator {

View File

@@ -124,8 +124,9 @@ func (this *HTTPRewriteRuleDAO) ComposeRewriteRule(tx *dbs.Tx, rewriteRuleId int
} }
// CreateRewriteRule 创建规则 // CreateRewriteRule 创建规则
func (this *HTTPRewriteRuleDAO) CreateRewriteRule(tx *dbs.Tx, pattern string, replace string, mode string, redirectStatus int, isBreak bool, proxyHost string, withQuery bool, isOn bool, condsJSON []byte) (int64, error) { func (this *HTTPRewriteRuleDAO) CreateRewriteRule(tx *dbs.Tx, userId int64, pattern string, replace string, mode string, redirectStatus int, isBreak bool, proxyHost string, withQuery bool, isOn bool, condsJSON []byte) (int64, error) {
var op = NewHTTPRewriteRuleOperator() var op = NewHTTPRewriteRuleOperator()
op.UserId = userId
op.State = HTTPRewriteRuleStateEnabled op.State = HTTPRewriteRuleStateEnabled
op.IsOn = isOn op.IsOn = isOn
@@ -172,6 +173,34 @@ func (this *HTTPRewriteRuleDAO) UpdateRewriteRule(tx *dbs.Tx, rewriteRuleId int6
return this.NotifyUpdate(tx, rewriteRuleId) return this.NotifyUpdate(tx, rewriteRuleId)
} }
func (this *HTTPRewriteRuleDAO) CheckUserRewriteRule(tx *dbs.Tx, userId int64, rewriteRuleId int64) error {
if rewriteRuleId <= 0 {
return ErrNotFound
}
exists, err := this.Query(tx).
Pk(rewriteRuleId).
Attr("userId", userId).
Exist()
if err != nil {
return err
}
if !exists {
return ErrNotFound
}
webId, err := SharedHTTPWebDAO.FindEnabledWebIdWithRewriteRuleId(tx, rewriteRuleId)
if err != nil {
return err
}
if webId <= 0 {
return ErrNotFound
}
return SharedHTTPWebDAO.CheckUserWeb(tx, userId, webId)
}
// NotifyUpdate 通知更新 // NotifyUpdate 通知更新
func (this *HTTPRewriteRuleDAO) NotifyUpdate(tx *dbs.Tx, rewriteRuleId int64) error { func (this *HTTPRewriteRuleDAO) NotifyUpdate(tx *dbs.Tx, rewriteRuleId int64) error {
webId, err := SharedHTTPWebDAO.FindEnabledWebIdWithRewriteRuleId(tx, rewriteRuleId) webId, err := SharedHTTPWebDAO.FindEnabledWebIdWithRewriteRuleId(tx, rewriteRuleId)

View File

@@ -101,7 +101,7 @@ func (this *HTTPWebDAO) ComposeWebConfig(tx *dbs.Tx, webId int64, isLocationOrGr
// root // root
if IsNotNull(web.Root) { if IsNotNull(web.Root) {
var rootConfig = &serverconfigs.HTTPRootConfig{} var rootConfig = serverconfigs.NewHTTPRootConfig()
err = json.Unmarshal(web.Root, rootConfig) err = json.Unmarshal(web.Root, rootConfig)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -151,6 +151,18 @@ func (this *HTTPWebDAO) ComposeWebConfig(tx *dbs.Tx, webId int64, isLocationOrGr
} }
} }
// Optimization
if IsNotNull(web.Optimization) {
var optimizationConfig = serverconfigs.NewHTTPPageOptimizationConfig()
err = json.Unmarshal(web.Optimization, optimizationConfig)
if err != nil {
return nil, err
}
if this.shouldCompose(isLocationOrGroup, forNode, optimizationConfig.IsPrior, true) {
config.Optimization = optimizationConfig
}
}
// charset // charset
if IsNotNull(web.Charset) { if IsNotNull(web.Charset) {
var charsetConfig = &serverconfigs.HTTPCharsetConfig{} var charsetConfig = &serverconfigs.HTTPCharsetConfig{}
@@ -289,7 +301,7 @@ func (this *HTTPWebDAO) ComposeWebConfig(tx *dbs.Tx, webId int64, isLocationOrGr
// 自定义防火墙设置 // 自定义防火墙设置
if firewallRef.FirewallPolicyId > 0 { if firewallRef.FirewallPolicyId > 0 {
firewallPolicy, err := SharedHTTPFirewallPolicyDAO.ComposeFirewallPolicy(tx, firewallRef.FirewallPolicyId, cacheMap) firewallPolicy, err := SharedHTTPFirewallPolicyDAO.ComposeFirewallPolicy(tx, firewallRef.FirewallPolicyId, forNode, cacheMap)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -507,6 +519,14 @@ func (this *HTTPWebDAO) ComposeWebConfig(tx *dbs.Tx, webId int64, isLocationOrGr
} }
if this.shouldCompose(isLocationOrGroup, forNode, ccConfig.IsPrior, ccConfig.IsOn) { if this.shouldCompose(isLocationOrGroup, forNode, ccConfig.IsPrior, ccConfig.IsOn) {
config.CC = ccConfig config.CC = ccConfig
if forNode {
for index, threshold := range ccConfig.Thresholds {
if index < len(serverconfigs.DefaultHTTPCCThresholds) {
threshold.MergeIfEmpty(serverconfigs.DefaultHTTPCCThresholds[index])
}
}
}
} }
} }
@@ -534,6 +554,18 @@ func (this *HTTPWebDAO) ComposeWebConfig(tx *dbs.Tx, webId int64, isLocationOrGr
} }
} }
// hls
if IsNotNull(web.Hls) {
var hlsConfig = &serverconfigs.HLSConfig{}
err = json.Unmarshal(web.Hls, hlsConfig)
if err != nil {
return nil, err
}
if this.shouldCompose(isLocationOrGroup, forNode, hlsConfig.IsPrior, true) {
config.HLS = hlsConfig
}
}
if cacheMap != nil { if cacheMap != nil {
cacheMap.Put(cacheKey, config) cacheMap.Put(cacheKey, config)
} }
@@ -550,7 +582,21 @@ func (this *HTTPWebDAO) CreateWeb(tx *dbs.Tx, adminId int64, userId int64, rootJ
if len(rootJSON) > 0 { if len(rootJSON) > 0 {
op.Root = JSONBytes(rootJSON) op.Root = JSONBytes(rootJSON)
} }
err := this.Save(tx, op)
// 设置默认的remote-addr
// set default remote-addr config
var remoteAddrConfig = &serverconfigs.HTTPRemoteAddrConfig{
IsOn: true,
Value: "${rawRemoteAddr}",
Type: serverconfigs.HTTPRemoteAddrTypeDefault,
}
remoteAddrConfigJSON, err := json.Marshal(remoteAddrConfig)
if err != nil {
return 0, err
}
op.RemoteAddr = remoteAddrConfigJSON
err = this.Save(tx, op)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -574,14 +620,42 @@ func (this *HTTPWebDAO) UpdateWeb(tx *dbs.Tx, webId int64, rootJSON []byte) erro
} }
// UpdateWebCompression 修改压缩配置 // UpdateWebCompression 修改压缩配置
func (this *HTTPWebDAO) UpdateWebCompression(tx *dbs.Tx, webId int64, compressionConfig []byte) error { func (this *HTTPWebDAO) UpdateWebCompression(tx *dbs.Tx, webId int64, compressionConfig *serverconfigs.HTTPCompressionConfig) error {
if webId <= 0 { if webId <= 0 {
return errors.New("invalid webId") return errors.New("invalid webId")
} }
compressionJSON, err := json.Marshal(compressionConfig)
if err != nil {
return err
}
var op = NewHTTPWebOperator() var op = NewHTTPWebOperator()
op.Id = webId op.Id = webId
op.Compression = JSONBytes(compressionConfig) op.Compression = compressionJSON
err := this.Save(tx, op) err = this.Save(tx, op)
if err != nil {
return err
}
return this.NotifyUpdate(tx, webId)
}
// UpdateWebOptimization 修改页面优化配置
func (this *HTTPWebDAO) UpdateWebOptimization(tx *dbs.Tx, webId int64, optimizationConfig *serverconfigs.HTTPPageOptimizationConfig) error {
if webId <= 0 {
return errors.New("invalid webId")
}
optimizationJSON, err := json.Marshal(optimizationConfig)
if err != nil {
return err
}
var op = NewHTTPWebOperator()
op.Id = webId
op.Optimization = optimizationJSON
err = this.Save(tx, op)
if err != nil { if err != nil {
return err return err
} }
@@ -1237,6 +1311,61 @@ func (this *HTTPWebDAO) UpdateWebRequestScripts(tx *dbs.Tx, webId int64, config
return this.NotifyUpdate(tx, webId) return this.NotifyUpdate(tx, webId)
} }
// UpdateWebRequestScriptsAsPassed 设置请求脚本为审核通过
func (this *HTTPWebDAO) UpdateWebRequestScriptsAsPassed(tx *dbs.Tx, webId int64, codeMD5 string) error {
if webId <= 0 || len(codeMD5) == 0 {
return nil
}
configString, err := this.Query(tx).
Pk(webId).
Result("requestScripts").
FindStringCol("")
if err != nil {
return nil
}
var config = &serverconfigs.HTTPRequestScriptsConfig{}
if len(configString) == 0 {
return nil
}
err = json.Unmarshal([]byte(configString), config)
if err != nil {
return err
}
var found bool
for _, group := range config.AllGroups() {
for _, script := range group.Scripts {
if script.AuditingCodeMD5 == codeMD5 {
script.Code = script.AuditingCode
script.AuditingCode = ""
script.AuditingCodeMD5 = ""
found = true
}
}
}
if found {
configJSON, err := json.Marshal(config)
if err != nil {
return err
}
err = this.Query(tx).
Pk(webId).
Set("requestScripts", configJSON).
UpdateQuickly()
if err != nil {
return err
}
return this.NotifyUpdate(tx, webId)
}
return nil
}
// FindWebRequestScripts 查找服务的脚本设置 // FindWebRequestScripts 查找服务的脚本设置
func (this *HTTPWebDAO) FindWebRequestScripts(tx *dbs.Tx, webId int64) (*serverconfigs.HTTPRequestScriptsConfig, error) { func (this *HTTPWebDAO) FindWebRequestScripts(tx *dbs.Tx, webId int64) (*serverconfigs.HTTPRequestScriptsConfig, error) {
configString, err := this.Query(tx). configString, err := this.Query(tx).
@@ -1337,7 +1466,7 @@ func (this *HTTPWebDAO) UpdateWebReferers(tx *dbs.Tx, webId int64, referersConfi
return this.NotifyUpdate(tx, webId) return this.NotifyUpdate(tx, webId)
} }
// FindWebReferers 查找服务的防盗链配置 // FindWebReferers 查找网站的防盗链配置
func (this *HTTPWebDAO) FindWebReferers(tx *dbs.Tx, webId int64) ([]byte, error) { func (this *HTTPWebDAO) FindWebReferers(tx *dbs.Tx, webId int64) ([]byte, error) {
return this.Query(tx). return this.Query(tx).
Pk(webId). Pk(webId).
@@ -1347,6 +1476,10 @@ func (this *HTTPWebDAO) FindWebReferers(tx *dbs.Tx, webId int64) ([]byte, error)
// UpdateWebUserAgent 修改User-Agent设置 // UpdateWebUserAgent 修改User-Agent设置
func (this *HTTPWebDAO) UpdateWebUserAgent(tx *dbs.Tx, webId int64, userAgentConfig *serverconfigs.UserAgentConfig) error { func (this *HTTPWebDAO) UpdateWebUserAgent(tx *dbs.Tx, webId int64, userAgentConfig *serverconfigs.UserAgentConfig) error {
if webId <= 0 {
return errors.New("require 'webId'")
}
if userAgentConfig == nil { if userAgentConfig == nil {
return nil return nil
} }

View File

@@ -2,6 +2,48 @@ package models
import "github.com/iwind/TeaGo/dbs" import "github.com/iwind/TeaGo/dbs"
const (
HTTPWebField_Id dbs.FieldName = "id" // ID
HTTPWebField_IsOn dbs.FieldName = "isOn" // 是否启用
HTTPWebField_TemplateId dbs.FieldName = "templateId" // 模版ID
HTTPWebField_AdminId dbs.FieldName = "adminId" // 管理员ID
HTTPWebField_UserId dbs.FieldName = "userId" // 用户ID
HTTPWebField_State dbs.FieldName = "state" // 状态
HTTPWebField_CreatedAt dbs.FieldName = "createdAt" // 创建时间
HTTPWebField_Root dbs.FieldName = "root" // 根目录
HTTPWebField_Charset dbs.FieldName = "charset" // 字符集
HTTPWebField_Shutdown dbs.FieldName = "shutdown" // 临时关闭页面配置
HTTPWebField_Pages dbs.FieldName = "pages" // 特殊页面
HTTPWebField_RedirectToHttps dbs.FieldName = "redirectToHttps" // 跳转到HTTPS设置
HTTPWebField_Indexes dbs.FieldName = "indexes" // 首页文件列表
HTTPWebField_MaxRequestBodySize dbs.FieldName = "maxRequestBodySize" // 最大允许的请求内容尺寸
HTTPWebField_RequestHeader dbs.FieldName = "requestHeader" // 请求Header配置
HTTPWebField_ResponseHeader dbs.FieldName = "responseHeader" // 响应Header配置
HTTPWebField_AccessLog dbs.FieldName = "accessLog" // 访问日志配置
HTTPWebField_Stat dbs.FieldName = "stat" // 统计配置
HTTPWebField_Gzip dbs.FieldName = "gzip" // Gzip配置v0.3.2弃用)
HTTPWebField_Compression dbs.FieldName = "compression" // 压缩配置
HTTPWebField_Cache dbs.FieldName = "cache" // 缓存配置
HTTPWebField_Firewall dbs.FieldName = "firewall" // 防火墙设置
HTTPWebField_Locations dbs.FieldName = "locations" // 路由规则配置
HTTPWebField_Websocket dbs.FieldName = "websocket" // Websocket设置
HTTPWebField_RewriteRules dbs.FieldName = "rewriteRules" // 重写规则配置
HTTPWebField_HostRedirects dbs.FieldName = "hostRedirects" // 域名跳转
HTTPWebField_Fastcgi dbs.FieldName = "fastcgi" // Fastcgi配置
HTTPWebField_Auth dbs.FieldName = "auth" // 认证策略配置
HTTPWebField_Webp dbs.FieldName = "webp" // WebP配置
HTTPWebField_RemoteAddr dbs.FieldName = "remoteAddr" // 客户端IP配置
HTTPWebField_MergeSlashes dbs.FieldName = "mergeSlashes" // 是否合并路径中的斜杠
HTTPWebField_RequestLimit dbs.FieldName = "requestLimit" // 请求限制
HTTPWebField_RequestScripts dbs.FieldName = "requestScripts" // 请求脚本
HTTPWebField_Uam dbs.FieldName = "uam" // UAM设置
HTTPWebField_Cc dbs.FieldName = "cc" // CC设置
HTTPWebField_Referers dbs.FieldName = "referers" // 防盗链设置
HTTPWebField_UserAgent dbs.FieldName = "userAgent" // UserAgent设置
HTTPWebField_Optimization dbs.FieldName = "optimization" // 页面优化配置
HTTPWebField_Hls dbs.FieldName = "hls" // HLS设置
)
// HTTPWeb HTTP Web // HTTPWeb HTTP Web
type HTTPWeb struct { type HTTPWeb struct {
Id uint32 `field:"id"` // ID Id uint32 `field:"id"` // ID
@@ -41,6 +83,8 @@ type HTTPWeb struct {
Cc dbs.JSON `field:"cc"` // CC设置 Cc dbs.JSON `field:"cc"` // CC设置
Referers dbs.JSON `field:"referers"` // 防盗链设置 Referers dbs.JSON `field:"referers"` // 防盗链设置
UserAgent dbs.JSON `field:"userAgent"` // UserAgent设置 UserAgent dbs.JSON `field:"userAgent"` // UserAgent设置
Optimization dbs.JSON `field:"optimization"` // 页面优化配置
Hls dbs.JSON `field:"hls"` // HLS设置
} }
type HTTPWebOperator struct { type HTTPWebOperator struct {
@@ -81,6 +125,8 @@ type HTTPWebOperator struct {
Cc any // CC设置 Cc any // CC设置
Referers any // 防盗链设置 Referers any // 防盗链设置
UserAgent any // UserAgent设置 UserAgent any // UserAgent设置
Optimization any // 页面优化配置
Hls any // HLS设置
} }
func NewHTTPWebOperator() *HTTPWebOperator { func NewHTTPWebOperator() *HTTPWebOperator {

View File

@@ -5,7 +5,7 @@ import (
"github.com/TeaOSLab/EdgeAPI/internal/errors" "github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman" "github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs" "github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils" "github.com/TeaOSLab/EdgeCommon/pkg/iputils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
@@ -13,7 +13,8 @@ import (
"github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/lists" "github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/types" "github.com/iwind/TeaGo/types"
"math" "net"
"strings"
"time" "time"
) )
@@ -155,6 +156,59 @@ func (this *IPItemDAO) DisableIPItemsWithIP(tx *dbs.Tx, ipFrom string, ipTo stri
return nil return nil
} }
// DisableIPItemsWithIPValue 禁用某个IP相关条目
func (this *IPItemDAO) DisableIPItemsWithIPValue(tx *dbs.Tx, value string, sourceUserId int64, listId int64) error {
if len(value) == 0 {
return errors.New("invalid 'value'")
}
var query = this.Query(tx).
Result("id", "listId").
Attr("value", value).
State(IPItemStateEnabled)
if listId > 0 {
query.Attr("listId", listId)
}
if sourceUserId > 0 {
query.Attr("sourceUserId", sourceUserId)
}
ones, err := query.FindAll()
if err != nil {
return err
}
var itemIds = []int64{}
for _, one := range ones {
var item = one.(*IPItem)
var itemId = int64(item.Id)
itemIds = append(itemIds, itemId)
}
for _, itemId := range itemIds {
version, err := SharedIPListDAO.IncreaseVersion(tx)
if err != nil {
return err
}
_, err = this.Query(tx).
Pk(itemId).
Set("state", IPItemStateDisabled).
Set("version", version).
Update()
if err != nil {
return err
}
}
if len(itemIds) > 0 {
return this.NotifyUpdate(tx, itemIds[len(itemIds)-1])
}
return nil
}
// DisableIPItemsWithListId 禁用某个IP名单内的所有IP // DisableIPItemsWithListId 禁用某个IP名单内的所有IP
func (this *IPItemDAO) DisableIPItemsWithListId(tx *dbs.Tx, listId int64) error { func (this *IPItemDAO) DisableIPItemsWithListId(tx *dbs.Tx, listId int64) error {
for { for {
@@ -236,9 +290,46 @@ func (this *IPItemDAO) DeleteOldItem(tx *dbs.Tx, listId int64, ipFrom string, ip
return nil return nil
} }
// DeleteOldItemWithValue 根据IP删除以前的旧记录
func (this *IPItemDAO) DeleteOldItemWithValue(tx *dbs.Tx, listId int64, value string) error {
if len(value) == 0 {
return nil
}
ones, err := this.Query(tx).
ResultPk().
UseIndex("ipFrom").
Attr("listId", listId).
Attr("value", value).
Attr("state", IPItemStateEnabled).
FindAll()
if err != nil {
return err
}
for _, one := range ones {
var itemId = int64(one.(*IPItem).Id)
version, err := SharedIPListDAO.IncreaseVersion(tx)
if err != nil {
return err
}
err = this.Query(tx).
Pk(itemId).
Set("version", version).
Set("state", IPItemStateDisabled).
UpdateQuickly()
if err != nil {
return err
}
}
return nil
}
// CreateIPItem 创建IP // CreateIPItem 创建IP
func (this *IPItemDAO) CreateIPItem(tx *dbs.Tx, func (this *IPItemDAO) CreateIPItem(tx *dbs.Tx,
listId int64, listId int64,
value string,
ipFrom string, ipFrom string,
ipTo string, ipTo string,
expiredAt int64, expiredAt int64,
@@ -253,6 +344,15 @@ func (this *IPItemDAO) CreateIPItem(tx *dbs.Tx,
sourceHTTPFirewallRuleGroupId int64, sourceHTTPFirewallRuleGroupId int64,
sourceHTTPFirewallRuleSetId int64, sourceHTTPFirewallRuleSetId int64,
shouldNotify bool) (int64, error) { shouldNotify bool) (int64, error) {
// generate 'itemType'
if itemType != IPItemTypeAll && len(ipFrom) > 0 {
if iputils.IsIPv4(ipFrom) {
itemType = IPItemTypeIPv4
} else if iputils.IsIPv6(ipFrom) {
itemType = IPItemTypeIPv6
}
}
version, err := SharedIPListDAO.IncreaseVersion(tx) version, err := SharedIPListDAO.IncreaseVersion(tx)
if err != nil { if err != nil {
return 0, err return 0, err
@@ -260,10 +360,10 @@ func (this *IPItemDAO) CreateIPItem(tx *dbs.Tx,
var op = NewIPItemOperator() var op = NewIPItemOperator()
op.ListId = listId op.ListId = listId
op.Value = value
op.IpFrom = ipFrom op.IpFrom = ipFrom
op.IpTo = ipTo op.IpTo = ipTo
op.IpFromLong = utils.IP2Long(ipFrom)
op.IpToLong = utils.IP2Long(ipTo)
op.Reason = reason op.Reason = reason
op.Type = itemType op.Type = itemType
op.EventLevel = eventLevel op.EventLevel = eventLevel
@@ -319,11 +419,20 @@ func (this *IPItemDAO) CreateIPItem(tx *dbs.Tx,
} }
// UpdateIPItem 修改IP // UpdateIPItem 修改IP
func (this *IPItemDAO) UpdateIPItem(tx *dbs.Tx, itemId int64, ipFrom string, ipTo string, expiredAt int64, reason string, itemType IPItemType, eventLevel string) error { func (this *IPItemDAO) UpdateIPItem(tx *dbs.Tx, itemId int64, value string, ipFrom string, ipTo string, expiredAt int64, reason string, itemType IPItemType, eventLevel string) error {
if itemId <= 0 { if itemId <= 0 {
return errors.New("invalid itemId") return errors.New("invalid itemId")
} }
// generate 'itemType'
if itemType != IPItemTypeAll && len(ipFrom) > 0 {
if iputils.IsIPv4(ipFrom) {
itemType = IPItemTypeIPv4
} else if iputils.IsIPv6(ipFrom) {
itemType = IPItemTypeIPv6
}
}
listId, err := this.Query(tx). listId, err := this.Query(tx).
Pk(itemId). Pk(itemId).
Result("listId"). Result("listId").
@@ -342,10 +451,10 @@ func (this *IPItemDAO) UpdateIPItem(tx *dbs.Tx, itemId int64, ipFrom string, ipT
var op = NewIPItemOperator() var op = NewIPItemOperator()
op.Id = itemId op.Id = itemId
op.Value = value
op.IpFrom = ipFrom op.IpFrom = ipFrom
op.IpTo = ipTo op.IpTo = ipTo
op.IpFromLong = utils.IP2Long(ipFrom)
op.IpToLong = utils.IP2Long(ipTo)
op.Reason = reason op.Reason = reason
op.Type = itemType op.Type = itemType
op.EventLevel = eventLevel op.EventLevel = eventLevel
@@ -442,16 +551,21 @@ func (this *IPItemDAO) FindItemListId(tx *dbs.Tx, itemId int64) (int64, error) {
} }
// FindEnabledItemContainsIP 查找包含某个IP的Item // FindEnabledItemContainsIP 查找包含某个IP的Item
func (this *IPItemDAO) FindEnabledItemContainsIP(tx *dbs.Tx, listId int64, ip uint64) (*IPItem, error) { func (this *IPItemDAO) FindEnabledItemContainsIP(tx *dbs.Tx, listId int64, ip string) (*IPItem, error) {
query := this.Query(tx). var query = this.Query(tx).
Attr("listId", listId). Attr("listId", listId).
State(IPItemStateEnabled) State(IPItemStateEnabled)
if ip > math.MaxUint32 {
query.Where("(type='all' OR ipFromLong=:ip)") if iputils.IsIPv4(ip) {
} else { query.Where("(type='all' OR ipFrom =:ip OR INET_ATON(:ip) BETWEEN INET_ATON(ipFrom) AND INET_ATON(ipTo))").
query.Where("(type='all' OR ipFromLong=:ip OR (ipToLong>0 AND ipFromLong<=:ip AND ipToLong>=:ip))").
Param("ip", ip) Param("ip", ip)
} else if iputils.IsIPv6(ip) {
query.Where("(type='all' OR ipFrom =:ip OR HEX(INET6_ATON(:ip)) BETWEEN HEX(INET6_ATON(ipFrom)) AND HEX(INET6_ATON(ipTo)))").
Param("ip", ip)
} else {
return nil, nil
} }
one, err := query.Find() one, err := query.Find()
if err != nil { if err != nil {
return nil, err return nil, err
@@ -498,7 +612,17 @@ func (this *IPItemDAO) CountAllEnabledIPItems(tx *dbs.Tx, sourceUserId int64, ke
} }
} }
if len(keyword) > 0 { if len(keyword) > 0 {
query.Like("ipFrom", dbutils.QuoteLike(keyword)) if net.ParseIP(keyword) != nil { // 是一个IP地址
if iputils.IsIPv4(keyword) {
query.Where("(type='all' OR ipFrom =:ipKeyword OR INET_ATON(:ipKeyword) BETWEEN INET_ATON(ipFrom) AND INET_ATON(ipTo))").
Param("ipKeyword", keyword)
} else if iputils.IsIPv6(keyword) {
query.Where("(type='all' OR ipFrom =:ipKeyword OR HEX(INET6_ATON(:ipKeyword)) BETWEEN HEX(INET6_ATON(ipFrom)) AND HEX(INET6_ATON(ipTo)))").
Param("ipKeyword", keyword)
}
} else {
query.Like("ipFrom", dbutils.QuoteLike(keyword))
}
} }
if len(ip) > 0 { if len(ip) > 0 {
query.Attr("ipFrom", ip) query.Attr("ipFrom", ip)
@@ -540,7 +664,17 @@ func (this *IPItemDAO) ListAllEnabledIPItems(tx *dbs.Tx, sourceUserId int64, key
} }
} }
if len(keyword) > 0 { if len(keyword) > 0 {
query.Like("ipFrom", dbutils.QuoteLike(keyword)) if net.ParseIP(keyword) != nil { // 是一个IP地址
if iputils.IsIPv4(keyword) {
query.Where("(type='all' OR ipFrom =:ipKeyword OR INET_ATON(:ipKeyword) BETWEEN INET_ATON(ipFrom) AND INET_ATON(ipTo))").
Param("ipKeyword", keyword)
} else if iputils.IsIPv6(keyword) {
query.Where("(type='all' OR ipFrom =:ipKeyword OR HEX(INET6_ATON(:ipKeyword)) BETWEEN HEX(INET6_ATON(ipFrom)) AND HEX(INET6_ATON(ipTo)))").
Param("ipKeyword", keyword)
}
} else {
query.Like("ipFrom", dbutils.QuoteLike(keyword))
}
} }
if len(ip) > 0 { if len(ip) > 0 {
query.Attr("ipFrom", ip) query.Attr("ipFrom", ip)
@@ -573,6 +707,62 @@ func (this *IPItemDAO) ListAllEnabledIPItems(tx *dbs.Tx, sourceUserId int64, key
return return
} }
// ListAllIPItemIds 搜索所有IP Id列表
func (this *IPItemDAO) ListAllIPItemIds(tx *dbs.Tx, sourceUserId int64, keyword string, ip string, listId int64, unread bool, eventLevel string, listType string, offset int64, size int64) (itemIds []int64, err error) {
var query = this.Query(tx)
if sourceUserId > 0 {
if listId <= 0 {
query.Where("((listId=" + types.String(firewallconfigs.GlobalListId) + " AND sourceUserId=:sourceUserId) OR listId IN (SELECT id FROM " + SharedIPListDAO.Table + " WHERE userId=:sourceUserId AND state=1))")
query.Param("sourceUserId", sourceUserId)
} else if listId == firewallconfigs.GlobalListId {
query.Attr("sourceUserId", sourceUserId)
query.UseIndex("sourceUserId")
}
}
if len(keyword) > 0 {
if net.ParseIP(keyword) != nil { // 是一个IP地址
query.Attr("ipFrom", keyword)
} else {
query.Like("ipFrom", dbutils.QuoteLike(keyword))
}
}
if len(ip) > 0 {
query.Attr("ipFrom", ip)
}
if listId > 0 {
query.Attr("listId", listId)
} else {
if len(listType) > 0 {
query.Where("(listId=" + types.String(firewallconfigs.GlobalListId) + " OR listId IN (SELECT id FROM " + SharedIPListDAO.Table + " WHERE state=1 AND type=:listType))")
query.Param("listType", listType)
} else {
query.Where("(listId=" + types.String(firewallconfigs.GlobalListId) + " OR listId IN (SELECT id FROM " + SharedIPListDAO.Table + " WHERE state=1))")
}
}
if unread {
query.Attr("isRead", 0)
}
if len(eventLevel) > 0 {
query.Attr("eventLevel", eventLevel)
}
result, err := query.
ResultPk().
State(IPItemStateEnabled).
Where("(expiredAt=0 OR expiredAt>:expiredAt)").
Param("expiredAt", time.Now().Unix()).
DescPk().
Offset(offset).
Size(size).
FindAll()
if err != nil {
return nil, err
}
for _, itemOne := range result {
itemIds = append(itemIds, int64(itemOne.(*IPItem).Id))
}
return
}
// UpdateItemsRead 设置所有未已读 // UpdateItemsRead 设置所有未已读
func (this *IPItemDAO) UpdateItemsRead(tx *dbs.Tx, sourceUserId int64) error { func (this *IPItemDAO) UpdateItemsRead(tx *dbs.Tx, sourceUserId int64) error {
var query = this.Query(tx). var query = this.Query(tx).
@@ -632,6 +822,60 @@ func (this *IPItemDAO) CleanExpiredIPItems(tx *dbs.Tx) error {
return nil return nil
} }
// ParseIPValue 解析IP值
func (this *IPItemDAO) ParseIPValue(value string) (newValue string, ipFrom string, ipTo string, ok bool) {
if len(value) == 0 {
return
}
newValue = value
// ip1-ip2
if strings.Contains(value, "-") {
var pieces = strings.Split(value, "-")
if len(pieces) != 2 {
return
}
ipFrom = strings.TrimSpace(pieces[0])
ipTo = strings.TrimSpace(pieces[1])
if !iputils.IsValid(ipFrom) || !iputils.IsValid(ipTo) {
return
}
if !iputils.IsSameVersion(ipFrom, ipTo) {
return
}
if iputils.CompareIP(ipFrom, ipTo) > 0 {
ipFrom, ipTo = ipTo, ipFrom
newValue = ipFrom + "-" + ipTo
}
ok = true
return
}
// ip/mask
if strings.Contains(value, "/") {
cidr, err := iputils.ParseCIDR(value)
if err != nil {
return
}
return newValue, cidr.From().String(), cidr.To().String(), true
}
// single value
if iputils.IsValid(value) {
ipFrom = value
ok = true
return
}
return
}
// NotifyUpdate 通知更新 // NotifyUpdate 通知更新
func (this *IPItemDAO) NotifyUpdate(tx *dbs.Tx, itemId int64) error { func (this *IPItemDAO) NotifyUpdate(tx *dbs.Tx, itemId int64) error {
// 获取ListId // 获取ListId
@@ -665,6 +909,9 @@ func (this *IPItemDAO) NotifyUpdate(tx *dbs.Tx, itemId int64) error {
} }
} else { } else {
clusterIds, err := SharedNodeClusterDAO.FindAllEnabledNodeClusterIds(tx) clusterIds, err := SharedNodeClusterDAO.FindAllEnabledNodeClusterIds(tx)
if err != nil {
return err
}
for _, clusterId := range clusterIds { for _, clusterId := range clusterIds {
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeIPItemChanged) err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeIPItemChanged)
if err != nil { if err != nil {

View File

@@ -51,7 +51,8 @@ func TestIPItemDAO_CreateManyIPs(t *testing.T) {
var dao = models.NewIPItemDAO() var dao = models.NewIPItemDAO()
var n = 10 var n = 10
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
itemId, err := dao.CreateIPItem(tx, firewallconfigs.GlobalListId, "192."+types.String(rands.Int(0, 255))+"."+types.String(rands.Int(0, 255))+"."+types.String(rands.Int(0, 255)), "", time.Now().Unix()+86400, "test", models.IPItemTypeIPv4, "warning", 0, 0, 0, 0, 0, 0, 0, false) var ip = "192." + types.String(rands.Int(0, 255)) + "." + types.String(rands.Int(0, 255)) + "." + types.String(rands.Int(0, 255))
itemId, err := dao.CreateIPItem(tx, firewallconfigs.GlobalListId, ip, ip, "", time.Now().Unix()+86400, "test", models.IPItemTypeIPv4, "warning", 0, 0, 0, 0, 0, 0, 0, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -74,3 +75,16 @@ func TestIPItemDAO_DisableIPItemsWithIP(t *testing.T) {
} }
t.Log("ok") t.Log("ok")
} }
func TestIPItemDAO_ParseIPValue(t *testing.T) {
var dao = models.NewIPItemDAO()
t.Log(dao.ParseIPValue("192.168.1.100"))
t.Log(dao.ParseIPValue("192.168.1.100-192.168.1.200"))
t.Log(dao.ParseIPValue("192.168.1.200-192.168.1.100"))
t.Log(dao.ParseIPValue("192.168.1.100/24"))
t.Log(dao.ParseIPValue("::1"))
t.Log(dao.ParseIPValue("192.168.1.100-::2"))
t.Log(dao.ParseIPValue("192"))
t.Log(dao.ParseIPValue("192.168.1.200/256"))
t.Log(dao.ParseIPValue("192.168.1.200-"))
}

View File

@@ -1,14 +1,44 @@
package models package models
import "github.com/iwind/TeaGo/dbs"
const (
IPItemField_Id dbs.FieldName = "id" // ID
IPItemField_ListId dbs.FieldName = "listId" // 所属名单ID
IPItemField_Value dbs.FieldName = "value" // 原始值
IPItemField_Type dbs.FieldName = "type" // 类型
IPItemField_IpFrom dbs.FieldName = "ipFrom" // 开始IP
IPItemField_IpTo dbs.FieldName = "ipTo" // 结束IP
IPItemField_IpFromLong dbs.FieldName = "ipFromLong" // 开始IP整型弃用
IPItemField_IpToLong dbs.FieldName = "ipToLong" // 结束IP整型弃用
IPItemField_Version dbs.FieldName = "version" // 版本
IPItemField_CreatedAt dbs.FieldName = "createdAt" // 创建时间
IPItemField_UpdatedAt dbs.FieldName = "updatedAt" // 修改时间
IPItemField_Reason dbs.FieldName = "reason" // 加入说明
IPItemField_EventLevel dbs.FieldName = "eventLevel" // 事件级别
IPItemField_State dbs.FieldName = "state" // 状态
IPItemField_ExpiredAt dbs.FieldName = "expiredAt" // 过期时间
IPItemField_ServerId dbs.FieldName = "serverId" // 有效范围服务ID
IPItemField_NodeId dbs.FieldName = "nodeId" // 有效范围节点ID
IPItemField_SourceNodeId dbs.FieldName = "sourceNodeId" // 来源节点ID
IPItemField_SourceServerId dbs.FieldName = "sourceServerId" // 来源服务ID
IPItemField_SourceHTTPFirewallPolicyId dbs.FieldName = "sourceHTTPFirewallPolicyId" // 来源策略ID
IPItemField_SourceHTTPFirewallRuleGroupId dbs.FieldName = "sourceHTTPFirewallRuleGroupId" // 来源规则集分组ID
IPItemField_SourceHTTPFirewallRuleSetId dbs.FieldName = "sourceHTTPFirewallRuleSetId" // 来源规则集ID
IPItemField_SourceUserId dbs.FieldName = "sourceUserId" // 用户ID
IPItemField_IsRead dbs.FieldName = "isRead" // 是否已读
)
// IPItem IP // IPItem IP
type IPItem struct { type IPItem struct {
Id uint64 `field:"id"` // ID Id uint64 `field:"id"` // ID
ListId uint32 `field:"listId"` // 所属名单ID ListId uint32 `field:"listId"` // 所属名单ID
Value string `field:"value"` // 原始值
Type string `field:"type"` // 类型 Type string `field:"type"` // 类型
IpFrom string `field:"ipFrom"` // 开始IP IpFrom string `field:"ipFrom"` // 开始IP
IpTo string `field:"ipTo"` // 结束IP IpTo string `field:"ipTo"` // 结束IP
IpFromLong uint64 `field:"ipFromLong"` // 开始IP整型 IpFromLong uint64 `field:"ipFromLong"` // 开始IP整型(弃用)
IpToLong uint64 `field:"ipToLong"` // 结束IP整型 IpToLong uint64 `field:"ipToLong"` // 结束IP整型(弃用)
Version uint64 `field:"version"` // 版本 Version uint64 `field:"version"` // 版本
CreatedAt uint64 `field:"createdAt"` // 创建时间 CreatedAt uint64 `field:"createdAt"` // 创建时间
UpdatedAt uint64 `field:"updatedAt"` // 修改时间 UpdatedAt uint64 `field:"updatedAt"` // 修改时间
@@ -30,11 +60,12 @@ type IPItem struct {
type IPItemOperator struct { type IPItemOperator struct {
Id any // ID Id any // ID
ListId any // 所属名单ID ListId any // 所属名单ID
Value any // 原始值
Type any // 类型 Type any // 类型
IpFrom any // 开始IP IpFrom any // 开始IP
IpTo any // 结束IP IpTo any // 结束IP
IpFromLong any // 开始IP整型 IpFromLong any // 开始IP整型(弃用)
IpToLong any // 结束IP整型 IpToLong any // 结束IP整型(弃用)
Version any // 版本 Version any // 版本
CreatedAt any // 创建时间 CreatedAt any // 创建时间
UpdatedAt any // 修改时间 UpdatedAt any // 修改时间

View File

@@ -1 +1,15 @@
package models package models
// ComposeValue 组合原始值
func (this *IPItem) ComposeValue() string {
if len(this.Value) > 0 {
return this.Value
}
// 兼容以往版本
if len(this.IpTo) > 0 {
return this.IpFrom + "-" + this.IpTo
}
return this.IpFrom
}

View File

@@ -3,6 +3,7 @@ package models
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/regions" "github.com/TeaOSLab/EdgeAPI/internal/db/models/regions"
"github.com/TeaOSLab/EdgeAPI/internal/utils" "github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary" "github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
@@ -12,6 +13,7 @@ import (
"github.com/iwind/TeaGo/types" "github.com/iwind/TeaGo/types"
"io" "io"
"os" "os"
"strings"
"time" "time"
) )
@@ -299,7 +301,7 @@ func (this *IPLibraryFileDAO) GenerateIPLibrary(tx *dbs.Tx, libraryFileId int64)
var libraryFile = one.(*IPLibraryFile) var libraryFile = one.(*IPLibraryFile)
template, err := iplibrary.NewTemplate(libraryFile.Template) template, err := iplibrary.NewTemplate(libraryFile.Template)
if err != nil { if err != nil {
return errors.New("create template from '" + libraryFile.Template + "' failed: " + err.Error()) return fmt.Errorf("create template from '%s' failed: %w", libraryFile.Template, err)
} }
var fileId = int64(libraryFile.FileId) var fileId = int64(libraryFile.FileId)
@@ -314,17 +316,17 @@ func (this *IPLibraryFileDAO) GenerateIPLibrary(tx *dbs.Tx, libraryFileId int64)
if os.IsNotExist(err) { if os.IsNotExist(err) {
err = os.Mkdir(dir, 0777) err = os.Mkdir(dir, 0777)
if err != nil { if err != nil {
return errors.New("can not open dir '" + dir + "' to write: " + err.Error()) return fmt.Errorf("can not open dir '%s' to write: %w", dir, err)
} }
} else { } else {
return errors.New("can not open dir '" + dir + "' to write: " + err.Error()) return fmt.Errorf("can not open dir '%s' to write: %w", dir, err)
} }
} else if !stat.IsDir() { } else if !stat.IsDir() {
_ = os.Remove(dir) _ = os.Remove(dir)
err = os.Mkdir(dir, 0777) err = os.Mkdir(dir, 0777)
if err != nil { if err != nil {
return errors.New("can not open dir '" + dir + "' to write: " + err.Error()) return fmt.Errorf("can not open dir '%s' to write: %w", dir, err)
} }
} }
@@ -339,7 +341,7 @@ func (this *IPLibraryFileDAO) GenerateIPLibrary(tx *dbs.Tx, libraryFileId int64)
var countries = []*iplibrary.Country{} var countries = []*iplibrary.Country{}
for _, country := range dbCountries { for _, country := range dbCountries {
countries = append(countries, &iplibrary.Country{ countries = append(countries, &iplibrary.Country{
Id: types.Uint16(country.Id), Id: types.Uint16(country.ValueId),
Name: country.DisplayName(), Name: country.DisplayName(),
Codes: country.AllCodes(), Codes: country.AllCodes(),
}) })
@@ -354,7 +356,7 @@ func (this *IPLibraryFileDAO) GenerateIPLibrary(tx *dbs.Tx, libraryFileId int64)
var provinces = []*iplibrary.Province{} var provinces = []*iplibrary.Province{}
for _, province := range dbProvinces { for _, province := range dbProvinces {
provinces = append(provinces, &iplibrary.Province{ provinces = append(provinces, &iplibrary.Province{
Id: types.Uint16(province.Id), Id: types.Uint16(province.ValueId),
Name: province.DisplayName(), Name: province.DisplayName(),
Codes: province.AllCodes(), Codes: province.AllCodes(),
}) })
@@ -369,7 +371,7 @@ func (this *IPLibraryFileDAO) GenerateIPLibrary(tx *dbs.Tx, libraryFileId int64)
var cities = []*iplibrary.City{} var cities = []*iplibrary.City{}
for _, city := range dbCities { for _, city := range dbCities {
cities = append(cities, &iplibrary.City{ cities = append(cities, &iplibrary.City{
Id: city.Id, Id: city.ValueId,
Name: city.DisplayName(), Name: city.DisplayName(),
Codes: city.AllCodes(), Codes: city.AllCodes(),
}) })
@@ -384,7 +386,7 @@ func (this *IPLibraryFileDAO) GenerateIPLibrary(tx *dbs.Tx, libraryFileId int64)
var towns = []*iplibrary.Town{} var towns = []*iplibrary.Town{}
for _, town := range dbTowns { for _, town := range dbTowns {
towns = append(towns, &iplibrary.Town{ towns = append(towns, &iplibrary.Town{
Id: town.Id, Id: town.ValueId,
Name: town.DisplayName(), Name: town.DisplayName(),
Codes: town.AllCodes(), Codes: town.AllCodes(),
}) })
@@ -399,7 +401,7 @@ func (this *IPLibraryFileDAO) GenerateIPLibrary(tx *dbs.Tx, libraryFileId int64)
var providers = []*iplibrary.Provider{} var providers = []*iplibrary.Provider{}
for _, provider := range dbProviders { for _, provider := range dbProviders {
providers = append(providers, &iplibrary.Provider{ providers = append(providers, &iplibrary.Provider{
Id: types.Uint16(provider.Id), Id: types.Uint16(provider.ValueId),
Name: provider.DisplayName(), Name: provider.DisplayName(),
Codes: provider.AllCodes(), Codes: provider.AllCodes(),
}) })
@@ -428,7 +430,7 @@ func (this *IPLibraryFileDAO) GenerateIPLibrary(tx *dbs.Tx, libraryFileId int64)
err = writer.WriteMeta() err = writer.WriteMeta()
if err != nil { if err != nil {
return errors.New("write meta failed: " + err.Error()) return fmt.Errorf("write meta failed: %w", err)
} }
chunkIds, err := SharedFileChunkDAO.FindAllFileChunkIds(tx, fileId) chunkIds, err := SharedFileChunkDAO.FindAllFileChunkIds(tx, fileId)
@@ -440,35 +442,43 @@ func (this *IPLibraryFileDAO) GenerateIPLibrary(tx *dbs.Tx, libraryFileId int64)
var countryMap = map[string]int64{} // countryName => countryId var countryMap = map[string]int64{} // countryName => countryId
for _, country := range dbCountries { for _, country := range dbCountries {
for _, code := range country.AllCodes() { for _, code := range country.AllCodes() {
countryMap[code] = int64(country.Id) countryMap[code] = int64(country.ValueId)
} }
} }
var provinceMap = map[string]int64{} // countryId_provinceName => provinceId var provinceMap = map[string]int64{} // countryId_provinceName => provinceId
for _, province := range dbProvinces { for _, province := range dbProvinces {
for _, code := range province.AllCodes() { for _, code := range province.AllCodes() {
provinceMap[types.String(province.CountryId)+"_"+code] = int64(province.Id) provinceMap[types.String(province.CountryId)+"_"+code] = int64(province.ValueId)
for _, suffix := range regions.RegionProvinceSuffixes {
if strings.HasSuffix(code, suffix) {
provinceMap[types.String(province.CountryId)+"_"+strings.TrimSuffix(code, suffix)] = int64(province.ValueId)
} else {
provinceMap[types.String(province.CountryId)+"_"+(code+suffix)] = int64(province.ValueId)
}
}
} }
} }
var cityMap = map[string]int64{} // provinceId_cityName => cityId var cityMap = map[string]int64{} // provinceId_cityName => cityId
for _, city := range dbCities { for _, city := range dbCities {
for _, code := range city.AllCodes() { for _, code := range city.AllCodes() {
cityMap[types.String(city.ProvinceId)+"_"+code] = int64(city.Id) cityMap[types.String(city.ProvinceId)+"_"+code] = int64(city.ValueId)
} }
} }
var townMap = map[string]int64{} // cityId_townName => townId var townMap = map[string]int64{} // cityId_townName => townId
for _, town := range dbTowns { for _, town := range dbTowns {
for _, code := range town.AllCodes() { for _, code := range town.AllCodes() {
townMap[types.String(town.CityId)+"_"+code] = int64(town.Id) townMap[types.String(town.CityId)+"_"+code] = int64(town.ValueId)
} }
} }
var providerMap = map[string]int64{} // providerName => providerId var providerMap = map[string]int64{} // providerName => providerId
for _, provider := range dbProviders { for _, provider := range dbProviders {
for _, code := range provider.AllCodes() { for _, code := range provider.AllCodes() {
providerMap[code] = int64(provider.Id) providerMap[code] = int64(provider.ValueId)
} }
} }
@@ -503,7 +513,7 @@ func (this *IPLibraryFileDAO) GenerateIPLibrary(tx *dbs.Tx, libraryFileId int64)
err = writer.Write(ipFrom, ipTo, countryId, provinceId, cityId, townId, providerId) err = writer.Write(ipFrom, ipTo, countryId, provinceId, cityId, townId, providerId)
if err != nil { if err != nil {
return errors.New("write failed: " + err.Error()) return fmt.Errorf("write failed: %w", err)
} }
return nil return nil
@@ -536,7 +546,7 @@ func (this *IPLibraryFileDAO) GenerateIPLibrary(tx *dbs.Tx, libraryFileId int64)
// 将生成的内容写入到文件 // 将生成的内容写入到文件
stat, err = os.Stat(filePath) stat, err = os.Stat(filePath)
if err != nil { if err != nil {
return errors.New("stat generated file failed: " + err.Error()) return fmt.Errorf("stat generated file failed: %w", err)
} }
generatedFileId, err := SharedFileDAO.CreateFile(tx, 0, 0, "ipLibraryFile", "", libraryCode+".db", stat.Size(), "", false) generatedFileId, err := SharedFileDAO.CreateFile(tx, 0, 0, "ipLibraryFile", "", libraryCode+".db", stat.Size(), "", false)
if err != nil { if err != nil {
@@ -545,7 +555,7 @@ func (this *IPLibraryFileDAO) GenerateIPLibrary(tx *dbs.Tx, libraryFileId int64)
fp, err := os.Open(filePath) fp, err := os.Open(filePath)
if err != nil { if err != nil {
return errors.New("open generated file failed: " + err.Error()) return fmt.Errorf("open generated file failed: %w", err)
} }
var buf = make([]byte, 256*1024) var buf = make([]byte, 256*1024)
for { for {

View File

@@ -11,6 +11,7 @@ import (
"github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/lists" "github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types" "github.com/iwind/TeaGo/types"
) )
@@ -22,7 +23,7 @@ const (
var listTypeCacheMap = map[int64]*IPList{} // listId => *IPList var listTypeCacheMap = map[int64]*IPList{} // listId => *IPList
var DefaultGlobalIPList = &IPList{ var DefaultGlobalIPList = &IPList{
Id: uint32(firewallconfigs.GlobalListId), Id: uint32(firewallconfigs.GlobalListId),
Name: "全局封锁名单", Name: "系统黑名单",
IsPublic: true, IsPublic: true,
IsGlobal: true, IsGlobal: true,
Type: "black", Type: "black",
@@ -61,12 +62,16 @@ func (this *IPListDAO) EnableIPList(tx *dbs.Tx, id int64) error {
} }
// DisableIPList 禁用条目 // DisableIPList 禁用条目
func (this *IPListDAO) DisableIPList(tx *dbs.Tx, id int64) error { func (this *IPListDAO) DisableIPList(tx *dbs.Tx, listId int64) error {
_, err := this.Query(tx). _, err := this.Query(tx).
Pk(id). Pk(listId).
Set("state", IPListStateDisabled). Set("state", IPListStateDisabled).
Update() Update()
return err if err != nil {
return err
}
return this.NotifyUpdate(tx, listId, NodeTaskTypeIPListDeleted+"@"+string(maps.Map{"listId": listId}.AsJSON()))
} }
// FindEnabledIPList 查找启用中的条目 // FindEnabledIPList 查找启用中的条目
@@ -258,11 +263,35 @@ func (this *IPListDAO) ExistsEnabledIPList(tx *dbs.Tx, listId int64) (bool, erro
// NotifyUpdate 通知更新 // NotifyUpdate 通知更新
func (this *IPListDAO) NotifyUpdate(tx *dbs.Tx, listId int64, taskType NodeTaskType) error { func (this *IPListDAO) NotifyUpdate(tx *dbs.Tx, listId int64, taskType NodeTaskType) error {
// WAF策略中的
httpFirewallPolicyIds, err := SharedHTTPFirewallPolicyDAO.FindEnabledFirewallPolicyIdsWithIPListId(tx, listId) httpFirewallPolicyIds, err := SharedHTTPFirewallPolicyDAO.FindEnabledFirewallPolicyIdsWithIPListId(tx, listId)
if err != nil { if err != nil {
return err return err
} }
resultClusterIds := []int64{}
// 规则集动作中使用此名单的策略
ruleSetIds, err := SharedHTTPFirewallRuleSetDAO.FindAllEnabledRuleSetIdsWithIPListId(tx, listId)
if err != nil {
return err
}
for _, ruleSetId := range ruleSetIds {
ruleGroupId, err := SharedHTTPFirewallRuleGroupDAO.FindRuleGroupIdWithRuleSetId(tx, ruleSetId)
if err != nil {
return err
}
if ruleGroupId > 0 {
policyId, err := SharedHTTPFirewallPolicyDAO.FindEnabledFirewallPolicyIdWithRuleGroupId(tx, ruleGroupId)
if err != nil {
return err
}
if policyId > 0 && !lists.ContainsInt64(httpFirewallPolicyIds, policyId) {
httpFirewallPolicyIds = append(httpFirewallPolicyIds, policyId)
}
}
}
// 查找集群
var resultClusterIds = []int64{}
for _, policyId := range httpFirewallPolicyIds { for _, policyId := range httpFirewallPolicyIds {
// 集群 // 集群
clusterIds, err := SharedNodeClusterDAO.FindAllEnabledNodeClusterIdsWithHTTPFirewallPolicyId(tx, policyId) clusterIds, err := SharedNodeClusterDAO.FindAllEnabledNodeClusterIdsWithHTTPFirewallPolicyId(tx, policyId)
@@ -310,3 +339,16 @@ func (this *IPListDAO) NotifyUpdate(tx *dbs.Tx, listId int64, taskType NodeTaskT
return nil return nil
} }
// FindServerIdWithListId 查找IP名单对应的网站ID
func (this *IPListDAO) FindServerIdWithListId(tx *dbs.Tx, listId int64) (serverId int64, err error) {
if listId <= 0 {
return
}
serverId, err = this.Query(tx).
Pk(listId).
Result("serverId").
FindInt64Col(0)
return
}

View File

@@ -1,6 +1,7 @@
package models package models
import ( import (
"errors"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/dbs"
"runtime" "runtime"
@@ -27,7 +28,7 @@ func TestIPListDAO_CheckUserIPList(t *testing.T) {
{ {
err := NewIPListDAO().CheckUserIPList(tx, 1, 100) err := NewIPListDAO().CheckUserIPList(tx, 1, 100)
if err == ErrNotFound { if err != nil && errors.Is(err, ErrNotFound) {
t.Log("not found") t.Log("not found")
} else { } else {
t.Log(err) t.Log(err)
@@ -36,7 +37,7 @@ func TestIPListDAO_CheckUserIPList(t *testing.T) {
{ {
err := NewIPListDAO().CheckUserIPList(tx, 1, 85) err := NewIPListDAO().CheckUserIPList(tx, 1, 85)
if err == ErrNotFound { if err != nil && errors.Is(err, ErrNotFound) {
t.Log("not found") t.Log("not found")
} else { } else {
t.Log(err) t.Log(err)
@@ -45,7 +46,7 @@ func TestIPListDAO_CheckUserIPList(t *testing.T) {
{ {
err := NewIPListDAO().CheckUserIPList(tx, 1, 17) err := NewIPListDAO().CheckUserIPList(tx, 1, 17)
if err == ErrNotFound { if err != nil && errors.Is(err, ErrNotFound) {
t.Log("not found") t.Log("not found")
} else { } else {
t.Log(err) t.Log(err)
@@ -53,6 +54,17 @@ func TestIPListDAO_CheckUserIPList(t *testing.T) {
} }
} }
func TestIPListDAO_NotifyUpdate(t *testing.T) {
dbs.NotifyReady()
var dao = NewIPListDAO()
var tx *dbs.Tx
err := dao.NotifyUpdate(tx, 104, NodeTaskTypeIPListDeleted)
if err != nil {
t.Fatal(err)
}
}
func BenchmarkIPListDAO_IncreaseVersion(b *testing.B) { func BenchmarkIPListDAO_IncreaseVersion(b *testing.B) {
runtime.GOMAXPROCS(1) runtime.GOMAXPROCS(1)
@@ -65,4 +77,3 @@ func BenchmarkIPListDAO_IncreaseVersion(b *testing.B) {
_, _ = dao.IncreaseVersion(tx) _, _ = dao.IncreaseVersion(tx)
} }
} }

View File

@@ -1,9 +1,11 @@
package models package models
import ( import (
"encoding/json"
dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils" dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils"
"github.com/TeaOSLab/EdgeAPI/internal/errors" "github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/utils" "github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/dbs"
@@ -36,7 +38,7 @@ func init() {
} }
// CreateLog 创建管理员日志 // CreateLog 创建管理员日志
func (this *LogDAO) CreateLog(tx *dbs.Tx, adminType string, adminId int64, level string, description string, action string, ip string) error { func (this *LogDAO) CreateLog(tx *dbs.Tx, adminType string, adminId int64, level string, description string, action string, ip string, langMessageCode langs.MessageCode, langMessageArgs []any) error {
var op = NewLogOperator() var op = NewLogOperator()
op.Level = level op.Level = level
op.Description = utils.LimitString(description, 1000) op.Description = utils.LimitString(description, 1000)
@@ -53,6 +55,16 @@ func (this *LogDAO) CreateLog(tx *dbs.Tx, adminType string, adminId int64, level
op.ProviderId = adminId op.ProviderId = adminId
} }
// i18n
op.LangMessageCode = langMessageCode
if len(langMessageArgs) > 0 {
langMessageArgsJSON, err := json.Marshal(langMessageArgs)
if err != nil {
return err
}
op.LangMessageArgs = langMessageArgsJSON
}
op.Day = timeutil.Format("Ymd") op.Day = timeutil.Format("Ymd")
op.Type = LogTypeAdmin op.Type = LogTypeAdmin
err := this.Save(tx, op) err := this.Save(tx, op)

View File

@@ -1,34 +1,60 @@
package models package models
// 操作日志 import "github.com/iwind/TeaGo/dbs"
const (
LogField_Id dbs.FieldName = "id" // ID
LogField_Level dbs.FieldName = "level" // 级别
LogField_Description dbs.FieldName = "description" // 描述
LogField_CreatedAt dbs.FieldName = "createdAt" // 创建时间
LogField_Action dbs.FieldName = "action" // 动作
LogField_UserId dbs.FieldName = "userId" // 用户ID
LogField_AdminId dbs.FieldName = "adminId" // 管理员ID
LogField_ProviderId dbs.FieldName = "providerId" // 供应商ID
LogField_Ip dbs.FieldName = "ip" // IP地址
LogField_Type dbs.FieldName = "type" // 类型admin, user
LogField_Day dbs.FieldName = "day" // 日期
LogField_BillId dbs.FieldName = "billId" // 账单ID
LogField_LangMessageCode dbs.FieldName = "langMessageCode" // 多语言消息代号
LogField_LangMessageArgs dbs.FieldName = "langMessageArgs" // 多语言参数
LogField_Params dbs.FieldName = "params" // 关联对象参数
)
// Log 操作日志
type Log struct { type Log struct {
Id uint32 `field:"id"` // ID Id uint32 `field:"id"` // ID
Level string `field:"level"` // 级别 Level string `field:"level"` // 级别
Description string `field:"description"` // 描述 Description string `field:"description"` // 描述
CreatedAt uint64 `field:"createdAt"` // 创建时间 CreatedAt uint64 `field:"createdAt"` // 创建时间
Action string `field:"action"` // 动作 Action string `field:"action"` // 动作
UserId uint32 `field:"userId"` // 用户ID UserId uint32 `field:"userId"` // 用户ID
AdminId uint32 `field:"adminId"` // 管理员ID AdminId uint32 `field:"adminId"` // 管理员ID
ProviderId uint32 `field:"providerId"` // 供应商ID ProviderId uint32 `field:"providerId"` // 供应商ID
Ip string `field:"ip"` // IP地址 Ip string `field:"ip"` // IP地址
Type string `field:"type"` // 类型admin, user Type string `field:"type"` // 类型admin, user
Day string `field:"day"` // 日期 Day string `field:"day"` // 日期
BillId uint32 `field:"billId"` // 账单ID BillId uint32 `field:"billId"` // 账单ID
LangMessageCode string `field:"langMessageCode"` // 多语言消息代号
LangMessageArgs dbs.JSON `field:"langMessageArgs"` // 多语言参数
Params dbs.JSON `field:"params"` // 关联对象参数
} }
type LogOperator struct { type LogOperator struct {
Id interface{} // ID Id any // ID
Level interface{} // 级别 Level any // 级别
Description interface{} // 描述 Description any // 描述
CreatedAt interface{} // 创建时间 CreatedAt any // 创建时间
Action interface{} // 动作 Action any // 动作
UserId interface{} // 用户ID UserId any // 用户ID
AdminId interface{} // 管理员ID AdminId any // 管理员ID
ProviderId interface{} // 供应商ID ProviderId any // 供应商ID
Ip interface{} // IP地址 Ip any // IP地址
Type interface{} // 类型admin, user Type any // 类型admin, user
Day interface{} // 日期 Day any // 日期
BillId interface{} // 账单ID BillId any // 账单ID
LangMessageCode any // 多语言消息代号
LangMessageArgs any // 多语言参数
Params any // 关联对象参数
} }
func NewLogOperator() *LogOperator { func NewLogOperator() *LogOperator {

View File

@@ -135,40 +135,16 @@ func (this *LoginSessionDAO) WriteSessionValue(tx *dbs.Tx, sid string, key strin
sessionOp.UserId = userId sessionOp.UserId = userId
if isNewSession { if isNewSession {
// 删除此用户之前创建的SESSION防止单个用户SESSION过多 // 删除此用户之前创建的SESSION不再保存以往的SESSION避免安全问题
// TODO 将来改成按照活跃时间排序 err = this.Query(tx).
const maxSessionsPerUser = 10
oldOnes, err := this.Query(tx).
ResultPk(). ResultPk().
Attr("adminId", adminId). Attr("adminId", adminId).
Attr("userId", userId). Attr("userId", userId).
Asc("createdAt"). Neq("sid", sid).
FindAll() DeleteQuickly()
if err != nil { if err != nil {
return err return err
} }
var countOldOnes = len(oldOnes)
if countOldOnes > maxSessionsPerUser {
var countDeleted int
for _, oldOne := range oldOnes {
var oldSessionId = int64(oldOne.(*LoginSession).Id)
if oldSessionId == sessionId {
continue
}
if countDeleted < countOldOnes-maxSessionsPerUser {
err = this.Query(tx).
Pk(oldSessionId).
DeleteQuickly()
if err != nil {
return err
}
countDeleted++
} else {
break
}
}
}
} }
} }

View File

@@ -27,6 +27,8 @@ const (
type MessageType = string type MessageType = string
const ( const (
MessageTypeAll MessageType = "*"
// 这里的命名问题(首字母大写)为历史遗留问题,暂不修改 // 这里的命名问题(首字母大写)为历史遗留问题,暂不修改
MessageTypeHealthCheckFailed MessageType = "HealthCheckFailed" // 节点健康检查失败 MessageTypeHealthCheckFailed MessageType = "HealthCheckFailed" // 节点健康检查失败
@@ -109,14 +111,17 @@ func (this *MessageDAO) FindEnabledMessage(tx *dbs.Tx, id int64) (*Message, erro
} }
// CreateClusterMessage 创建集群消息 // CreateClusterMessage 创建集群消息
func (this *MessageDAO) CreateClusterMessage(tx *dbs.Tx, role string, clusterId int64, messageType MessageType, level string, subject string, body string, paramsJSON []byte) error { func (this *MessageDAO) CreateClusterMessage(tx *dbs.Tx, role string, clusterId int64, messageType MessageType, level string, subject string, shortBody string, body string, paramsJSON []byte) error {
_, err := this.createMessage(tx, role, clusterId, 0, messageType, level, subject, body, paramsJSON) if len(shortBody) == 0 {
shortBody = body
}
_, err := this.createMessage(tx, role, clusterId, 0, messageType, level, subject, shortBody, paramsJSON)
if err != nil { if err != nil {
return err return err
} }
// 发送给媒介接收人 // 发送给媒介接收人
err = SharedMessageTaskDAO.CreateMessageTasks(tx, role, 0, 0, 0, messageType, subject, body) err = SharedMessageTaskDAO.CreateMessageTasks(tx, role, clusterId, 0, 0, messageType, subject, body)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -12,7 +12,7 @@ func TestMessageDAO_CreateClusterMessage(t *testing.T) {
var tx *dbs.Tx var tx *dbs.Tx
dao := NewMessageDAO() dao := NewMessageDAO()
err := dao.CreateClusterMessage(tx, nodeconfigs.NodeRoleNode, 1, "test", "error", "123", "123", []byte("456")) err := dao.CreateClusterMessage(tx, nodeconfigs.NodeRoleNode, 1, "test", "error", "123", "123", "123", []byte("456"))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@@ -4,8 +4,6 @@ import (
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
) )
const ( const (
@@ -34,7 +32,7 @@ func init() {
}) })
} }
// 启用条目 // EnableMessageMedia 启用条目
func (this *MessageMediaDAO) EnableMessageMedia(tx *dbs.Tx, id int64) error { func (this *MessageMediaDAO) EnableMessageMedia(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx). _, err := this.Query(tx).
Pk(id). Pk(id).
@@ -43,7 +41,7 @@ func (this *MessageMediaDAO) EnableMessageMedia(tx *dbs.Tx, id int64) error {
return err return err
} }
// 禁用条目 // DisableMessageMedia 禁用条目
func (this *MessageMediaDAO) DisableMessageMedia(tx *dbs.Tx, id int64) error { func (this *MessageMediaDAO) DisableMessageMedia(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx). _, err := this.Query(tx).
Pk(id). Pk(id).
@@ -52,7 +50,7 @@ func (this *MessageMediaDAO) DisableMessageMedia(tx *dbs.Tx, id int64) error {
return err return err
} }
// 查找启用中的条目 // FindEnabledMessageMedia 查找启用中的条目
func (this *MessageMediaDAO) FindEnabledMessageMedia(tx *dbs.Tx, id int64) (*MessageMedia, error) { func (this *MessageMediaDAO) FindEnabledMessageMedia(tx *dbs.Tx, id int64) (*MessageMedia, error) {
result, err := this.Query(tx). result, err := this.Query(tx).
Pk(id). Pk(id).
@@ -64,7 +62,7 @@ func (this *MessageMediaDAO) FindEnabledMessageMedia(tx *dbs.Tx, id int64) (*Mes
return result.(*MessageMedia), err return result.(*MessageMedia), err
} }
// 根据主键查找名称 // FindMessageMediaName 根据主键查找名称
func (this *MessageMediaDAO) FindMessageMediaName(tx *dbs.Tx, id int64) (string, error) { func (this *MessageMediaDAO) FindMessageMediaName(tx *dbs.Tx, id int64) (string, error) {
return this.Query(tx). return this.Query(tx).
Pk(id). Pk(id).
@@ -72,7 +70,7 @@ func (this *MessageMediaDAO) FindMessageMediaName(tx *dbs.Tx, id int64) (string,
FindStringCol("") FindStringCol("")
} }
// 查询所有可用媒介 // FindAllEnabledMessageMedias 查询所有可用媒介
func (this *MessageMediaDAO) FindAllEnabledMessageMedias(tx *dbs.Tx) (result []*MessageMedia, err error) { func (this *MessageMediaDAO) FindAllEnabledMessageMedias(tx *dbs.Tx) (result []*MessageMedia, err error) {
_, err = this.Query(tx). _, err = this.Query(tx).
State(MessageMediaStateEnabled). State(MessageMediaStateEnabled).
@@ -82,74 +80,3 @@ func (this *MessageMediaDAO) FindAllEnabledMessageMedias(tx *dbs.Tx) (result []*
FindAll() FindAll()
return return
} }
// 设置当前所有可用的媒介
func (this *MessageMediaDAO) UpdateMessageMedias(tx *dbs.Tx, mediaMaps []maps.Map) error {
// 新的媒介信息
mediaTypes := []string{}
for index, m := range mediaMaps {
order := len(mediaMaps) - index
mediaType := m.GetString("type")
mediaTypes = append(mediaTypes, mediaType)
name := m.GetString("name")
description := m.GetString("description")
userDescription := m.GetString("userDescription")
isOn := m.GetBool("isOn")
mediaId, err := this.Query(tx).
ResultPk().
Attr("type", mediaType).
FindInt64Col(0)
if err != nil {
return err
}
var op = NewMessageMediaOperator()
if mediaId > 0 {
op.Id = mediaId
}
op.Name = name
op.Type = mediaType
op.Description = description
op.UserDescription = userDescription
op.IsOn = isOn
op.Order = order
op.State = MessageMediaStateEnabled
err = this.Save(tx, op)
if err != nil {
return err
}
}
// 老的媒介信息
ones, err := this.Query(tx).
FindAll()
if err != nil {
return err
}
for _, one := range ones {
mediaType := one.(*MessageMedia).Type
if !lists.ContainsString(mediaTypes, mediaType) {
err := this.Query(tx).
Pk(one.(*MessageMedia).Id).
Set("state", MessageMediaStateDisabled).
UpdateQuickly()
if err != nil {
return err
}
}
}
return nil
}
// 根据类型查找媒介
func (this *MessageMediaDAO) FindEnabledMediaWithType(tx *dbs.Tx, mediaType string) (*MessageMedia, error) {
one, err := this.Query(tx).
Attr("type", mediaType).
State(MessageMediaStateEnabled).
Find()
if one == nil || err != nil {
return nil, err
}
return one.(*MessageMedia), nil
}

View File

@@ -98,24 +98,6 @@ func (this *MessageReceiverDAO) CreateReceiver(tx *dbs.Tx, role string, clusterI
return this.SaveInt64(tx, op) return this.SaveInt64(tx, op)
} }
// FindAllEnabledReceivers 查询接收人
func (this *MessageReceiverDAO) FindAllEnabledReceivers(tx *dbs.Tx, role string, clusterId int64, nodeId int64, serverId int64, messageType string) (result []*MessageReceiver, err error) {
query := this.Query(tx)
if len(messageType) > 0 {
query.Attr("type", []string{"*", messageType}) // *表示所有的
}
_, err = query.
Attr("role", role).
Attr("clusterId", clusterId).
Attr("nodeId", nodeId).
Attr("serverId", serverId).
State(MessageReceiverStateEnabled).
AscPk().
Slice(&result).
FindAll()
return
}
// CountAllEnabledReceivers 计算接收人数量 // CountAllEnabledReceivers 计算接收人数量
func (this *MessageReceiverDAO) CountAllEnabledReceivers(tx *dbs.Tx, role string, clusterId int64, nodeId int64, serverId int64, messageType string) (int64, error) { func (this *MessageReceiverDAO) CountAllEnabledReceivers(tx *dbs.Tx, role string, clusterId int64, nodeId int64, serverId int64, messageType string) (int64, error) {
query := this.Query(tx) query := this.Query(tx)
@@ -146,6 +128,8 @@ func (this *MessageReceiverDAO) FindEnabledBestFitReceivers(tx *dbs.Tx, role str
} else if nodeId > 0 { } else if nodeId > 0 {
query.Attr("nodeId", nodeId) query.Attr("nodeId", nodeId)
} else if clusterId > 0 { } else if clusterId > 0 {
query.Attr("serverId", 0)
query.Attr("nodeId", 0)
query.Attr("clusterId", clusterId) query.Attr("clusterId", clusterId)
} }
_, err = query. _, err = query.

View File

@@ -1,30 +0,0 @@
package models
import (
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/logs"
"testing"
)
func TestMessageReceiverDAO_FindEnabledBestFitReceivers(t *testing.T) {
var tx *dbs.Tx
{
receivers, err := NewMessageReceiverDAO().FindEnabledBestFitReceivers(tx, nodeconfigs.NodeRoleNode, 18, 1, 2, "*")
if err != nil {
t.Fatal(err)
}
logs.PrintAsJSON(receivers, t)
}
{
receivers, err := NewMessageReceiverDAO().FindEnabledBestFitReceivers(tx, nodeconfigs.NodeRoleNode, 30, 1, 2, "*")
if err != nil {
t.Fatal(err)
}
logs.PrintAsJSON(receivers, t)
}
}

View File

@@ -1,31 +1,19 @@
package models package models
import ( import (
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman" "github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs" "github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/rands" "github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
stringutil "github.com/iwind/TeaGo/utils/string"
timeutil "github.com/iwind/TeaGo/utils/time" timeutil "github.com/iwind/TeaGo/utils/time"
"time" "time"
) )
type MessageTaskStatus = int
const ( const (
MessageTaskStateEnabled = 1 // 已启用 MessageTaskStateEnabled = 1 // 已启用
MessageTaskStateDisabled = 0 // 已禁用 MessageTaskStateDisabled = 0 // 已禁用
MessageTaskStatusNone MessageTaskStatus = 0 // 普通状态
MessageTaskStatusSending MessageTaskStatus = 1 // 发送中
MessageTaskStatusSuccess MessageTaskStatus = 2 // 发送成功
MessageTaskStatusFailed MessageTaskStatus = 3 // 发送失败
) )
type MessageTaskDAO dbs.DAO type MessageTaskDAO dbs.DAO
@@ -94,151 +82,6 @@ func (this *MessageTaskDAO) FindEnabledMessageTask(tx *dbs.Tx, id int64) (*Messa
return result.(*MessageTask), err return result.(*MessageTask), err
} }
// CreateMessageTask 创建任务
func (this *MessageTaskDAO) CreateMessageTask(tx *dbs.Tx, recipientId int64, instanceId int64, user string, subject string, body string, isPrimary bool) (int64, error) {
if !teaconst.IsPlus {
return 0, nil
}
var hash = stringutil.Md5(types.String(recipientId) + "@" + types.String(instanceId) + "@" + user + "@" + subject + "@" + types.String(isPrimary))
recipientInstanceId, err := SharedMessageRecipientDAO.FindRecipientInstanceId(tx, recipientId)
if err != nil {
return 0, err
}
if recipientInstanceId > 0 {
hashLifeSeconds, err := SharedMessageMediaInstanceDAO.FindInstanceHashLifeSeconds(tx, recipientInstanceId)
if err != nil {
return 0, err
}
if hashLifeSeconds >= 0 { // 意味着此值如果小于0则不做判断
lastMessageAt, err := this.Query(tx).
Attr("hash", hash).
Result("createdAt").
DescPk().
FindInt64Col(0)
if err != nil {
return 0, err
}
// 对于同一个人N分钟内消息不重复发送
if hashLifeSeconds <= 0 {
hashLifeSeconds = 60
}
if lastMessageAt > 0 && time.Now().Unix()-lastMessageAt < int64(hashLifeSeconds) {
return 0, nil
}
}
}
var op = NewMessageTaskOperator()
op.RecipientId = recipientId
op.InstanceId = instanceId
op.Hash = hash
op.User = user
op.Subject = subject
op.Body = body
op.IsPrimary = isPrimary
op.Day = timeutil.Format("Ymd")
op.Status = MessageTaskStatusNone
op.State = MessageTaskStateEnabled
return this.SaveInt64(tx, op)
}
// FindSendingMessageTasks 查找需要发送的任务
func (this *MessageTaskDAO) FindSendingMessageTasks(tx *dbs.Tx, size int64) (result []*MessageTask, err error) {
if size <= 0 {
return nil, nil
}
_, err = this.Query(tx).
State(MessageTaskStateEnabled).
Attr("status", MessageTaskStatusNone).
Where("(recipientId=0 OR recipientId IN (SELECT id FROM "+SharedMessageRecipientDAO.Table+" WHERE state=1 AND isOn=1 AND (timeFrom IS NULL OR timeTo IS NULL OR :time BETWEEN timeFrom AND timeTo)))").
Param("time", timeutil.Format("H:i:s")).
Desc("isPrimary").
AscPk().
Limit(size).
Slice(&result).
FindAll()
return
}
// CountMessageTasksWithStatus 根据状态计算任务数量
func (this *MessageTaskDAO) CountMessageTasksWithStatus(tx *dbs.Tx, status MessageTaskStatus) (int64, error) {
return this.Query(tx).
State(MessageTaskStateEnabled).
Attr("status", status).
Count()
}
// ListMessageTasksWithStatus 根据状态列出单页任务
func (this *MessageTaskDAO) ListMessageTasksWithStatus(tx *dbs.Tx, status MessageTaskStatus, offset int64, size int64) (result []*MessageTask, err error) {
_, err = this.Query(tx).
State(MessageTaskStateEnabled).
Attr("status", status).
Desc("isPrimary").
AscPk().
Offset(offset).
Limit(size).
Slice(&result).
FindAll()
return
}
// UpdateMessageTaskStatus 设置发送的状态
func (this *MessageTaskDAO) UpdateMessageTaskStatus(tx *dbs.Tx, taskId int64, status MessageTaskStatus, result []byte) error {
if taskId <= 0 {
return errors.New("invalid taskId")
}
var op = NewMessageTaskOperator()
op.Id = taskId
op.Status = status
op.SentAt = time.Now().Unix()
if len(result) > 0 {
op.Result = result
}
return this.Save(tx, op)
}
// CreateMessageTasks 从集群、节点或者服务中创建任务
func (this *MessageTaskDAO) CreateMessageTasks(tx *dbs.Tx, role nodeconfigs.NodeRole, clusterId int64, nodeId int64, serverId int64, messageType MessageType, subject string, body string) error {
if !teaconst.IsPlus {
return nil
}
receivers, err := SharedMessageReceiverDAO.FindEnabledBestFitReceivers(tx, role, clusterId, nodeId, serverId, messageType)
if err != nil {
return err
}
allRecipientIds := []int64{}
for _, receiver := range receivers {
if receiver.RecipientId > 0 {
allRecipientIds = append(allRecipientIds, int64(receiver.RecipientId))
} else if receiver.RecipientGroupId > 0 {
recipientIds, err := SharedMessageRecipientDAO.FindAllEnabledAndOnRecipientIdsWithGroup(tx, int64(receiver.RecipientGroupId))
if err != nil {
return err
}
allRecipientIds = append(allRecipientIds, recipientIds...)
}
}
sentMap := map[int64]bool{} // recipientId => bool 用来检查是否已经发送,防止重复发送给某个接收人
for _, recipientId := range allRecipientIds {
_, ok := sentMap[recipientId]
if ok {
continue
}
sentMap[recipientId] = true
_, err := this.CreateMessageTask(tx, recipientId, 0, "", subject, body, false)
if err != nil {
return err
}
}
return nil
}
// CleanExpiredMessageTasks 清理 // CleanExpiredMessageTasks 清理
func (this *MessageTaskDAO) CleanExpiredMessageTasks(tx *dbs.Tx, days int) error { func (this *MessageTaskDAO) CleanExpiredMessageTasks(tx *dbs.Tx, days int) error {
if days <= 0 { if days <= 0 {

View File

@@ -0,0 +1,14 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package models
import (
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/iwind/TeaGo/dbs"
)
// CreateMessageTasks 从集群、节点或者服务中创建任务
func (this *MessageTaskDAO) CreateMessageTasks(tx *dbs.Tx, role nodeconfigs.NodeRole, clusterId int64, nodeId int64, serverId int64, messageType MessageType, subject string, body string) error {
return nil
}

View File

@@ -8,20 +8,6 @@ import (
"testing" "testing"
) )
func TestMessageTaskDAO_FindSendingMessageTasks(t *testing.T) {
dbs.NotifyReady()
var tx *dbs.Tx
tasks, err := models.NewMessageTaskDAO().FindSendingMessageTasks(tx, 100)
if err != nil {
t.Fatal(err)
}
t.Log(len(tasks), "tasks")
for _, task := range tasks {
t.Log("task:", task.Id, "recipient:", task.RecipientId)
}
}
func TestMessageTaskDAO_CleanExpiredMessageTasks(t *testing.T) { func TestMessageTaskDAO_CleanExpiredMessageTasks(t *testing.T) {
var dao = models.NewMessageTaskDAO() var dao = models.NewMessageTaskDAO()
var tx *dbs.Tx var tx *dbs.Tx

View File

@@ -5,7 +5,6 @@ import (
"github.com/TeaOSLab/EdgeAPI/internal/goman" "github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs" "github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/go-sql-driver/mysql"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/dbs"
@@ -14,8 +13,10 @@ import (
"github.com/iwind/TeaGo/rands" "github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types" "github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time" timeutil "github.com/iwind/TeaGo/utils/time"
"regexp"
"sort" "sort"
"strconv" "strconv"
"strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@@ -40,6 +41,8 @@ func init() {
const MetricStatTablePartials = 20 // 表格Partial数量 const MetricStatTablePartials = 20 // 表格Partial数量
var metricHashRegexp = regexp.MustCompile(`^\w+$`)
func NewMetricStatDAO() *MetricStatDAO { func NewMetricStatDAO() *MetricStatDAO {
return dbs.NewDAO(&MetricStatDAO{ return dbs.NewDAO(&MetricStatDAO{
DAOObject: dbs.DAOObject{ DAOObject: dbs.DAOObject{
@@ -124,18 +127,30 @@ func (this *MetricStatDAO) DeleteItemStats(tx *dbs.Tx, itemId int64) error {
} }
// DeleteNodeItemStats 删除某个节点的统计数据 // DeleteNodeItemStats 删除某个节点的统计数据
func (this *MetricStatDAO) DeleteNodeItemStats(tx *dbs.Tx, nodeId int64, serverId int64, itemId int64, time string) error { func (this *MetricStatDAO) DeleteNodeItemStats(tx *dbs.Tx, nodeId int64, serverId int64, itemId int64, time string, keepKeys []string) error {
if serverId > 0 { if serverId > 0 {
_, err := this.Query(tx). var query = this.Query(tx).
Table(this.partialTable(serverId)). Table(this.partialTable(serverId)).
Attr("nodeId", nodeId). Attr("nodeId", nodeId).
Attr("serverId", serverId). Attr("serverId", serverId).
Attr("itemId", itemId). Attr("itemId", itemId).
Attr("time", time). Attr("time", time)
Delete() if len(keepKeys) > 0 {
if this.canIgnore(err) { query.Reuse(false)
var s []string
for _, k := range keepKeys {
if metricHashRegexp.MatchString(k) {
s = append(s, "'"+k+"@"+types.String(nodeId)+"'")
}
}
query.Where("hash NOT IN (" + strings.Join(s, ",") + ")")
}
err := query.
DeleteQuickly()
if err == nil || this.canIgnore(err) {
return nil return nil
} }
return err return err
} }
@@ -759,10 +774,5 @@ func (this *MetricStatDAO) canIgnore(err error) bool {
} }
// 忽略 Error 1213: Deadlock found 错误 // 忽略 Error 1213: Deadlock found 错误
mysqlErr, ok := err.(*mysql.MySQLError) return CheckSQLErrCode(err, 1213)
if ok && mysqlErr.Number == 1213 {
return true
}
return false
} }

View File

@@ -41,7 +41,7 @@ func TestMetricStatDAO_DeleteNodeItemStats(t *testing.T) {
defer func() { defer func() {
t.Log(time.Since(before).Seconds()*1000, "ms") t.Log(time.Since(before).Seconds()*1000, "ms")
}() }()
err := dao.DeleteNodeItemStats(nil, 1, 0, 1, timeutil.Format("Ymd")) err := dao.DeleteNodeItemStats(nil, 1, 0, 1, timeutil.Format("Ymd"), nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@@ -4,7 +4,6 @@ import (
"github.com/TeaOSLab/EdgeAPI/internal/goman" "github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs" "github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/go-sql-driver/mysql"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/dbs"
@@ -289,10 +288,5 @@ func (this *MetricSumStatDAO) canIgnore(err error) bool {
} }
// 忽略 Error 1213: Deadlock found 错误 // 忽略 Error 1213: Deadlock found 错误
mysqlErr, ok := err.(*mysql.MySQLError) return CheckSQLErrCode(err, 1213)
if ok && mysqlErr.Number == 1213 {
return true
}
return false
} }

View File

@@ -1,215 +0,0 @@
package models
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
)
const (
MonitorNodeStateEnabled = 1 // 已启用
MonitorNodeStateDisabled = 0 // 已禁用
)
type MonitorNodeDAO dbs.DAO
func NewMonitorNodeDAO() *MonitorNodeDAO {
return dbs.NewDAO(&MonitorNodeDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeMonitorNodes",
Model: new(MonitorNode),
PkName: "id",
},
}).(*MonitorNodeDAO)
}
var SharedMonitorNodeDAO *MonitorNodeDAO
func init() {
dbs.OnReady(func() {
SharedMonitorNodeDAO = NewMonitorNodeDAO()
})
}
// EnableMonitorNode 启用条目
func (this *MonitorNodeDAO) EnableMonitorNode(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
Set("state", MonitorNodeStateEnabled).
Update()
return err
}
// DisableMonitorNode 禁用条目
func (this *MonitorNodeDAO) DisableMonitorNode(tx *dbs.Tx, nodeId int64) error {
_, err := this.Query(tx).
Pk(nodeId).
Set("state", MonitorNodeStateDisabled).
Update()
if err != nil {
return err
}
// 删除运行日志
return SharedNodeLogDAO.DeleteNodeLogs(tx, nodeconfigs.NodeRoleMonitor, nodeId)
}
// FindEnabledMonitorNode 查找启用中的条目
func (this *MonitorNodeDAO) FindEnabledMonitorNode(tx *dbs.Tx, id int64) (*MonitorNode, error) {
result, err := this.Query(tx).
Pk(id).
Attr("state", MonitorNodeStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*MonitorNode), err
}
// FindMonitorNodeName 根据主键查找名称
func (this *MonitorNodeDAO) FindMonitorNodeName(tx *dbs.Tx, id int64) (string, error) {
return this.Query(tx).
Pk(id).
Result("name").
FindStringCol("")
}
// FindAllEnabledMonitorNodes 列出所有可用监控节点
func (this *MonitorNodeDAO) FindAllEnabledMonitorNodes(tx *dbs.Tx) (result []*MonitorNode, err error) {
_, err = this.Query(tx).
State(MonitorNodeStateEnabled).
Desc("order").
AscPk().
Slice(&result).
FindAll()
return
}
// CountAllEnabledMonitorNodes 计算监控节点数量
func (this *MonitorNodeDAO) CountAllEnabledMonitorNodes(tx *dbs.Tx) (int64, error) {
return this.Query(tx).
State(MonitorNodeStateEnabled).
Count()
}
// ListEnabledMonitorNodes 列出单页的监控节点
func (this *MonitorNodeDAO) ListEnabledMonitorNodes(tx *dbs.Tx, offset int64, size int64) (result []*MonitorNode, err error) {
_, err = this.Query(tx).
State(MonitorNodeStateEnabled).
Offset(offset).
Limit(size).
Desc("order").
DescPk().
Slice(&result).
FindAll()
return
}
// CreateMonitorNode 创建监控节点
func (this *MonitorNodeDAO) CreateMonitorNode(tx *dbs.Tx, name string, description string, isOn bool) (nodeId int64, err error) {
uniqueId, err := this.GenUniqueId(tx)
if err != nil {
return 0, err
}
secret := rands.String(32)
err = NewApiTokenDAO().CreateAPIToken(tx, uniqueId, secret, nodeconfigs.NodeRoleMonitor)
if err != nil {
return
}
var op = NewMonitorNodeOperator()
op.IsOn = isOn
op.UniqueId = uniqueId
op.Secret = secret
op.Name = name
op.Description = description
op.State = NodeStateEnabled
err = this.Save(tx, op)
if err != nil {
return
}
return types.Int64(op.Id), nil
}
// UpdateMonitorNode 修改监控节点
func (this *MonitorNodeDAO) UpdateMonitorNode(tx *dbs.Tx, nodeId int64, name string, description string, isOn bool) error {
if nodeId <= 0 {
return errors.New("invalid nodeId")
}
var op = NewMonitorNodeOperator()
op.Id = nodeId
op.Name = name
op.Description = description
op.IsOn = isOn
err := this.Save(tx, op)
return err
}
// FindEnabledMonitorNodeWithUniqueId 根据唯一ID获取节点信息
func (this *MonitorNodeDAO) FindEnabledMonitorNodeWithUniqueId(tx *dbs.Tx, uniqueId string) (*MonitorNode, error) {
result, err := this.Query(tx).
Attr("uniqueId", uniqueId).
Attr("state", MonitorNodeStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*MonitorNode), err
}
// FindEnabledMonitorNodeIdWithUniqueId 根据唯一ID获取节点ID
func (this *MonitorNodeDAO) FindEnabledMonitorNodeIdWithUniqueId(tx *dbs.Tx, uniqueId string) (int64, error) {
return this.Query(tx).
Attr("uniqueId", uniqueId).
Attr("state", MonitorNodeStateEnabled).
ResultPk().
FindInt64Col(0)
}
// GenUniqueId 生成唯一ID
func (this *MonitorNodeDAO) 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
}
}
// UpdateNodeStatus 更改节点状态
func (this *MonitorNodeDAO) UpdateNodeStatus(tx *dbs.Tx, nodeId int64, statusJSON []byte) error {
if statusJSON == nil {
return nil
}
_, err := this.Query(tx).
Pk(nodeId).
Set("status", string(statusJSON)).
Update()
return err
}
// CountAllLowerVersionNodes 计算所有节点中低于某个版本的节点数量
func (this *MonitorNodeDAO) CountAllLowerVersionNodes(tx *dbs.Tx, version string) (int64, error) {
return this.Query(tx).
State(MonitorNodeStateEnabled).
Attr("isOn", true).
Where("status IS NOT NULL").
Where("(JSON_EXTRACT(status, '$.buildVersionCode') IS NULL OR JSON_EXTRACT(status, '$.buildVersionCode')<:version)").
Param("version", utils.VersionToLong(version)).
Count()
}

View File

@@ -1,38 +0,0 @@
package models
import "github.com/iwind/TeaGo/dbs"
// MonitorNode 监控节点
type MonitorNode struct {
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 MonitorNodeOperator struct {
Id interface{} // ID
IsOn interface{} // 是否启用
UniqueId interface{} // 唯一ID
Secret interface{} // 密钥
Name interface{} // 名称
Description interface{} // 描述
Order interface{} // 排序
State interface{} // 状态
CreatedAt interface{} // 创建时间
AdminId interface{} // 管理员ID
Weight interface{} // 权重
Status interface{} // 运行状态
}
func NewMonitorNodeOperator() *MonitorNodeOperator {
return &MonitorNodeOperator{}
}

View File

@@ -100,6 +100,7 @@ func (this *NodeClusterDAO) FindNodeClusterName(tx *dbs.Tx, clusterId int64) (st
// FindAllEnableClusters 查找所有可用的集群 // FindAllEnableClusters 查找所有可用的集群
func (this *NodeClusterDAO) FindAllEnableClusters(tx *dbs.Tx) (result []*NodeCluster, err error) { func (this *NodeClusterDAO) FindAllEnableClusters(tx *dbs.Tx) (result []*NodeCluster, err error) {
_, err = this.Query(tx). _, err = this.Query(tx).
Result(NodeClusterField_Id, NodeClusterField_Name, NodeClusterField_IsOn, NodeClusterField_HealthCheck, NodeClusterField_AutoRemoteStart, NodeClusterField_AutoRegister, NodeClusterField_CreatedAt, NodeClusterField_UniqueId, NodeClusterField_Secret).
State(NodeClusterStateEnabled). State(NodeClusterStateEnabled).
Slice(&result). Slice(&result).
Desc("isPinned"). Desc("isPinned").
@@ -125,7 +126,7 @@ func (this *NodeClusterDAO) FindAllEnableClusterIds(tx *dbs.Tx) (result []int64,
} }
// CreateCluster 创建集群 // CreateCluster 创建集群
func (this *NodeClusterDAO) CreateCluster(tx *dbs.Tx, adminId int64, name string, grantId int64, installDir string, dnsDomainId int64, dnsName string, dnsTTL int32, cachePolicyId int64, httpFirewallPolicyId int64, systemServices map[string]maps.Map, globalServerConfig *serverconfigs.GlobalServerConfig, autoInstallNftables bool) (clusterId int64, err error) { func (this *NodeClusterDAO) CreateCluster(tx *dbs.Tx, adminId int64, name string, grantId int64, installDir string, dnsDomainId int64, dnsName string, dnsTTL int32, cachePolicyId int64, httpFirewallPolicyId int64, systemServices map[string]maps.Map, globalServerConfig *serverconfigs.GlobalServerConfig, autoInstallNftables bool, autoSystemTuning bool, autoTrimDisks bool) (clusterId int64, err error) {
uniqueId, err := this.GenUniqueId(tx) uniqueId, err := this.GenUniqueId(tx)
if err != nil { if err != nil {
return 0, err return 0, err
@@ -175,7 +176,7 @@ func (this *NodeClusterDAO) CreateCluster(tx *dbs.Tx, adminId int64, name string
// 全局服务配置 // 全局服务配置
if globalServerConfig == nil { if globalServerConfig == nil {
globalServerConfig = serverconfigs.DefaultGlobalServerConfig() globalServerConfig = serverconfigs.NewGlobalServerConfig()
} }
globalServerConfigJSON, err := json.Marshal(globalServerConfig) globalServerConfigJSON, err := json.Marshal(globalServerConfig)
if err != nil { if err != nil {
@@ -188,6 +189,8 @@ func (this *NodeClusterDAO) CreateCluster(tx *dbs.Tx, adminId int64, name string
op.UniqueId = uniqueId op.UniqueId = uniqueId
op.Secret = secret op.Secret = secret
op.AutoInstallNftables = autoInstallNftables op.AutoInstallNftables = autoInstallNftables
op.AutoSystemTuning = autoSystemTuning
op.AutoTrimDisks = autoTrimDisks
op.State = NodeClusterStateEnabled op.State = NodeClusterStateEnabled
err = this.Save(tx, op) err = this.Save(tx, op)
if err != nil { if err != nil {
@@ -198,7 +201,7 @@ func (this *NodeClusterDAO) CreateCluster(tx *dbs.Tx, adminId int64, name string
} }
// UpdateCluster 修改集群 // UpdateCluster 修改集群
func (this *NodeClusterDAO) UpdateCluster(tx *dbs.Tx, clusterId int64, name string, grantId int64, installDir string, timezone string, nodeMaxThreads int32, autoOpenPorts bool, clockConfig *nodeconfigs.ClockConfig, autoRemoteStart bool, autoInstallTables bool, sshParams *nodeconfigs.SSHParams) error { func (this *NodeClusterDAO) UpdateCluster(tx *dbs.Tx, clusterId int64, name string, grantId int64, installDir string, timezone string, nodeMaxThreads int32, autoOpenPorts bool, clockConfig *nodeconfigs.ClockConfig, autoRemoteStart bool, autoInstallTables bool, sshParams *nodeconfigs.SSHParams, autoSystemTuning bool, autoTrimDisks bool) error {
if clusterId <= 0 { if clusterId <= 0 {
return errors.New("invalid clusterId") return errors.New("invalid clusterId")
} }
@@ -225,6 +228,8 @@ func (this *NodeClusterDAO) UpdateCluster(tx *dbs.Tx, clusterId int64, name stri
op.AutoRemoteStart = autoRemoteStart op.AutoRemoteStart = autoRemoteStart
op.AutoInstallNftables = autoInstallTables op.AutoInstallNftables = autoInstallTables
op.AutoSystemTuning = autoSystemTuning
op.AutoTrimDisks = autoTrimDisks
if sshParams != nil { if sshParams != nil {
sshParamsJSON, err := json.Marshal(sshParams) sshParamsJSON, err := json.Marshal(sshParams)
@@ -261,20 +266,48 @@ func (this *NodeClusterDAO) CountAllEnabledClusters(tx *dbs.Tx, keyword string)
} }
// ListEnabledClusters 列出单页集群 // ListEnabledClusters 列出单页集群
func (this *NodeClusterDAO) ListEnabledClusters(tx *dbs.Tx, keyword string, offset, size int64) (result []*NodeCluster, err error) { func (this *NodeClusterDAO) ListEnabledClusters(tx *dbs.Tx, keyword string, idDesc bool, idAsc bool, offset, size int64) (result []*NodeCluster, err error) {
query := this.Query(tx). var query = this.Query(tx).
State(NodeClusterStateEnabled) State(NodeClusterStateEnabled)
if len(keyword) > 0 { if len(keyword) > 0 {
query.Where("(name LIKE :keyword OR dnsName like :keyword OR (dnsDomainId > 0 AND dnsDomainId IN (SELECT id FROM "+dns.SharedDNSDomainDAO.Table+" WHERE name LIKE :keyword AND state=1)))"). query.Where("(name LIKE :keyword OR dnsName like :keyword OR (dnsDomainId > 0 AND dnsDomainId IN (SELECT id FROM "+dns.SharedDNSDomainDAO.Table+" WHERE name LIKE :keyword AND state=1)))").
Param("keyword", dbutils.QuoteLike(keyword)) Param("keyword", dbutils.QuoteLike(keyword))
} }
if idDesc {
query.DescPk()
} else if idAsc {
query.AscPk()
} else {
query.Desc("isPinned").DescPk()
}
_, err = query. _, err = query.
Result(
NodeClusterField_Id,
NodeClusterField_Name,
NodeClusterField_IsOn,
NodeClusterField_IsPinned,
NodeClusterField_InstallDir,
NodeClusterField_HttpFirewallPolicyId,
NodeClusterField_AdminId,
NodeClusterField_IsOn,
NodeClusterField_IsAD,
NodeClusterField_UserId,
NodeClusterField_DnsName,
NodeClusterField_DnsDomainId,
NodeClusterField_Dns,
NodeClusterField_CreatedAt,
NodeClusterField_UniqueId,
NodeClusterField_Secret,
NodeClusterField_GrantId,
NodeClusterField_TimeZone,
).
Offset(offset). Offset(offset).
Limit(size). Limit(size).
Slice(&result). Slice(&result).
Desc("isPinned").
DescPk().
FindAll() FindAll()
return return
} }
@@ -625,10 +658,10 @@ func (this *NodeClusterDAO) FindClusterTOAConfig(tx *dbs.Tx, clusterId int64, ca
return nil, err return nil, err
} }
if !IsNotNull([]byte(toa)) { if !IsNotNull([]byte(toa)) {
return nodeconfigs.DefaultTOAConfig(), nil return nodeconfigs.NewTOAConfig(), nil
} }
config := &nodeconfigs.TOAConfig{} var config = nodeconfigs.NewTOAConfig()
err = json.Unmarshal([]byte(toa), config) err = json.Unmarshal([]byte(toa), config)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -653,7 +686,7 @@ func (this *NodeClusterDAO) UpdateClusterTOA(tx *dbs.Tx, clusterId int64, toaJSO
if err != nil { if err != nil {
return err return err
} }
return this.NotifyUpdate(tx, clusterId) return this.NotifyTOAUpdate(tx, clusterId)
} }
// CountAllEnabledNodeClustersWithHTTPCachePolicyId 计算使用某个缓存策略的集群数量 // CountAllEnabledNodeClustersWithHTTPCachePolicyId 计算使用某个缓存策略的集群数量
@@ -928,11 +961,12 @@ func (this *NodeClusterDAO) GenUniqueId(tx *dbs.Tx) (string, error) {
// FindLatestNodeClusters 查询最近访问的集群 // FindLatestNodeClusters 查询最近访问的集群
func (this *NodeClusterDAO) FindLatestNodeClusters(tx *dbs.Tx, size int64) (result []*NodeCluster, err error) { func (this *NodeClusterDAO) FindLatestNodeClusters(tx *dbs.Tx, size int64) (result []*NodeCluster, err error) {
itemTable := SharedLatestItemDAO.Table var itemTable = SharedLatestItemDAO.Table
itemType := LatestItemTypeCluster var itemType = LatestItemTypeCluster
_, err = this.Query(tx). _, err = this.Query(tx).
Result(this.Table+".id", this.Table+".name"). Result(this.Table+".id", this.Table+".name").
Join(SharedLatestItemDAO, dbs.QueryJoinRight, this.Table+".id="+itemTable+".itemId AND "+itemTable+".itemType='"+itemType+"'"). Join(SharedLatestItemDAO, dbs.QueryJoinRight, this.Table+".id="+itemTable+".itemId AND "+itemTable+".itemType='"+itemType+"'").
Where(itemTable + ".updatedAt<=UNIX_TIMESTAMP()"). // VERY IMPORTANT
Asc("CEIL((UNIX_TIMESTAMP() - " + itemTable + ".updatedAt) / (7 * 86400))"). // 优先一个星期以内的 Asc("CEIL((UNIX_TIMESTAMP() - " + itemTable + ".updatedAt) / (7 * 86400))"). // 优先一个星期以内的
Desc(itemTable + ".count"). Desc(itemTable + ".count").
State(NodeClusterStateEnabled). State(NodeClusterStateEnabled).
@@ -996,7 +1030,7 @@ func (this *NodeClusterDAO) FindClusterBasicInfo(tx *dbs.Tx, clusterId int64, ca
cluster, err := this.Query(tx). cluster, err := this.Query(tx).
Pk(clusterId). Pk(clusterId).
State(NodeClusterStateEnabled). State(NodeClusterStateEnabled).
Result("id", "name", "timeZone", "nodeMaxThreads", "cachePolicyId", "httpFirewallPolicyId", "autoOpenPorts", "webp", "uam", "cc", "httpPages", "http3", "isOn", "ddosProtection", "clock", "globalServerConfig", "autoInstallNftables"). Result("id", "name", "timeZone", "nodeMaxThreads", "cachePolicyId", "httpFirewallPolicyId", "autoOpenPorts", "webp", "uam", "cc", "httpPages", "http3", "isOn", "ddosProtection", "clock", "globalServerConfig", "autoInstallNftables", "autoSystemTuning", "networkSecurity", "autoTrimDisks", "secret").
Find() Find()
if err != nil || cluster == nil { if err != nil || cluster == nil {
return nil, err return nil, err
@@ -1018,7 +1052,7 @@ func (this *NodeClusterDAO) UpdateClusterWebPPolicy(tx *dbs.Tx, clusterId int64,
return err return err
} }
return this.NotifyUpdate(tx, clusterId) return this.NotifyWebPPolicyUpdate(tx, clusterId)
} }
webpPolicyJSON, err := json.Marshal(webpPolicy) webpPolicyJSON, err := json.Marshal(webpPolicy)
@@ -1033,7 +1067,7 @@ func (this *NodeClusterDAO) UpdateClusterWebPPolicy(tx *dbs.Tx, clusterId int64,
return err return err
} }
return this.NotifyUpdate(tx, clusterId) return this.NotifyWebPPolicyUpdate(tx, clusterId)
} }
// FindClusterWebPPolicy 查询WebP设置 // FindClusterWebPPolicy 查询WebP设置
@@ -1058,7 +1092,7 @@ func (this *NodeClusterDAO) FindClusterWebPPolicy(tx *dbs.Tx, clusterId int64, c
return nodeconfigs.DefaultWebPImagePolicy, nil return nodeconfigs.DefaultWebPImagePolicy, nil
} }
var policy = &nodeconfigs.WebPImagePolicy{} var policy = nodeconfigs.NewWebPImagePolicy()
err = json.Unmarshal(webpJSON, policy) err = json.Unmarshal(webpJSON, policy)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -1113,11 +1147,12 @@ func (this *NodeClusterDAO) FindClusterUAMPolicy(tx *dbs.Tx, clusterId int64, ca
return nil, err return nil, err
} }
var policy = nodeconfigs.NewUAMPolicy()
if IsNull(uamJSON) { if IsNull(uamJSON) {
return nodeconfigs.DefaultUAMPolicy, nil return policy, nil
} }
var policy = &nodeconfigs.UAMPolicy{}
err = json.Unmarshal(uamJSON, policy) err = json.Unmarshal(uamJSON, policy)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -1243,6 +1278,57 @@ func (this *NodeClusterDAO) FindClusterHTTP3Policy(tx *dbs.Tx, clusterId int64,
return policy, nil return policy, nil
} }
// UpdateClusterNetworkSecurityPolicy 修改网络安全策略设置
func (this *NodeClusterDAO) UpdateClusterNetworkSecurityPolicy(tx *dbs.Tx, clusterId int64, networkSecurityPolicy *nodeconfigs.NetworkSecurityPolicy) error {
if networkSecurityPolicy == nil {
networkSecurityPolicy = nodeconfigs.NewNetworkSecurityPolicy()
}
networkSecurityPolicyJSON, err := json.Marshal(networkSecurityPolicy)
if err != nil {
return err
}
err = this.Query(tx).
Pk(clusterId).
Set("networkSecurity", networkSecurityPolicyJSON).
UpdateQuickly()
if err != nil {
return err
}
return this.NotifyNetworkSecurityUpdate(tx, clusterId)
}
// FindClusterNetworkSecurityPolicy 查询网络安全策略设置
func (this *NodeClusterDAO) FindClusterNetworkSecurityPolicy(tx *dbs.Tx, clusterId int64, cacheMap *utils.CacheMap) (*nodeconfigs.NetworkSecurityPolicy, error) {
var cacheKey = this.Table + ":FindClusterNetworkSecurityPolicy:" + types.String(clusterId)
if cacheMap != nil {
cache, ok := cacheMap.Get(cacheKey)
if ok {
return cache.(*nodeconfigs.NetworkSecurityPolicy), nil
}
}
networkSecurityPolicyJSON, err := this.Query(tx).
Pk(clusterId).
Result("networkSecurity").
FindJSONCol()
if err != nil {
return nil, err
}
if IsNull(networkSecurityPolicyJSON) {
return nodeconfigs.NewNetworkSecurityPolicy(), nil
}
var policy = nodeconfigs.NewNetworkSecurityPolicy()
err = json.Unmarshal(networkSecurityPolicyJSON, policy)
if err != nil {
return nil, err
}
return policy, nil
}
// UpdateClusterHTTPPagesPolicy 修改自定义页面设置 // UpdateClusterHTTPPagesPolicy 修改自定义页面设置
func (this *NodeClusterDAO) UpdateClusterHTTPPagesPolicy(tx *dbs.Tx, clusterId int64, httpPagesPolicy *nodeconfigs.HTTPPagesPolicy) error { func (this *NodeClusterDAO) UpdateClusterHTTPPagesPolicy(tx *dbs.Tx, clusterId int64, httpPagesPolicy *nodeconfigs.HTTPPagesPolicy) error {
if httpPagesPolicy == nil { if httpPagesPolicy == nil {
@@ -1373,7 +1459,7 @@ func (this *NodeClusterDAO) FindClusterGlobalServerConfig(tx *dbs.Tx, clusterId
return nil, err return nil, err
} }
var config = serverconfigs.DefaultGlobalServerConfig() var config = serverconfigs.NewGlobalServerConfig()
if IsNull(configJSON) { if IsNull(configJSON) {
return config, nil return config, nil
} }
@@ -1389,7 +1475,7 @@ func (this *NodeClusterDAO) FindClusterGlobalServerConfig(tx *dbs.Tx, clusterId
// UpdateClusterGlobalServerConfig 修改全局服务配置 // UpdateClusterGlobalServerConfig 修改全局服务配置
func (this *NodeClusterDAO) UpdateClusterGlobalServerConfig(tx *dbs.Tx, clusterId int64, config *serverconfigs.GlobalServerConfig) error { func (this *NodeClusterDAO) UpdateClusterGlobalServerConfig(tx *dbs.Tx, clusterId int64, config *serverconfigs.GlobalServerConfig) error {
if config == nil { if config == nil {
config = serverconfigs.DefaultGlobalServerConfig() config = serverconfigs.NewGlobalServerConfig()
} }
configJSON, err := json.Marshal(config) configJSON, err := json.Marshal(config)
if err != nil { if err != nil {
@@ -1426,17 +1512,28 @@ func (this *NodeClusterDAO) NotifyHTTP3Update(tx *dbs.Tx, clusterId int64) error
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeHTTP3PolicyChanged) return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeHTTP3PolicyChanged)
} }
// NotifyNetworkSecurityUpdate 通知网络安全策略更新
func (this *NodeClusterDAO) NotifyNetworkSecurityUpdate(tx *dbs.Tx, clusterId int64) error {
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeNetworkSecurityPolicyChanged)
}
// NotifyHTTPPagesPolicyUpdate 通知HTTP Pages更新 // NotifyHTTPPagesPolicyUpdate 通知HTTP Pages更新
func (this *NodeClusterDAO) NotifyHTTPPagesPolicyUpdate(tx *dbs.Tx, clusterId int64) error { func (this *NodeClusterDAO) NotifyHTTPPagesPolicyUpdate(tx *dbs.Tx, clusterId int64) error {
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeHTTPPagesPolicyChanged) return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeHTTPPagesPolicyChanged)
} }
// NotifyTOAUpdate 通知TOA变化
func (this *NodeClusterDAO) NotifyTOAUpdate(tx *dbs.Tx, clusterId int64) error {
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeTOAChanged)
}
// NotifyWebPPolicyUpdate 通知WebP策略更新
func (this *NodeClusterDAO) NotifyWebPPolicyUpdate(tx *dbs.Tx, clusterId int64) error {
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeWebPPolicyChanged)
}
// NotifyDNSUpdate 通知DNS更新 // NotifyDNSUpdate 通知DNS更新
// TODO 更新新的DNS解析记录的同时需要删除老的DNS解析记录 // TODO 更新新的DNS解析记录的同时需要删除老的DNS解析记录
func (this *NodeClusterDAO) NotifyDNSUpdate(tx *dbs.Tx, clusterId int64) error { func (this *NodeClusterDAO) NotifyDNSUpdate(tx *dbs.Tx, clusterId int64) error {
err := dns.SharedDNSTaskDAO.CreateClusterTask(tx, clusterId, dns.DNSTaskTypeClusterChange) return dns.SharedDNSTaskDAO.CreateClusterTask(tx, clusterId, dns.DNSTaskTypeClusterChange)
if err != nil {
return err
}
return nil
} }

View File

@@ -2,6 +2,52 @@ package models
import "github.com/iwind/TeaGo/dbs" import "github.com/iwind/TeaGo/dbs"
const (
NodeClusterField_Id dbs.FieldName = "id" // ID
NodeClusterField_AdminId dbs.FieldName = "adminId" // 管理员ID
NodeClusterField_UserId dbs.FieldName = "userId" // 用户ID
NodeClusterField_IsOn dbs.FieldName = "isOn" // 是否启用
NodeClusterField_Name dbs.FieldName = "name" // 名称
NodeClusterField_UseAllAPINodes dbs.FieldName = "useAllAPINodes" // 是否使用所有API节点
NodeClusterField_ApiNodes dbs.FieldName = "apiNodes" // 使用的API节点
NodeClusterField_InstallDir dbs.FieldName = "installDir" // 安装目录
NodeClusterField_Order dbs.FieldName = "order" // 排序
NodeClusterField_CreatedAt dbs.FieldName = "createdAt" // 创建时间
NodeClusterField_GrantId dbs.FieldName = "grantId" // 默认认证方式
NodeClusterField_SshParams dbs.FieldName = "sshParams" // SSH默认参数
NodeClusterField_State dbs.FieldName = "state" // 状态
NodeClusterField_AutoRegister dbs.FieldName = "autoRegister" // 是否开启自动注册
NodeClusterField_UniqueId dbs.FieldName = "uniqueId" // 唯一ID
NodeClusterField_Secret dbs.FieldName = "secret" // 密钥
NodeClusterField_HealthCheck dbs.FieldName = "healthCheck" // 健康检查
NodeClusterField_DnsName dbs.FieldName = "dnsName" // DNS名称
NodeClusterField_DnsDomainId dbs.FieldName = "dnsDomainId" // 域名ID
NodeClusterField_Dns dbs.FieldName = "dns" // DNS配置
NodeClusterField_Toa dbs.FieldName = "toa" // TOA配置
NodeClusterField_CachePolicyId dbs.FieldName = "cachePolicyId" // 缓存策略ID
NodeClusterField_HttpFirewallPolicyId dbs.FieldName = "httpFirewallPolicyId" // WAF策略ID
NodeClusterField_AccessLog dbs.FieldName = "accessLog" // 访问日志设置
NodeClusterField_SystemServices dbs.FieldName = "systemServices" // 系统服务设置
NodeClusterField_TimeZone dbs.FieldName = "timeZone" // 时区
NodeClusterField_NodeMaxThreads dbs.FieldName = "nodeMaxThreads" // 节点最大线程数
NodeClusterField_DdosProtection dbs.FieldName = "ddosProtection" // DDoS防护设置
NodeClusterField_AutoOpenPorts dbs.FieldName = "autoOpenPorts" // 是否自动尝试开放端口
NodeClusterField_IsPinned dbs.FieldName = "isPinned" // 是否置顶
NodeClusterField_Webp dbs.FieldName = "webp" // WebP设置
NodeClusterField_Uam dbs.FieldName = "uam" // UAM设置
NodeClusterField_Clock dbs.FieldName = "clock" // 时钟配置
NodeClusterField_GlobalServerConfig dbs.FieldName = "globalServerConfig" // 全局服务配置
NodeClusterField_AutoRemoteStart dbs.FieldName = "autoRemoteStart" // 自动远程启动
NodeClusterField_AutoInstallNftables dbs.FieldName = "autoInstallNftables" // 自动安装nftables
NodeClusterField_IsAD dbs.FieldName = "isAD" // 是否为高防集群
NodeClusterField_HttpPages dbs.FieldName = "httpPages" // 自定义页面设置
NodeClusterField_Cc dbs.FieldName = "cc" // CC设置
NodeClusterField_Http3 dbs.FieldName = "http3" // HTTP3设置
NodeClusterField_AutoSystemTuning dbs.FieldName = "autoSystemTuning" // 是否自动调整系统参数
NodeClusterField_NetworkSecurity dbs.FieldName = "networkSecurity" // 网络安全策略
NodeClusterField_AutoTrimDisks dbs.FieldName = "autoTrimDisks" // 是否自动执行TRIM
)
// NodeCluster 节点集群 // NodeCluster 节点集群
type NodeCluster struct { type NodeCluster struct {
Id uint32 `field:"id"` // ID Id uint32 `field:"id"` // ID
@@ -44,6 +90,9 @@ type NodeCluster struct {
HttpPages dbs.JSON `field:"httpPages"` // 自定义页面设置 HttpPages dbs.JSON `field:"httpPages"` // 自定义页面设置
Cc dbs.JSON `field:"cc"` // CC设置 Cc dbs.JSON `field:"cc"` // CC设置
Http3 dbs.JSON `field:"http3"` // HTTP3设置 Http3 dbs.JSON `field:"http3"` // HTTP3设置
AutoSystemTuning bool `field:"autoSystemTuning"` // 是否自动调整系统参数
NetworkSecurity dbs.JSON `field:"networkSecurity"` // 网络安全策略
AutoTrimDisks bool `field:"autoTrimDisks"` // 是否自动执行TRIM
} }
type NodeClusterOperator struct { type NodeClusterOperator struct {
@@ -87,6 +136,9 @@ type NodeClusterOperator struct {
HttpPages any // 自定义页面设置 HttpPages any // 自定义页面设置
Cc any // CC设置 Cc any // CC设置
Http3 any // HTTP3设置 Http3 any // HTTP3设置
AutoSystemTuning any // 是否自动调整系统参数
NetworkSecurity any // 网络安全策略
AutoTrimDisks any // 是否自动执行TRIM
} }
func NewNodeClusterOperator() *NodeClusterOperator { func NewNodeClusterOperator() *NodeClusterOperator {

View File

@@ -37,7 +37,7 @@ func (this *NodeCluster) DecodeDDoSProtection() *ddosconfigs.ProtectionConfig {
return result return result
} }
// HasDDoSProtection 检查是否有DDOS设置 // HasDDoSProtection 检查是否有DDoS设置
func (this *NodeCluster) HasDDoSProtection() bool { func (this *NodeCluster) HasDDoSProtection() bool {
var config = this.DecodeDDoSProtection() var config = this.DecodeDDoSProtection()
if config != nil { if config != nil {
@@ -46,6 +46,27 @@ func (this *NodeCluster) HasDDoSProtection() bool {
return false return false
} }
// HasNetworkSecurityPolicy 检查是否有安全策略设置
func (this *NodeCluster) HasNetworkSecurityPolicy() bool {
var policy = this.DecodeNetworkSecurityPolicy()
if policy != nil {
return policy.IsOn()
}
return false
}
// DecodeNetworkSecurityPolicy 解析安全策略设置
func (this *NodeCluster) DecodeNetworkSecurityPolicy() *nodeconfigs.NetworkSecurityPolicy {
var policy = nodeconfigs.NewNetworkSecurityPolicy()
if IsNotNull(this.NetworkSecurity) {
err := json.Unmarshal(this.NetworkSecurity, policy)
if err != nil {
remotelogs.Error("NodeCluster.DecodeNetworkSecurityPolicy()", err.Error())
}
}
return policy
}
// DecodeClock 解析时钟配置 // DecodeClock 解析时钟配置
func (this *NodeCluster) DecodeClock() *nodeconfigs.ClockConfig { func (this *NodeCluster) DecodeClock() *nodeconfigs.ClockConfig {
var clock = nodeconfigs.DefaultClockConfig() var clock = nodeconfigs.DefaultClockConfig()
@@ -60,7 +81,7 @@ func (this *NodeCluster) DecodeClock() *nodeconfigs.ClockConfig {
// DecodeGlobalServerConfig 解析全局服务配置 // DecodeGlobalServerConfig 解析全局服务配置
func (this *NodeCluster) DecodeGlobalServerConfig() *serverconfigs.GlobalServerConfig { func (this *NodeCluster) DecodeGlobalServerConfig() *serverconfigs.GlobalServerConfig {
var config = serverconfigs.DefaultGlobalServerConfig() var config = serverconfigs.NewGlobalServerConfig()
if IsNotNull(this.GlobalServerConfig) { if IsNotNull(this.GlobalServerConfig) {
err := json.Unmarshal(this.GlobalServerConfig, config) err := json.Unmarshal(this.GlobalServerConfig, config)
if err != nil { if err != nil {

View File

@@ -18,7 +18,6 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ddosconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ddosconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/dbs"
@@ -27,6 +26,7 @@ import (
"github.com/iwind/TeaGo/rands" "github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types" "github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time" timeutil "github.com/iwind/TeaGo/utils/time"
"sort"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@@ -882,9 +882,28 @@ func (this *NodeDAO) FindNodeStatus(tx *dbs.Tx, nodeId int64) (*nodeconfigs.Node
return status, nil return status, nil
} }
// UpdateNodeIsOn 修改节点启用状态
func (this *NodeDAO) UpdateNodeIsOn(tx *dbs.Tx, nodeId int64, isOn bool) error {
if nodeId <= 0 {
return errors.New("invalid nodeId")
}
err := this.Query(tx).
Pk(nodeId).
Set("isOn", isOn).
UpdateQuickly()
if err != nil {
return err
}
return this.NotifyDNSUpdate(tx, nodeId)
}
// UpdateNodeIsActive 更改节点在线状态 // UpdateNodeIsActive 更改节点在线状态
func (this *NodeDAO) UpdateNodeIsActive(tx *dbs.Tx, nodeId int64, isActive bool) error { func (this *NodeDAO) UpdateNodeIsActive(tx *dbs.Tx, nodeId int64, isActive bool) error {
b := "true" if nodeId <= 0 {
return errors.New("invalid nodeId")
}
var b = "true"
if !isActive { if !isActive {
b = "false" b = "false"
} }
@@ -898,6 +917,9 @@ func (this *NodeDAO) UpdateNodeIsActive(tx *dbs.Tx, nodeId int64, isActive bool)
// UpdateNodeIsInstalled 设置节点安装状态 // UpdateNodeIsInstalled 设置节点安装状态
func (this *NodeDAO) UpdateNodeIsInstalled(tx *dbs.Tx, nodeId int64, isInstalled bool) error { func (this *NodeDAO) UpdateNodeIsInstalled(tx *dbs.Tx, nodeId int64, isInstalled bool) error {
if nodeId <= 0 {
return errors.New("invalid nodeId")
}
_, err := this.Query(tx). _, err := this.Query(tx).
Pk(nodeId). Pk(nodeId).
Set("isInstalled", isInstalled). Set("isInstalled", isInstalled).
@@ -1018,6 +1040,13 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, dataMap *shared
} }
config.AllowedIPs = append(config.AllowedIPs, apiNodeIPs...) config.AllowedIPs = append(config.AllowedIPs, apiNodeIPs...)
// 当前的节点IP地址
nodeNodeIPs, err := SharedNodeIPAddressDAO.FindAllEnabledAddressStringsWithNode(tx, nodeId, nodeconfigs.NodeRoleNode)
if err != nil {
return nil, err
}
config.IPAddresses = nodeNodeIPs
// 所属集群 // 所属集群
var primaryClusterId = int64(node.ClusterId) var primaryClusterId = int64(node.ClusterId)
var clusterIds = []int64{primaryClusterId} var clusterIds = []int64{primaryClusterId}
@@ -1039,9 +1068,7 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, dataMap *shared
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, clusterServer := range clusterServers { servers = append(servers, clusterServers...)
servers = append(servers, clusterServer)
}
} }
for _, server := range servers { for _, server := range servers {
@@ -1059,36 +1086,15 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, dataMap *shared
} }
} }
// 全局设置
// TODO 根据用户的不同读取不同的全局设置
var settingCacheKey = "SharedSysSettingDAO:" + systemconfigs.SettingCodeServerGlobalConfig
settingJSONCache, ok := cacheMap.Get(settingCacheKey)
var settingJSON = []byte{}
if ok {
settingJSON = settingJSONCache.([]byte)
} else {
settingJSON, err = SharedSysSettingDAO.ReadSetting(tx, systemconfigs.SettingCodeServerGlobalConfig)
if err != nil {
return nil, err
}
cacheMap.Put(settingCacheKey, settingJSON)
}
if len(settingJSON) > 0 {
globalConfig := &serverconfigs.GlobalConfig{}
err = json.Unmarshal(settingJSON, globalConfig)
if err != nil {
return nil, err
}
config.GlobalConfig = globalConfig
}
var clusterIndex = 0 var clusterIndex = 0
config.WebPImagePolicies = map[int64]*nodeconfigs.WebPImagePolicy{} config.WebPImagePolicies = map[int64]*nodeconfigs.WebPImagePolicy{}
config.UAMPolicies = map[int64]*nodeconfigs.UAMPolicy{} config.UAMPolicies = map[int64]*nodeconfigs.UAMPolicy{}
config.HTTPCCPolicies = map[int64]*nodeconfigs.HTTPCCPolicy{} config.HTTPCCPolicies = map[int64]*nodeconfigs.HTTPCCPolicy{}
config.HTTP3Policies = map[int64]*nodeconfigs.HTTP3Policy{} config.HTTP3Policies = map[int64]*nodeconfigs.HTTP3Policy{}
config.HTTPPagesPolicies = map[int64]*nodeconfigs.HTTPPagesPolicy{} config.HTTPPagesPolicies = map[int64]*nodeconfigs.HTTPPagesPolicy{}
var cachePolicyIds = []int64{}
var allowIPMaps = map[string]bool{} var allowIPMaps = map[string]bool{}
for _, clusterId := range clusterIds { for _, clusterId := range clusterIds {
nodeCluster, err := SharedNodeClusterDAO.FindClusterBasicInfo(tx, clusterId, cacheMap) nodeCluster, err := SharedNodeClusterDAO.FindClusterBasicInfo(tx, clusterId, cacheMap)
@@ -1099,7 +1105,12 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, dataMap *shared
continue continue
} }
// 节点IP地址 // 集群密钥
if len(config.ClusterSecret) == 0 {
config.ClusterSecret = nodeCluster.Secret
}
// 所有节点IP地址
nodeIPAddresses, err := SharedNodeIPAddressDAO.FindAllAccessibleIPAddressesWithClusterId(tx, nodeconfigs.NodeRoleNode, clusterId, cacheMap) nodeIPAddresses, err := SharedNodeIPAddressDAO.FindAllAccessibleIPAddressesWithClusterId(tx, nodeconfigs.NodeRoleNode, clusterId, cacheMap)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -1116,7 +1127,7 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, dataMap *shared
// 防火墙 // 防火墙
var httpFirewallPolicyId = int64(nodeCluster.HttpFirewallPolicyId) var httpFirewallPolicyId = int64(nodeCluster.HttpFirewallPolicyId)
if httpFirewallPolicyId > 0 { if httpFirewallPolicyId > 0 {
firewallPolicy, err := SharedHTTPFirewallPolicyDAO.ComposeFirewallPolicy(tx, httpFirewallPolicyId, cacheMap) firewallPolicy, err := SharedHTTPFirewallPolicyDAO.ComposeFirewallPolicy(tx, httpFirewallPolicyId, true, cacheMap)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -1128,12 +1139,15 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, dataMap *shared
// 缓存策略 // 缓存策略
var httpCachePolicyId = int64(nodeCluster.CachePolicyId) var httpCachePolicyId = int64(nodeCluster.CachePolicyId)
if httpCachePolicyId > 0 { if httpCachePolicyId > 0 {
cachePolicy, err := SharedHTTPCachePolicyDAO.ComposeCachePolicy(tx, httpCachePolicyId, cacheMap) if !lists.ContainsInt64(cachePolicyIds, httpCachePolicyId) {
if err != nil { cachePolicyIds = append(cachePolicyIds, httpCachePolicyId)
return nil, err cachePolicy, err := SharedHTTPCachePolicyDAO.ComposeCachePolicy(tx, httpCachePolicyId, cacheMap)
} if err != nil {
if cachePolicy != nil { return nil, err
config.HTTPCachePolicies = append(config.HTTPCachePolicies, cachePolicy) }
if cachePolicy != nil {
config.HTTPCachePolicies = append(config.HTTPCachePolicies, cachePolicy)
}
} }
} }
@@ -1164,7 +1178,7 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, dataMap *shared
// webp // webp
if IsNotNull(nodeCluster.Webp) { if IsNotNull(nodeCluster.Webp) {
var webpPolicy = &nodeconfigs.WebPImagePolicy{} var webpPolicy = nodeconfigs.NewWebPImagePolicy()
err = json.Unmarshal(nodeCluster.Webp, webpPolicy) err = json.Unmarshal(nodeCluster.Webp, webpPolicy)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -1174,7 +1188,7 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, dataMap *shared
// UAM // UAM
if IsNotNull(nodeCluster.Uam) { if IsNotNull(nodeCluster.Uam) {
var uamPolicy = &nodeconfigs.UAMPolicy{} var uamPolicy = nodeconfigs.NewUAMPolicy()
err = json.Unmarshal(nodeCluster.Uam, uamPolicy) err = json.Unmarshal(nodeCluster.Uam, uamPolicy)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -1235,9 +1249,16 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, dataMap *shared
} }
} }
// 自动安装nftables // 自动安装nftables等集群配置
if clusterIndex == 0 { if clusterIndex == 0 {
config.AutoInstallNftables = nodeCluster.AutoInstallNftables config.AutoInstallNftables = nodeCluster.AutoInstallNftables
config.AutoSystemTuning = nodeCluster.AutoSystemTuning
config.AutoTrimDisks = nodeCluster.AutoTrimDisks
}
// 安全设置
if clusterIndex == 0 {
config.NetworkSecurityPolicy = nodeCluster.DecodeNetworkSecurityPolicy()
} }
clusterIndex++ clusterIndex++
@@ -2118,12 +2139,18 @@ func (this *NodeDAO) FindParentNodeConfigs(tx *dbs.Tx, nodeId int64, groupId int
var secretHash = fmt.Sprintf("%x", sha256.Sum256([]byte(node.UniqueId+"@"+node.Secret))) var secretHash = fmt.Sprintf("%x", sha256.Sum256([]byte(node.UniqueId+"@"+node.Secret)))
for _, clusterId := range node.AllClusterIds() { for _, clusterId := range node.AllClusterIds() {
parentNodeConfigs, _ := result[clusterId] var parentNodeConfigs = result[clusterId]
parentNodeConfigs = append(parentNodeConfigs, &nodeconfigs.ParentNodeConfig{ parentNodeConfigs = append(parentNodeConfigs, &nodeconfigs.ParentNodeConfig{
Id: int64(node.Id), Id: int64(node.Id),
Addrs: addrStrings, Addrs: addrStrings,
SecretHash: secretHash, SecretHash: secretHash,
}) })
// 排序
sort.Slice(parentNodeConfigs, func(i, j int) bool {
return parentNodeConfigs[i].Id < parentNodeConfigs[j].Id
})
result[clusterId] = parentNodeConfigs result[clusterId] = parentNodeConfigs
} }
} }

View File

@@ -4,7 +4,10 @@
package models package models
import ( import (
"errors"
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
"github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/types"
) )
func (this *NodeDAO) CountAllAuthorityNodes(tx *dbs.Tx) (int64, error) { func (this *NodeDAO) CountAllAuthorityNodes(tx *dbs.Tx) (int64, error) {
@@ -15,5 +18,18 @@ func (this *NodeDAO) CountAllAuthorityNodes(tx *dbs.Tx) (int64, error) {
} }
func (this *NodeDAO) CheckNodesLimit(tx *dbs.Tx) error { func (this *NodeDAO) CheckNodesLimit(tx *dbs.Tx) error {
var maxNodes = teaconst.DefaultMaxNodes
// 检查节点数量
if maxNodes > 0 {
count, err := this.CountAllAuthorityNodes(tx)
if err != nil {
return err
}
if count >= int64(maxNodes) {
return errors.New("超出最大节点数限制:" + types.String(maxNodes) + ",当前已用:" + types.String(count) + "请自行修改源码修改此限制EdgeAPI/internal/const/const_community.go 或者 购买商业版本授权。")
}
}
return nil return nil
} }

View File

@@ -89,7 +89,9 @@ func (this *NodeGrantDAO) CreateGrant(tx *dbs.Tx, adminId int64, name string, me
op.PrivateKey = privateKey op.PrivateKey = privateKey
op.Passphrase = passphrase op.Passphrase = passphrase
} }
op.Su = su if username != "root" { // only for non-root user
op.Su = su
}
op.Description = description op.Description = description
op.NodeId = nodeId op.NodeId = nodeId
op.State = NodeGrantStateEnabled op.State = NodeGrantStateEnabled
@@ -117,7 +119,11 @@ func (this *NodeGrantDAO) UpdateGrant(tx *dbs.Tx, grantId int64, name string, me
op.PrivateKey = privateKey op.PrivateKey = privateKey
op.Passphrase = passphrase op.Passphrase = passphrase
} }
op.Su = su if username != "root" { // only for non-root user
op.Su = su
} else {
op.Su = false
}
op.Description = description op.Description = description
op.NodeId = nodeId op.NodeId = nodeId
err := this.Save(tx, op) err := this.Save(tx, op)

View File

@@ -256,6 +256,32 @@ func (this *NodeIPAddressDAO) FindAllEnabledAddressesWithNode(tx *dbs.Tx, nodeId
return return
} }
// FindAllEnabledAddressStringsWithNode 查找节点的所有的IP地址地府传
func (this *NodeIPAddressDAO) FindAllEnabledAddressStringsWithNode(tx *dbs.Tx, nodeId int64, role nodeconfigs.NodeRole) (result []string, err error) {
if len(role) == 0 {
role = nodeconfigs.NodeRoleNode
}
ones, err := this.Query(tx).
Attr("nodeId", nodeId).
Attr("role", role).
State(NodeIPAddressStateEnabled).
Result("ip", "backupIP").
FindAll()
if err != nil {
return nil, err
}
for _, one := range ones {
var addr = one.(*NodeIPAddress)
result = append(result, addr.Ip)
if len(addr.BackupIP) > 0 {
result = append(result, addr.BackupIP)
}
}
return
}
// FindFirstNodeAccessIPAddress 查找节点的第一个可访问的IP地址 // FindFirstNodeAccessIPAddress 查找节点的第一个可访问的IP地址
func (this *NodeIPAddressDAO) FindFirstNodeAccessIPAddress(tx *dbs.Tx, nodeId int64, mustUp bool, role nodeconfigs.NodeRole) (ip string, addrId int64, err error) { func (this *NodeIPAddressDAO) FindFirstNodeAccessIPAddress(tx *dbs.Tx, nodeId int64, mustUp bool, role nodeconfigs.NodeRole) (ip string, addrId int64, err error) {
if len(role) == 0 { if len(role) == 0 {

View File

@@ -70,8 +70,7 @@ func (this *Node) DNSRouteCodesForDomainId(dnsDomainId int64) ([]string, error)
if err != nil { if err != nil {
return nil, err return nil, err
} }
domainRoutes, _ := routes[dnsDomainId] var domainRoutes = routes[dnsDomainId]
if len(domainRoutes) > 0 { if len(domainRoutes) > 0 {
sort.Strings(domainRoutes) sort.Strings(domainRoutes)
} }

View File

@@ -8,6 +8,7 @@ import (
"github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps" "github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types" "github.com/iwind/TeaGo/types"
"strings"
"time" "time"
) )
@@ -16,19 +17,24 @@ type NodeTaskType = string
const ( const (
// CDN相关 // CDN相关
NodeTaskTypeConfigChanged NodeTaskType = "configChanged" // 节点整体配置变化 NodeTaskTypeConfigChanged NodeTaskType = "configChanged" // 节点整体配置变化
NodeTaskTypeDDosProtectionChanged NodeTaskType = "ddosProtectionChanged" // 节点DDoS配置变更 NodeTaskTypeDDosProtectionChanged NodeTaskType = "ddosProtectionChanged" // 节点DDoS配置变更
NodeTaskTypeGlobalServerConfigChanged NodeTaskType = "globalServerConfigChanged" // 全局服务设置变化 NodeTaskTypeGlobalServerConfigChanged NodeTaskType = "globalServerConfigChanged" // 全局服务设置变化
NodeTaskTypeIPItemChanged NodeTaskType = "ipItemChanged" // IP条目变更 NodeTaskTypeIPListDeleted NodeTaskType = "ipListDeleted" // IPList被删除
NodeTaskTypeNodeVersionChanged NodeTaskType = "nodeVersionChanged" // 节点版本变化 NodeTaskTypeIPItemChanged NodeTaskType = "ipItemChanged" // IP条目变更
NodeTaskTypeScriptsChanged NodeTaskType = "scriptsChanged" // 脚本配置变化 NodeTaskTypeNodeVersionChanged NodeTaskType = "nodeVersionChanged" // 节点版本变化
NodeTaskTypeNodeLevelChanged NodeTaskType = "nodeLevelChanged" // 节点级别变化 NodeTaskTypeScriptsChanged NodeTaskType = "scriptsChanged" // 脚本配置变化
NodeTaskTypeUserServersStateChanged NodeTaskType = "userServersStateChanged" // 用户服务状态变化 NodeTaskTypeNodeLevelChanged NodeTaskType = "nodeLevelChanged" // 节点级别变化
NodeTaskTypeUAMPolicyChanged NodeTaskType = "uamPolicyChanged" // UAM策略变化 NodeTaskTypeUserServersStateChanged NodeTaskType = "userServersStateChanged" // 用户服务状态变化
NodeTaskTypeHTTPPagesPolicyChanged NodeTaskType = "httpPagesPolicyChanged" // 自定义页面变化 NodeTaskTypeUAMPolicyChanged NodeTaskType = "uamPolicyChanged" // UAM策略变化
NodeTaskTypeHTTPCCPolicyChanged NodeTaskType = "httpCCPolicyChanged" // CC策略变化 NodeTaskTypeHTTPPagesPolicyChanged NodeTaskType = "httpPagesPolicyChanged" // 自定义页面变化
NodeTaskTypeHTTP3PolicyChanged NodeTaskType = "http3PolicyChanged" // HTTP3策略变化 NodeTaskTypeHTTPCCPolicyChanged NodeTaskType = "httpCCPolicyChanged" // CC策略变化
NodeTaskTypeUpdatingServers NodeTaskType = "updatingServers" // 更新一组服务 NodeTaskTypeHTTP3PolicyChanged NodeTaskType = "http3PolicyChanged" // HTTP3策略变化
NodeTaskTypeNetworkSecurityPolicyChanged NodeTaskType = "networkSecurityPolicyChanged" // 网络安全策略变化
NodeTaskTypeWebPPolicyChanged NodeTaskType = "webPPolicyChanged" // WebP策略变化
NodeTaskTypeUpdatingServers NodeTaskType = "updatingServers" // 更新一组服务
NodeTaskTypeTOAChanged NodeTaskType = "toaChanged" // TOA配置变化
NodeTaskTypePlanChanged NodeTaskType = "planChanged" // 套餐变化
// NS相关 // NS相关
@@ -233,6 +239,12 @@ func (this *NodeTaskDAO) DeleteNodeTasks(tx *dbs.Tx, role string, nodeId int64)
return err return err
} }
// DeleteAllNodeTasks 删除所有节点相关任务
func (this *NodeTaskDAO) DeleteAllNodeTasks(tx *dbs.Tx) error {
return this.Query(tx).
DeleteQuickly()
}
// FindDoingNodeTasks 查询一个节点的所有任务 // FindDoingNodeTasks 查询一个节点的所有任务
func (this *NodeTaskDAO) FindDoingNodeTasks(tx *dbs.Tx, role string, nodeId int64, version int64) (result []*NodeTask, err error) { func (this *NodeTaskDAO) FindDoingNodeTasks(tx *dbs.Tx, role string, nodeId int64, version int64) (result []*NodeTask, err error) {
if nodeId <= 0 { if nodeId <= 0 {
@@ -258,6 +270,23 @@ func (this *NodeTaskDAO) FindDoingNodeTasks(tx *dbs.Tx, role string, nodeId int6
// UpdateNodeTaskDone 修改节点任务的完成状态 // UpdateNodeTaskDone 修改节点任务的完成状态
func (this *NodeTaskDAO) UpdateNodeTaskDone(tx *dbs.Tx, taskId int64, isOk bool, errorMessage string) error { func (this *NodeTaskDAO) UpdateNodeTaskDone(tx *dbs.Tx, taskId int64, isOk bool, errorMessage string) error {
if isOk {
// 特殊任务删除
taskType, err := this.Query(tx).
Pk(taskId).
Result("type").
FindStringCol("")
if err != nil {
return err
}
if strings.HasPrefix(taskType, NodeTaskTypeIPListDeleted+"@") {
return this.Query(tx).
Pk(taskId).
DeleteQuickly()
}
}
// 其他任务标记为完成
var query = this.Query(tx). var query = this.Query(tx).
Pk(taskId) Pk(taskId)
if !isOk { if !isOk {
@@ -267,8 +296,9 @@ func (this *NodeTaskDAO) UpdateNodeTaskDone(tx *dbs.Tx, taskId int64, isOk bool,
} }
query.Set("version", version) query.Set("version", version)
} }
_, err := query. _, err := query.
Set("isDone", 1). Set("isDone", true).
Set("isOk", isOk). Set("isOk", isOk).
Set("error", errorMessage). Set("error", errorMessage).
Update() Update()

View File

@@ -54,3 +54,12 @@ func TestNodeTaskDAO_FindDoingNodeTasks(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestNodeTaskDAO_UpdateNodeTaskDone(t *testing.T) {
var tx *dbs.Tx
var dao = models.NewNodeTaskDAO()
err := dao.UpdateNodeTaskDone(tx, 1741, true, "")
if err != nil {
t.Fatal(err)
}
}

View File

@@ -1,17 +1,11 @@
package models package models
import ( import (
"fmt"
"github.com/TeaOSLab/EdgeAPI/internal/errors" "github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"strings"
"time"
) )
const ( const (
@@ -153,12 +147,13 @@ func (this *NodeThresholdDAO) FindAllEnabledAndOnClusterThresholds(tx *dbs.Tx, r
} }
// FindAllEnabledAndOnNodeThresholds 查询节点专属的阈值设置 // FindAllEnabledAndOnNodeThresholds 查询节点专属的阈值设置
func (this *NodeThresholdDAO) FindAllEnabledAndOnNodeThresholds(tx *dbs.Tx, role string, nodeId int64, item string) (result []*NodeThreshold, err error) { func (this *NodeThresholdDAO) FindAllEnabledAndOnNodeThresholds(tx *dbs.Tx, role string, clusterId int64, nodeId int64, item string) (result []*NodeThreshold, err error) {
if nodeId <= 0 { if clusterId <= 0 || nodeId <= 0 {
return return
} }
_, err = this.Query(tx). _, err = this.Query(tx).
Attr("role", role). Attr("role", role).
Attr("clusterId", clusterId).
Attr("nodeId", nodeId). Attr("nodeId", nodeId).
Attr("item", item). Attr("item", item).
Attr("isOn", true). Attr("isOn", true).
@@ -186,87 +181,3 @@ func (this *NodeThresholdDAO) CountAllEnabledThresholds(tx *dbs.Tx, role string,
query.State(NodeThresholdStateEnabled) query.State(NodeThresholdStateEnabled)
return query.Count() return query.Count()
} }
// FireNodeThreshold 触发相关阈值设置
func (this *NodeThresholdDAO) FireNodeThreshold(tx *dbs.Tx, role string, nodeId int64, item string) error {
clusterId, err := SharedNodeDAO.FindNodeClusterId(tx, nodeId)
if err != nil {
return err
}
if clusterId == 0 {
return nil
}
// 集群相关阈值
var thresholds []*NodeThreshold
{
clusterThresholds, err := this.FindAllEnabledAndOnClusterThresholds(tx, role, clusterId, item)
if err != nil {
return err
}
thresholds = append(thresholds, clusterThresholds...)
}
// 节点相关阈值
{
nodeThresholds, err := this.FindAllEnabledAndOnNodeThresholds(tx, role, nodeId, item)
if err != nil {
return err
}
thresholds = append(thresholds, nodeThresholds...)
}
if len(thresholds) > 0 {
for _, threshold := range thresholds {
if len(threshold.Param) == 0 || threshold.Duration <= 0 {
continue
}
paramValue, err := SharedNodeValueDAO.SumNodeValues(tx, role, nodeId, item, threshold.Param, threshold.SumMethod, types.Int32(threshold.Duration), threshold.DurationUnit)
if err != nil {
return err
}
originValue := nodeconfigs.UnmarshalNodeValue(threshold.Value)
thresholdValue := types.Float64(originValue)
isMatched := nodeconfigs.CompareNodeValue(threshold.Operator, paramValue, thresholdValue)
if isMatched {
// TODO 执行其他动作
// 是否已经通知过
if threshold.NotifyDuration > 0 && threshold.NotifiedAt > 0 && time.Now().Unix()-int64(threshold.NotifiedAt) < int64(threshold.NotifyDuration*60) {
continue
}
// 创建消息
nodeName, err := SharedNodeDAO.FindNodeName(tx, nodeId)
if err != nil {
return err
}
itemName := nodeconfigs.FindNodeValueItemName(threshold.Item)
paramName := nodeconfigs.FindNodeValueItemParamName(threshold.Item, threshold.Param)
operatorName := nodeconfigs.FindNodeValueOperatorName(threshold.Operator)
subject := "节点 \"" + nodeName + "\" " + itemName + " 达到阈值"
body := "节点 \"" + nodeName + "\" " + itemName + " 达到阈值\n阈值设置" + paramName + " " + operatorName + " " + originValue + "\n当前值" + fmt.Sprintf("%.2f", paramValue) + "\n触发时间" + timeutil.Format("Y-m-d H:i:s")
if len(threshold.Message) > 0 {
body = threshold.Message
body = strings.Replace(body, "${item.name}", itemName, -1)
body = strings.Replace(body, "${value}", fmt.Sprintf("%.2f", paramValue), -1)
}
err = SharedMessageDAO.CreateNodeMessage(tx, role, clusterId, nodeId, MessageTypeThresholdSatisfied, MessageLevelWarning, subject, body, maps.Map{}.AsJSON(), true)
if err != nil {
return err
}
// 设置通知时间
_, err = this.Query(tx).
Pk(threshold.Id).
Set("notifiedAt", time.Now().Unix()).
Update()
if err != nil {
return err
}
}
}
}
return nil
}

View File

@@ -0,0 +1,12 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package models
import "github.com/iwind/TeaGo/dbs"
// FireNodeThreshold 触发相关阈值设置
func (this *NodeThresholdDAO) FireNodeThreshold(tx *dbs.Tx, role string, nodeId int64, item string) error {
// stub
return nil
}

View File

@@ -22,7 +22,7 @@ func init() {
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour) var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() { goman.New(func() {
for range ticker.C { for range ticker.C {
err := SharedNodeTrafficDailyStatDAO.Clean(nil, 30) // 只保留N天 err := SharedNodeTrafficDailyStatDAO.CleanDefaultDays(nil, 32) // 只保留N天
if err != nil { if err != nil {
remotelogs.Error("NodeTrafficDailyStatDAO", "clean expired data failed: "+err.Error()) remotelogs.Error("NodeTrafficDailyStatDAO", "clean expired data failed: "+err.Error())
} }
@@ -134,11 +134,27 @@ func (this *NodeTrafficDailyStatDAO) SumDailyStat(tx *dbs.Tx, role string, nodeI
return one.(*NodeTrafficDailyStat), nil return one.(*NodeTrafficDailyStat), nil
} }
// Clean 清理历史数据 // CleanDays 清理历史数据
func (this *NodeTrafficDailyStatDAO) Clean(tx *dbs.Tx, days int) error { func (this *NodeTrafficDailyStatDAO) CleanDays(tx *dbs.Tx, days int) error {
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days)) var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days))
_, err := this.Query(tx). _, err := this.Query(tx).
Lt("day", day). Lt("day", day).
Delete() Delete()
return err return err
} }
func (this *NodeTrafficDailyStatDAO) CleanDefaultDays(tx *dbs.Tx, defaultDays int) error {
databaseConfig, err := SharedSysSettingDAO.ReadDatabaseConfig(tx)
if err != nil {
return err
}
if databaseConfig != nil && databaseConfig.NodeTrafficDailyStat.Clean.Days > 0 {
defaultDays = databaseConfig.NodeTrafficDailyStat.Clean.Days
}
if defaultDays <= 0 {
defaultDays = 32
}
return this.CleanDays(tx, defaultDays)
}

View File

@@ -104,7 +104,8 @@ func (this *OriginDAO) CreateOrigin(tx *dbs.Tx,
certRef *sslconfigs.SSLCertRef, certRef *sslconfigs.SSLCertRef,
domains []string, domains []string,
host string, host string,
followPort bool) (originId int64, err error) { followPort bool,
http2Enabled bool) (originId int64, err error) {
var op = NewOriginOperator() var op = NewOriginOperator()
op.AdminId = adminId op.AdminId = adminId
op.UserId = userId op.UserId = userId
@@ -182,6 +183,7 @@ func (this *OriginDAO) CreateOrigin(tx *dbs.Tx,
op.Host = host op.Host = host
op.FollowPort = followPort op.FollowPort = followPort
op.Http2Enabled = http2Enabled
op.State = OriginStateEnabled op.State = OriginStateEnabled
err = this.Save(tx, op) err = this.Save(tx, op)
@@ -208,7 +210,8 @@ func (this *OriginDAO) UpdateOrigin(tx *dbs.Tx,
certRef *sslconfigs.SSLCertRef, certRef *sslconfigs.SSLCertRef,
domains []string, domains []string,
host string, host string,
followPort bool) error { followPort bool,
http2Enabled bool) error {
if originId <= 0 { if originId <= 0 {
return errors.New("invalid originId") return errors.New("invalid originId")
} }
@@ -224,6 +227,8 @@ func (this *OriginDAO) UpdateOrigin(tx *dbs.Tx,
return err return err
} }
op.Oss = ossConfigJSON op.Oss = ossConfigJSON
} else {
op.Oss = dbs.SQL("NULL")
} }
op.Description = description op.Description = description
@@ -290,6 +295,7 @@ func (this *OriginDAO) UpdateOrigin(tx *dbs.Tx,
op.Host = host op.Host = host
op.FollowPort = followPort op.FollowPort = followPort
op.Http2Enabled = http2Enabled
err := this.Save(tx, op) err := this.Save(tx, op)
if err != nil { if err != nil {
@@ -299,6 +305,19 @@ func (this *OriginDAO) UpdateOrigin(tx *dbs.Tx,
return this.NotifyUpdate(tx, originId) return this.NotifyUpdate(tx, originId)
} }
// UpdateOriginIsOn 修改源站是否启用
func (this *OriginDAO) UpdateOriginIsOn(tx *dbs.Tx, originId int64, isOn bool) error {
err := this.Query(tx).
Pk(originId).
Set("isOn", isOn).
UpdateQuickly()
if err != nil {
return err
}
return this.NotifyUpdate(tx, originId)
}
// CloneOrigin 复制源站 // CloneOrigin 复制源站
func (this *OriginDAO) CloneOrigin(tx *dbs.Tx, fromOriginId int64) (newOriginId int64, err error) { func (this *OriginDAO) CloneOrigin(tx *dbs.Tx, fromOriginId int64) (newOriginId int64, err error) {
if fromOriginId <= 0 { if fromOriginId <= 0 {
@@ -353,6 +372,7 @@ func (this *OriginDAO) CloneOrigin(tx *dbs.Tx, fromOriginId int64) (newOriginId
op.Domains = origin.Domains op.Domains = origin.Domains
} }
op.FollowPort = origin.FollowPort op.FollowPort = origin.FollowPort
op.Http2Enabled = origin.Http2Enabled
op.State = origin.State op.State = origin.State
return this.SaveInt64(tx, op) return this.SaveInt64(tx, op)
} }
@@ -391,9 +411,11 @@ func (this *OriginDAO) ComposeOriginConfig(tx *dbs.Tx, originId int64, dataMap *
RequestHost: origin.Host, RequestHost: origin.Host,
Domains: origin.DecodeDomains(), Domains: origin.DecodeDomains(),
FollowPort: origin.FollowPort, FollowPort: origin.FollowPort,
HTTP2Enabled: origin.Http2Enabled,
} }
// addr // addr
var isOSS = false
if IsNotNull(origin.Addr) { if IsNotNull(origin.Addr) {
var addr = &serverconfigs.NetworkAddressConfig{} var addr = &serverconfigs.NetworkAddressConfig{}
err = json.Unmarshal(origin.Addr, addr) err = json.Unmarshal(origin.Addr, addr)
@@ -401,10 +423,11 @@ func (this *OriginDAO) ComposeOriginConfig(tx *dbs.Tx, originId int64, dataMap *
return nil, err return nil, err
} }
config.Addr = addr config.Addr = addr
isOSS = ossconfigs.IsOSSProtocol(string(addr.Protocol))
} }
// oss // oss
if IsNotNull(origin.Oss) { if isOSS && IsNotNull(origin.Oss) {
var ossConfig = ossconfigs.NewOSSConfig() var ossConfig = ossconfigs.NewOSSConfig()
err = json.Unmarshal(origin.Oss, ossConfig) err = json.Unmarshal(origin.Oss, ossConfig)
if err != nil { if err != nil {
@@ -528,6 +551,17 @@ func (this *OriginDAO) CheckUserOrigin(tx *dbs.Tx, userId int64, originId int64)
return SharedReverseProxyDAO.CheckUserReverseProxy(tx, userId, reverseProxyId) return SharedReverseProxyDAO.CheckUserReverseProxy(tx, userId, reverseProxyId)
} }
// ExistsOrigin 检查源站是否存在
func (this *OriginDAO) ExistsOrigin(tx *dbs.Tx, originId int64) (bool, error) {
if originId <= 0 {
return false, nil
}
return this.Query(tx).
Pk(originId).
State(OriginStateEnabled).
Exist()
}
// NotifyUpdate 通知更新 // NotifyUpdate 通知更新
func (this *OriginDAO) NotifyUpdate(tx *dbs.Tx, originId int64) error { func (this *OriginDAO) NotifyUpdate(tx *dbs.Tx, originId int64) error {
reverseProxyId, err := SharedReverseProxyDAO.FindReverseProxyContainsOriginId(tx, originId) reverseProxyId, err := SharedReverseProxyDAO.FindReverseProxyContainsOriginId(tx, originId)

View File

@@ -32,6 +32,7 @@ type Origin struct {
Domains dbs.JSON `field:"domains"` // 所属域名 Domains dbs.JSON `field:"domains"` // 所属域名
FollowPort bool `field:"followPort"` // 端口跟随 FollowPort bool `field:"followPort"` // 端口跟随
State uint8 `field:"state"` // 状态 State uint8 `field:"state"` // 状态
Http2Enabled bool `field:"http2Enabled"` // 是否支持HTTP/2
} }
type OriginOperator struct { type OriginOperator struct {
@@ -63,6 +64,7 @@ type OriginOperator struct {
Domains any // 所属域名 Domains any // 所属域名
FollowPort any // 端口跟随 FollowPort any // 端口跟随
State any // 状态 State any // 状态
Http2Enabled any // 是否支持HTTP/2
} }
func NewOriginOperator() *OriginOperator { func NewOriginOperator() *OriginOperator {

View File

@@ -49,7 +49,12 @@ func (this *PlanDAO) EnablePlan(tx *dbs.Tx, id uint32) error {
// DisablePlan 禁用条目 // DisablePlan 禁用条目
func (this *PlanDAO) DisablePlan(tx *dbs.Tx, id int64) error { func (this *PlanDAO) DisablePlan(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx). clusterId, err := this.FindPlanClusterId(tx, id)
if err != nil {
return err
}
_, err = this.Query(tx).
Pk(id). Pk(id).
Set("state", PlanStateDisabled). Set("state", PlanStateDisabled).
Update() Update()
@@ -57,19 +62,32 @@ func (this *PlanDAO) DisablePlan(tx *dbs.Tx, id int64) error {
return err return err
} }
return this.NotifyUpdate(tx, id) return this.NotifyUpdate(tx, id, clusterId)
} }
// FindEnabledPlan 查找启用中的条目 // FindEnabledPlan 查找启用中的条目
func (this *PlanDAO) FindEnabledPlan(tx *dbs.Tx, id int64) (*Plan, error) { func (this *PlanDAO) FindEnabledPlan(tx *dbs.Tx, planId int64, cacheMap *utils.CacheMap) (*Plan, error) {
var cacheKey = this.Table + ":FindEnabledPlan:" + types.String(planId)
if cacheMap != nil {
cache, _ := cacheMap.Get(cacheKey)
if cache != nil {
return cache.(*Plan), nil
}
}
result, err := this.Query(tx). result, err := this.Query(tx).
Pk(id). Pk(planId).
Attr("state", PlanStateEnabled). Attr("state", PlanStateEnabled).
Find() Find()
if result == nil { if result == nil {
return nil, err return nil, err
} }
return result.(*Plan), err
if cacheMap != nil {
cacheMap.Put(cacheKey, result)
}
return result.(*Plan), nil
} }
// FindPlanName 根据主键查找名称 // FindPlanName 根据主键查找名称
@@ -162,18 +180,18 @@ func (this *PlanDAO) FindEnabledPlanTrafficLimit(tx *dbs.Tx, planId int64, cache
return config, nil return config, nil
} }
// NotifyUpdate 通知变更 // FindPlanClusterId 查找套餐所属集群
func (this *PlanDAO) NotifyUpdate(tx *dbs.Tx, planId int64) error { func (this *PlanDAO) FindPlanClusterId(tx *dbs.Tx, planId int64) (clusterId int64, err error) {
// 这里不要加入状态参数,因为需要适应删除后的更新 return this.Query(tx).
clusterId, err := this.Query(tx).
Pk(planId). Pk(planId).
Result("clusterId"). Result("clusterId").
FindInt64Col(0) FindInt64Col(0)
if err != nil { }
return err
} // NotifyUpdate 通知变更
if clusterId > 0 { func (this *PlanDAO) NotifyUpdate(tx *dbs.Tx, planId int64, clusterId int64) error {
return SharedNodeClusterDAO.NotifyUpdate(tx, clusterId) if clusterId <= 0 {
} return nil
return nil }
return SharedNodeClusterDAO.NotifyUpdate(tx, clusterId)
} }

View File

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

View File

@@ -2,39 +2,89 @@ package models
import "github.com/iwind/TeaGo/dbs" import "github.com/iwind/TeaGo/dbs"
const (
PlanField_Id dbs.FieldName = "id" // ID
PlanField_IsOn dbs.FieldName = "isOn" // 是否启用
PlanField_Name dbs.FieldName = "name" // 套餐名
PlanField_Description dbs.FieldName = "description" // 套餐简介
PlanField_ClusterId dbs.FieldName = "clusterId" // 集群ID
PlanField_TrafficLimit dbs.FieldName = "trafficLimit" // 流量限制
PlanField_BandwidthLimitPerNode dbs.FieldName = "bandwidthLimitPerNode" // 单节点带宽限制
PlanField_Features dbs.FieldName = "features" // 允许的功能
PlanField_HasFullFeatures dbs.FieldName = "hasFullFeatures" // 是否有完整的功能
PlanField_TrafficPrice dbs.FieldName = "trafficPrice" // 流量价格设定
PlanField_BandwidthPrice dbs.FieldName = "bandwidthPrice" // 带宽价格
PlanField_MonthlyPrice dbs.FieldName = "monthlyPrice" // 月付
PlanField_SeasonallyPrice dbs.FieldName = "seasonallyPrice" // 季付
PlanField_YearlyPrice dbs.FieldName = "yearlyPrice" // 年付
PlanField_PriceType dbs.FieldName = "priceType" // 价格类型
PlanField_Order dbs.FieldName = "order" // 排序
PlanField_State dbs.FieldName = "state" // 状态
PlanField_TotalServers dbs.FieldName = "totalServers" // 可以绑定的网站数量
PlanField_TotalServerNamesPerServer dbs.FieldName = "totalServerNamesPerServer" // 每个网站可以绑定的域名数量
PlanField_TotalServerNames dbs.FieldName = "totalServerNames" // 总域名数量
PlanField_MonthlyRequests dbs.FieldName = "monthlyRequests" // 每月访问量额度
PlanField_DailyRequests dbs.FieldName = "dailyRequests" // 每日访问量额度
PlanField_DailyWebsocketConnections dbs.FieldName = "dailyWebsocketConnections" // 每日Websocket连接数
PlanField_MonthlyWebsocketConnections dbs.FieldName = "monthlyWebsocketConnections" // 每月Websocket连接数
PlanField_MaxUploadSize dbs.FieldName = "maxUploadSize" // 最大上传
)
// Plan 用户套餐 // Plan 用户套餐
type Plan struct { type Plan struct {
Id uint32 `field:"id"` // ID Id uint32 `field:"id"` // ID
IsOn bool `field:"isOn"` // 是否启用 IsOn bool `field:"isOn"` // 是否启用
Name string `field:"name"` // 套餐名 Name string `field:"name"` // 套餐名
ClusterId uint32 `field:"clusterId"` // 集群ID Description string `field:"description"` // 套餐简介
TrafficLimit dbs.JSON `field:"trafficLimit"` // 流量限制 ClusterId uint32 `field:"clusterId"` // 集群ID
Features dbs.JSON `field:"features"` // 允许的功能 TrafficLimit dbs.JSON `field:"trafficLimit"` // 流量限制
TrafficPrice dbs.JSON `field:"trafficPrice"` // 流量价格设定 BandwidthLimitPerNode dbs.JSON `field:"bandwidthLimitPerNode"` // 单节点带宽限制
BandwidthPrice dbs.JSON `field:"bandwidthPrice"` // 带宽价格 Features dbs.JSON `field:"features"` // 允许的功能
MonthlyPrice float64 `field:"monthlyPrice"` // 月付 HasFullFeatures bool `field:"hasFullFeatures"` // 是否有完整的功能
SeasonallyPrice float64 `field:"seasonallyPrice"` // 季付 TrafficPrice dbs.JSON `field:"trafficPrice"` // 流量价格设定
YearlyPrice float64 `field:"yearlyPrice"` // 年付 BandwidthPrice dbs.JSON `field:"bandwidthPrice"` // 带宽价格
PriceType string `field:"priceType"` // 价格类型 MonthlyPrice float64 `field:"monthlyPrice"` // 月付
Order uint32 `field:"order"` // 排序 SeasonallyPrice float64 `field:"seasonallyPrice"` // 季付
State uint8 `field:"state"` // 状态 YearlyPrice float64 `field:"yearlyPrice"` // 年付
PriceType string `field:"priceType"` // 价格类型
Order uint32 `field:"order"` // 排序
State uint8 `field:"state"` // 状态
TotalServers uint32 `field:"totalServers"` // 可以绑定的网站数量
TotalServerNamesPerServer uint32 `field:"totalServerNamesPerServer"` // 每个网站可以绑定的域名数量
TotalServerNames uint32 `field:"totalServerNames"` // 总域名数量
MonthlyRequests uint64 `field:"monthlyRequests"` // 每月访问量额度
DailyRequests uint64 `field:"dailyRequests"` // 每日访问量额度
DailyWebsocketConnections uint64 `field:"dailyWebsocketConnections"` // 每日Websocket连接数
MonthlyWebsocketConnections uint64 `field:"monthlyWebsocketConnections"` // 每月Websocket连接数
MaxUploadSize dbs.JSON `field:"maxUploadSize"` // 最大上传
} }
type PlanOperator struct { type PlanOperator struct {
Id interface{} // ID Id any // ID
IsOn interface{} // 是否启用 IsOn any // 是否启用
Name interface{} // 套餐名 Name any // 套餐名
ClusterId interface{} // 集群ID Description any // 套餐简介
TrafficLimit interface{} // 流量限制 ClusterId any // 集群ID
Features interface{} // 允许的功能 TrafficLimit any // 流量限制
TrafficPrice interface{} // 流量价格设定 BandwidthLimitPerNode any // 单节点带宽限制
BandwidthPrice interface{} // 带宽价格 Features any // 允许的功能
MonthlyPrice interface{} // 月付 HasFullFeatures any // 是否有完整的功能
SeasonallyPrice interface{} // 季付 TrafficPrice any // 流量价格设定
YearlyPrice interface{} // 年付 BandwidthPrice any // 带宽价格
PriceType interface{} // 价格类型 MonthlyPrice any // 月付
Order interface{} // 排序 SeasonallyPrice any // 季付
State interface{} // 状态 YearlyPrice any // 年付
PriceType any // 价格类型
Order any // 排序
State any // 状态
TotalServers any // 可以绑定的网站数量
TotalServerNamesPerServer any // 每个网站可以绑定的域名数量
TotalServerNames any // 总域名数量
MonthlyRequests any // 每月访问量额度
DailyRequests any // 每日访问量额度
DailyWebsocketConnections any // 每日Websocket连接数
MonthlyWebsocketConnections any // 每月Websocket连接数
MaxUploadSize any // 最大上传
} }
func NewPlanOperator() *PlanOperator { func NewPlanOperator() *PlanOperator {

View File

@@ -0,0 +1,71 @@
package posts
import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
)
const (
PostCategoryStateEnabled = 1 // 已启用
PostCategoryStateDisabled = 0 // 已禁用
)
type PostCategoryDAO dbs.DAO
func NewPostCategoryDAO() *PostCategoryDAO {
return dbs.NewDAO(&PostCategoryDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgePostCategories",
Model: new(PostCategory),
PkName: "id",
},
}).(*PostCategoryDAO)
}
var SharedPostCategoryDAO *PostCategoryDAO
func init() {
dbs.OnReady(func() {
SharedPostCategoryDAO = NewPostCategoryDAO()
})
}
// EnablePostCategory 启用条目
func (this *PostCategoryDAO) EnablePostCategory(tx *dbs.Tx, categoryId int64) error {
_, err := this.Query(tx).
Pk(categoryId).
Set("state", PostCategoryStateEnabled).
Update()
return err
}
// DisablePostCategory 禁用条目
func (this *PostCategoryDAO) DisablePostCategory(tx *dbs.Tx, categoryId int64) error {
_, err := this.Query(tx).
Pk(categoryId).
Set("state", PostCategoryStateDisabled).
Update()
return err
}
// FindEnabledPostCategory 查找启用中的条目
func (this *PostCategoryDAO) FindEnabledPostCategory(tx *dbs.Tx, categoryId int64) (*PostCategory, error) {
result, err := this.Query(tx).
Pk(categoryId).
State(PostCategoryStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*PostCategory), err
}
// FindPostCategoryName 根据主键查找名称
func (this *PostCategoryDAO) FindPostCategoryName(tx *dbs.Tx, categoryId int64) (string, error) {
return this.Query(tx).
Pk(categoryId).
Result("name").
FindStringCol("")
}

View File

@@ -1,4 +1,4 @@
package models package posts_test
import ( import (
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"

View File

@@ -0,0 +1,35 @@
package posts
import "github.com/iwind/TeaGo/dbs"
const (
PostCategoryField_Id dbs.FieldName = "id" // ID
PostCategoryField_Name dbs.FieldName = "name" // 分类名称
PostCategoryField_IsOn dbs.FieldName = "isOn" // 是否启用
PostCategoryField_Code dbs.FieldName = "code" // 代号
PostCategoryField_Order dbs.FieldName = "order" // 排序
PostCategoryField_State dbs.FieldName = "state" // 分类状态
)
// PostCategory 文章分类
type PostCategory struct {
Id uint32 `field:"id"` // ID
Name string `field:"name"` // 分类名称
IsOn bool `field:"isOn"` // 是否启用
Code string `field:"code"` // 代号
Order uint32 `field:"order"` // 排序
State uint8 `field:"state"` // 分类状态
}
type PostCategoryOperator struct {
Id any // ID
Name any // 分类名称
IsOn any // 是否启用
Code any // 代号
Order any // 排序
State any // 分类状态
}
func NewPostCategoryOperator() *PostCategoryOperator {
return &PostCategoryOperator{}
}

View File

@@ -0,0 +1 @@
package posts

View File

@@ -0,0 +1,63 @@
package posts
import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
)
const (
PostStateEnabled = 1 // 已启用
PostStateDisabled = 0 // 已禁用
)
type PostDAO dbs.DAO
func NewPostDAO() *PostDAO {
return dbs.NewDAO(&PostDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgePosts",
Model: new(Post),
PkName: "id",
},
}).(*PostDAO)
}
var SharedPostDAO *PostDAO
func init() {
dbs.OnReady(func() {
SharedPostDAO = NewPostDAO()
})
}
// EnablePost 启用条目
func (this *PostDAO) EnablePost(tx *dbs.Tx, postId int64) error {
_, err := this.Query(tx).
Pk(postId).
Set("state", PostStateEnabled).
Update()
return err
}
// DisablePost 禁用条目
func (this *PostDAO) DisablePost(tx *dbs.Tx, postId int64) error {
_, err := this.Query(tx).
Pk(postId).
Set("state", PostStateDisabled).
Update()
return err
}
// FindEnabledPost 查找启用中的条目
func (this *PostDAO) FindEnabledPost(tx *dbs.Tx, postId int64) (*Post, error) {
result, err := this.Query(tx).
Pk(postId).
State(PostStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*Post), err
}

View File

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

View File

@@ -0,0 +1,50 @@
package posts
import "github.com/iwind/TeaGo/dbs"
const (
PostField_Id dbs.FieldName = "id" // ID
PostField_CategoryId dbs.FieldName = "categoryId" // 文章分类
PostField_Type dbs.FieldName = "type" // 类型normal, url
PostField_Url dbs.FieldName = "url" // URL
PostField_Subject dbs.FieldName = "subject" // 标题
PostField_Body dbs.FieldName = "body" // 内容
PostField_CreatedAt dbs.FieldName = "createdAt" // 创建时间
PostField_IsPublished dbs.FieldName = "isPublished" // 是否已发布
PostField_PublishedAt dbs.FieldName = "publishedAt" // 发布时间
PostField_ProductCode dbs.FieldName = "productCode" // 产品代号
PostField_State dbs.FieldName = "state" // 状态
)
// Post 文章管理
type Post struct {
Id uint32 `field:"id"` // ID
CategoryId uint32 `field:"categoryId"` // 文章分类
Type string `field:"type"` // 类型normal, url
Url string `field:"url"` // URL
Subject string `field:"subject"` // 标题
Body string `field:"body"` // 内容
CreatedAt uint64 `field:"createdAt"` // 创建时间
IsPublished bool `field:"isPublished"` // 是否已发布
PublishedAt uint64 `field:"publishedAt"` // 发布时间
ProductCode string `field:"productCode"` // 产品代号
State uint8 `field:"state"` // 状态
}
type PostOperator struct {
Id any // ID
CategoryId any // 文章分类
Type any // 类型normal, url
Url any // URL
Subject any // 标题
Body any // 内容
CreatedAt any // 创建时间
IsPublished any // 是否已发布
PublishedAt any // 发布时间
ProductCode any // 产品代号
State any // 状态
}
func NewPostOperator() *PostOperator {
return &PostOperator{}
}

View File

@@ -0,0 +1 @@
package posts

View File

@@ -42,7 +42,7 @@ func init() {
// EnableRegionCity 启用条目 // EnableRegionCity 启用条目
func (this *RegionCityDAO) EnableRegionCity(tx *dbs.Tx, id uint32) error { func (this *RegionCityDAO) EnableRegionCity(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx). _, err := this.Query(tx).
Pk(id). Attr("valueId", id).
Set("state", RegionCityStateEnabled). Set("state", RegionCityStateEnabled).
Update() Update()
return err return err
@@ -51,7 +51,7 @@ func (this *RegionCityDAO) EnableRegionCity(tx *dbs.Tx, id uint32) error {
// DisableRegionCity 禁用条目 // DisableRegionCity 禁用条目
func (this *RegionCityDAO) DisableRegionCity(tx *dbs.Tx, id uint32) error { func (this *RegionCityDAO) DisableRegionCity(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx). _, err := this.Query(tx).
Pk(id). Attr("valueId", id).
Set("state", RegionCityStateDisabled). Set("state", RegionCityStateDisabled).
Update() Update()
return err return err
@@ -60,7 +60,7 @@ func (this *RegionCityDAO) DisableRegionCity(tx *dbs.Tx, id uint32) error {
// FindEnabledRegionCity 查找启用中的条目 // FindEnabledRegionCity 查找启用中的条目
func (this *RegionCityDAO) FindEnabledRegionCity(tx *dbs.Tx, id int64) (*RegionCity, error) { func (this *RegionCityDAO) FindEnabledRegionCity(tx *dbs.Tx, id int64) (*RegionCity, error) {
result, err := this.Query(tx). result, err := this.Query(tx).
Pk(id). Attr("valueId", id).
Attr("state", RegionCityStateEnabled). Attr("state", RegionCityStateEnabled).
Find() Find()
if result == nil { if result == nil {
@@ -72,7 +72,7 @@ func (this *RegionCityDAO) FindEnabledRegionCity(tx *dbs.Tx, id int64) (*RegionC
// FindRegionCityName 根据主键查找名称 // FindRegionCityName 根据主键查找名称
func (this *RegionCityDAO) FindRegionCityName(tx *dbs.Tx, id uint32) (string, error) { func (this *RegionCityDAO) FindRegionCityName(tx *dbs.Tx, id uint32) (string, error) {
return this.Query(tx). return this.Query(tx).
Pk(id). Attr("valueId", id).
Result("name"). Result("name").
FindStringCol("") FindStringCol("")
} }
@@ -81,7 +81,7 @@ func (this *RegionCityDAO) FindRegionCityName(tx *dbs.Tx, id uint32) (string, er
func (this *RegionCityDAO) FindCityWithDataId(tx *dbs.Tx, dataId string) (int64, error) { func (this *RegionCityDAO) FindCityWithDataId(tx *dbs.Tx, dataId string) (int64, error) {
return this.Query(tx). return this.Query(tx).
Attr("dataId", dataId). Attr("dataId", dataId).
ResultPk(). Result(RegionCityField_ValueId).
FindInt64Col(0) FindInt64Col(0)
} }
@@ -93,7 +93,7 @@ func (this *RegionCityDAO) CreateCity(tx *dbs.Tx, provinceId int64, name string,
op.DataId = dataId op.DataId = dataId
op.State = RegionCityStateEnabled op.State = RegionCityStateEnabled
codes := []string{name} var codes = []string{name}
codesJSON, err := json.Marshal(codes) codesJSON, err := json.Marshal(codes)
if err != nil { if err != nil {
return 0, err return 0, err
@@ -103,7 +103,18 @@ func (this *RegionCityDAO) CreateCity(tx *dbs.Tx, provinceId int64, name string,
if err != nil { if err != nil {
return 0, err return 0, err
} }
return types.Int64(op.Id), nil var cityId = types.Int64(op.Id)
// value id
err = this.Query(tx).
Pk(cityId).
Set(RegionCityField_ValueId, cityId).
UpdateQuickly()
if err != nil {
return 0, err
}
return cityId, nil
} }
// FindCityIdWithName 根据城市名查找城市ID // FindCityIdWithName 根据城市名查找城市ID
@@ -113,7 +124,7 @@ func (this *RegionCityDAO) FindCityIdWithName(tx *dbs.Tx, provinceId int64, city
Where("(name=:cityName OR customName=:cityName OR JSON_CONTAINS(codes, :cityNameJSON) OR JSON_CONTAINS(customCodes, :cityNameJSON))"). Where("(name=:cityName OR customName=:cityName OR JSON_CONTAINS(codes, :cityNameJSON) OR JSON_CONTAINS(customCodes, :cityNameJSON))").
Param("cityName", cityName). Param("cityName", cityName).
Param("cityNameJSON", strconv.Quote(cityName)). // 查询的需要是个JSON字符串所以这里加双引号 Param("cityNameJSON", strconv.Quote(cityName)). // 查询的需要是个JSON字符串所以这里加双引号
ResultPk(). Result(RegionCityField_ValueId).
FindInt64Col(0) FindInt64Col(0)
} }
@@ -147,7 +158,7 @@ func (this *RegionCityDAO) UpdateCityCustom(tx *dbs.Tx, cityId int64, customName
} }
return this.Query(tx). return this.Query(tx).
Pk(cityId). Attr(RegionCityField_ValueId, cityId).
Set("customName", customName). Set("customName", customName).
Set("customCodes", customCodesJSON). Set("customCodes", customCodesJSON).
UpdateQuickly() UpdateQuickly()

View File

@@ -2,9 +2,22 @@ package regions
import "github.com/iwind/TeaGo/dbs" import "github.com/iwind/TeaGo/dbs"
const (
RegionCityField_Id dbs.FieldName = "id" // ID
RegionCityField_ValueId dbs.FieldName = "valueId" // 实际ID
RegionCityField_ProvinceId dbs.FieldName = "provinceId" // 省份ID
RegionCityField_Name dbs.FieldName = "name" // 名称
RegionCityField_Codes dbs.FieldName = "codes" // 代号
RegionCityField_CustomName dbs.FieldName = "customName" // 自定义名称
RegionCityField_CustomCodes dbs.FieldName = "customCodes" // 自定义代号
RegionCityField_State dbs.FieldName = "state" // 状态
RegionCityField_DataId dbs.FieldName = "dataId" // 原始数据ID
)
// RegionCity 区域-城市 // RegionCity 区域-城市
type RegionCity struct { type RegionCity struct {
Id uint32 `field:"id"` // ID Id1 uint32 `field:"id"` // ID
ValueId uint32 `field:"valueId"` // 实际ID
ProvinceId uint32 `field:"provinceId"` // 省份ID ProvinceId uint32 `field:"provinceId"` // 省份ID
Name string `field:"name"` // 名称 Name string `field:"name"` // 名称
Codes dbs.JSON `field:"codes"` // 代号 Codes dbs.JSON `field:"codes"` // 代号
@@ -15,14 +28,15 @@ type RegionCity struct {
} }
type RegionCityOperator struct { type RegionCityOperator struct {
Id interface{} // ID Id any // ID
ProvinceId interface{} // 省份ID ValueId any // 实际ID
Name interface{} // 名称 ProvinceId any // 省份ID
Codes interface{} // 代号 Name any // 名称
CustomName interface{} // 自定义名称 Codes any // 代号
CustomCodes interface{} // 自定义代号 CustomName any // 自定义名称
State interface{} // 状态 CustomCodes any // 自定义代号
DataId interface{} // 原始数据ID State any // 状态
DataId any // 原始数据ID
} }
func NewRegionCityOperator() *RegionCityOperator { func NewRegionCityOperator() *RegionCityOperator {

View File

@@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/utils" "github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils" "github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/regionconfigs"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/dbs"
@@ -20,10 +21,6 @@ const (
RegionCountryStateDisabled = 0 // 已禁用 RegionCountryStateDisabled = 0 // 已禁用
) )
const (
CountryChinaId = 1
)
var regionCountryIdAndNameCacheMap = map[int64]string{} // country id => name var regionCountryIdAndNameCacheMap = map[int64]string{} // country id => name
type RegionCountryDAO dbs.DAO type RegionCountryDAO dbs.DAO
@@ -50,7 +47,7 @@ func init() {
// EnableRegionCountry 启用条目 // EnableRegionCountry 启用条目
func (this *RegionCountryDAO) EnableRegionCountry(tx *dbs.Tx, id uint32) error { func (this *RegionCountryDAO) EnableRegionCountry(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx). _, err := this.Query(tx).
Pk(id). Attr("valueId", id).
Set("state", RegionCountryStateEnabled). Set("state", RegionCountryStateEnabled).
Update() Update()
return err return err
@@ -59,7 +56,7 @@ func (this *RegionCountryDAO) EnableRegionCountry(tx *dbs.Tx, id uint32) error {
// DisableRegionCountry 禁用条目 // DisableRegionCountry 禁用条目
func (this *RegionCountryDAO) DisableRegionCountry(tx *dbs.Tx, id int64) error { func (this *RegionCountryDAO) DisableRegionCountry(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx). _, err := this.Query(tx).
Pk(id). Attr("valueId", id).
Set("state", RegionCountryStateDisabled). Set("state", RegionCountryStateDisabled).
Update() Update()
return err return err
@@ -68,7 +65,7 @@ func (this *RegionCountryDAO) DisableRegionCountry(tx *dbs.Tx, id int64) error {
// FindEnabledRegionCountry 查找启用中的条目 // FindEnabledRegionCountry 查找启用中的条目
func (this *RegionCountryDAO) FindEnabledRegionCountry(tx *dbs.Tx, id int64) (*RegionCountry, error) { func (this *RegionCountryDAO) FindEnabledRegionCountry(tx *dbs.Tx, id int64) (*RegionCountry, error) {
result, err := this.Query(tx). result, err := this.Query(tx).
Pk(id). Attr("valueId", id).
Attr("state", RegionCountryStateEnabled). Attr("state", RegionCountryStateEnabled).
Find() Find()
if result == nil { if result == nil {
@@ -88,7 +85,7 @@ func (this *RegionCountryDAO) FindRegionCountryName(tx *dbs.Tx, id int64) (strin
} }
name, err := this.Query(tx). name, err := this.Query(tx).
Pk(id). Attr("valueId", id).
Result("name"). Result("name").
FindStringCol("") FindStringCol("")
if err != nil { if err != nil {
@@ -101,11 +98,19 @@ func (this *RegionCountryDAO) FindRegionCountryName(tx *dbs.Tx, id int64) (strin
return name, nil return name, nil
} }
// FindRegionCountryRouteCode 查找国家|地区线路代号
func (this *RegionCountryDAO) FindRegionCountryRouteCode(tx *dbs.Tx, countryId int64) (string, error) {
return this.Query(tx).
Attr("valueId", countryId).
Result("routeCode").
FindStringCol("")
}
// FindCountryIdWithDataId 根据数据ID查找国家 // FindCountryIdWithDataId 根据数据ID查找国家
func (this *RegionCountryDAO) FindCountryIdWithDataId(tx *dbs.Tx, dataId string) (int64, error) { func (this *RegionCountryDAO) FindCountryIdWithDataId(tx *dbs.Tx, dataId string) (int64, error) {
return this.Query(tx). return this.Query(tx).
Attr("dataId", dataId). Attr("dataId", dataId).
ResultPk(). Result(RegionCountryField_ValueId).
FindInt64Col(0) FindInt64Col(0)
} }
@@ -115,7 +120,7 @@ func (this *RegionCountryDAO) FindCountryIdWithName(tx *dbs.Tx, countryName stri
Where("(name=:countryName OR JSON_CONTAINS(codes, :countryNameJSON) OR customName=:countryName OR JSON_CONTAINS(customCodes, :countryNameJSON))"). Where("(name=:countryName OR JSON_CONTAINS(codes, :countryNameJSON) OR customName=:countryName OR JSON_CONTAINS(customCodes, :countryNameJSON))").
Param("countryName", countryName). Param("countryName", countryName).
Param("countryNameJSON", strconv.Quote(countryName)). // 查询的需要是个JSON字符串所以这里加双引号 Param("countryNameJSON", strconv.Quote(countryName)). // 查询的需要是个JSON字符串所以这里加双引号
ResultPk(). Result(RegionCountryField_ValueId).
FindInt64Col(0) FindInt64Col(0)
} }
@@ -130,6 +135,9 @@ func (this *RegionCountryDAO) CreateCountry(tx *dbs.Tx, name string, dataId stri
pinyinResult = append(pinyinResult, strings.Join(piece, " ")) pinyinResult = append(pinyinResult, strings.Join(piece, " "))
} }
pinyinJSON, err := json.Marshal([]string{strings.Join(pinyinResult, " ")}) pinyinJSON, err := json.Marshal([]string{strings.Join(pinyinResult, " ")})
if err != nil {
return 0, err
}
op.Pinyin = pinyinJSON op.Pinyin = pinyinJSON
codes := []string{name} codes := []string{name}
@@ -145,16 +153,61 @@ func (this *RegionCountryDAO) CreateCountry(tx *dbs.Tx, name string, dataId stri
if err != nil { if err != nil {
return 0, err return 0, err
} }
return types.Int64(op.Id), nil var countryId = types.Int64(op.Id)
err = this.Query(tx).
Pk(countryId).
Set(RegionCountryField_ValueId, countryId).
UpdateQuickly()
if err != nil {
return 0, err
}
return countryId, nil
} }
// FindAllEnabledCountriesOrderByPinyin 查找所有可用的国家并按拼音排序 // FindAllEnabledCountriesOrderByPinyin 查找所有可用的国家并按拼音排序
func (this *RegionCountryDAO) FindAllEnabledCountriesOrderByPinyin(tx *dbs.Tx) (result []*RegionCountry, err error) { func (this *RegionCountryDAO) FindAllEnabledCountriesOrderByPinyin(tx *dbs.Tx) (result []*RegionCountry, err error) {
_, err = this.Query(tx). ones, err := this.Query(tx).
State(RegionCountryStateEnabled). State(RegionCountryStateEnabled).
Slice(&result). Asc("JSON_EXTRACT(pinyin, '$[0]')").
Asc("pinyin").
FindAll() FindAll()
if err != nil {
return nil, err
}
// resort China special regions
var chinaRegionMap = map[int64]*RegionCountry{} // countryId => *RegionCountry
for _, one := range ones {
var country = one.(*RegionCountry)
var valueId = int64(country.ValueId)
if regionconfigs.CheckRegionIsInGreaterChina(valueId) {
chinaRegionMap[valueId] = country
}
}
for _, one := range ones {
var country = one.(*RegionCountry)
var valueId = int64(country.ValueId)
if valueId == regionconfigs.RegionChinaId {
result = append(result, country)
// add hk, tw, mo, mainland ...
for _, subRegionId := range regionconfigs.FindAllGreaterChinaSubRegionIds() {
subRegion, ok := chinaRegionMap[subRegionId]
if ok {
result = append(result, subRegion)
}
}
continue
}
if regionconfigs.CheckRegionIsInGreaterChina(valueId) {
continue
}
result = append(result, country)
}
return return
} }
@@ -163,7 +216,7 @@ func (this *RegionCountryDAO) FindAllCountries(tx *dbs.Tx) (result []*RegionCoun
_, err = this.Query(tx). _, err = this.Query(tx).
State(RegionCountryStateEnabled). State(RegionCountryStateEnabled).
Slice(&result). Slice(&result).
AscPk(). Asc(RegionCountryField_ValueId).
FindAll() FindAll()
return return
} }
@@ -185,7 +238,7 @@ func (this *RegionCountryDAO) UpdateCountryCustom(tx *dbs.Tx, countryId int64, c
}() }()
return this.Query(tx). return this.Query(tx).
Pk(countryId). Attr("valueId", countryId).
Set("customName", customName). Set("customName", customName).
Set("customCodes", customCodesJSON). Set("customCodes", customCodesJSON).
UpdateQuickly() UpdateQuickly()

View File

@@ -2,9 +2,26 @@ package regions
import "github.com/iwind/TeaGo/dbs" import "github.com/iwind/TeaGo/dbs"
const (
RegionCountryField_Id dbs.FieldName = "id" // ID
RegionCountryField_ValueId dbs.FieldName = "valueId" // 实际ID
RegionCountryField_ValueCode dbs.FieldName = "valueCode" // 值代号
RegionCountryField_Name dbs.FieldName = "name" // 名称
RegionCountryField_Codes dbs.FieldName = "codes" // 代号
RegionCountryField_CustomName dbs.FieldName = "customName" // 自定义名称
RegionCountryField_CustomCodes dbs.FieldName = "customCodes" // 自定义代号
RegionCountryField_State dbs.FieldName = "state" // 状态
RegionCountryField_DataId dbs.FieldName = "dataId" // 原始数据ID
RegionCountryField_Pinyin dbs.FieldName = "pinyin" // 拼音
RegionCountryField_IsCommon dbs.FieldName = "isCommon" // 是否常用
RegionCountryField_RouteCode dbs.FieldName = "routeCode" // 线路代号
)
// RegionCountry 区域-国家/地区 // RegionCountry 区域-国家/地区
type RegionCountry struct { type RegionCountry struct {
Id uint32 `field:"id"` // ID Id uint32 `field:"id"` // ID
ValueId uint32 `field:"valueId"` // 实际ID
ValueCode string `field:"valueCode"` // 值代号
Name string `field:"name"` // 名称 Name string `field:"name"` // 名称
Codes dbs.JSON `field:"codes"` // 代号 Codes dbs.JSON `field:"codes"` // 代号
CustomName string `field:"customName"` // 自定义名称 CustomName string `field:"customName"` // 自定义名称
@@ -12,17 +29,23 @@ type RegionCountry struct {
State uint8 `field:"state"` // 状态 State uint8 `field:"state"` // 状态
DataId string `field:"dataId"` // 原始数据ID DataId string `field:"dataId"` // 原始数据ID
Pinyin dbs.JSON `field:"pinyin"` // 拼音 Pinyin dbs.JSON `field:"pinyin"` // 拼音
IsCommon bool `field:"isCommon"` // 是否常用
RouteCode string `field:"routeCode"` // 线路代号
} }
type RegionCountryOperator struct { type RegionCountryOperator struct {
Id interface{} // ID Id any // ID
Name interface{} // 名称 ValueId any // 实际ID
Codes interface{} // 代号 ValueCode any // 代号
CustomName interface{} // 自定义名称 Name any // 名称
CustomCodes interface{} // 自定义代号 Codes any // 代号
State interface{} // 状态 CustomName any // 自定义名称
DataId interface{} // 原始数据ID CustomCodes any // 自定义代号
Pinyin interface{} // 拼音 State any // 状态
DataId any // 原始数据ID
Pinyin any // 拼音
IsCommon any // 是否常用
RouteCode any // 线路代号
} }
func NewRegionCountryOperator() *RegionCountryOperator { func NewRegionCountryOperator() *RegionCountryOperator {

View File

@@ -41,7 +41,7 @@ func init() {
// EnableRegionProvider 启用条目 // EnableRegionProvider 启用条目
func (this *RegionProviderDAO) EnableRegionProvider(tx *dbs.Tx, id uint32) error { func (this *RegionProviderDAO) EnableRegionProvider(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx). _, err := this.Query(tx).
Pk(id). Attr("valueId", id).
Set("state", RegionProviderStateEnabled). Set("state", RegionProviderStateEnabled).
Update() Update()
return err return err
@@ -50,7 +50,7 @@ func (this *RegionProviderDAO) EnableRegionProvider(tx *dbs.Tx, id uint32) error
// DisableRegionProvider 禁用条目 // DisableRegionProvider 禁用条目
func (this *RegionProviderDAO) DisableRegionProvider(tx *dbs.Tx, id uint32) error { func (this *RegionProviderDAO) DisableRegionProvider(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx). _, err := this.Query(tx).
Pk(id). Attr("valueId", id).
Set("state", RegionProviderStateDisabled). Set("state", RegionProviderStateDisabled).
Update() Update()
return err return err
@@ -59,7 +59,7 @@ func (this *RegionProviderDAO) DisableRegionProvider(tx *dbs.Tx, id uint32) erro
// FindEnabledRegionProvider 查找启用中的条目 // FindEnabledRegionProvider 查找启用中的条目
func (this *RegionProviderDAO) FindEnabledRegionProvider(tx *dbs.Tx, id int64) (*RegionProvider, error) { func (this *RegionProviderDAO) FindEnabledRegionProvider(tx *dbs.Tx, id int64) (*RegionProvider, error) {
result, err := this.Query(tx). result, err := this.Query(tx).
Pk(id). Attr("valueId", id).
Attr("state", RegionProviderStateEnabled). Attr("state", RegionProviderStateEnabled).
Find() Find()
if result == nil { if result == nil {
@@ -71,7 +71,7 @@ func (this *RegionProviderDAO) FindEnabledRegionProvider(tx *dbs.Tx, id int64) (
// FindRegionProviderName 根据主键查找名称 // FindRegionProviderName 根据主键查找名称
func (this *RegionProviderDAO) FindRegionProviderName(tx *dbs.Tx, id uint32) (string, error) { func (this *RegionProviderDAO) FindRegionProviderName(tx *dbs.Tx, id uint32) (string, error) {
return this.Query(tx). return this.Query(tx).
Pk(id). Attr("valueId", id).
Result("name"). Result("name").
FindStringCol("") FindStringCol("")
} }
@@ -82,7 +82,7 @@ func (this *RegionProviderDAO) FindProviderIdWithName(tx *dbs.Tx, providerName s
Where("(name=:providerName OR customName=:providerName OR JSON_CONTAINS(codes, :providerNameJSON) OR JSON_CONTAINS(customCodes, :providerNameJSON))"). Where("(name=:providerName OR customName=:providerName OR JSON_CONTAINS(codes, :providerNameJSON) OR JSON_CONTAINS(customCodes, :providerNameJSON))").
Param("providerName", providerName). Param("providerName", providerName).
Param("providerNameJSON", strconv.Quote(providerName)). // 查询的需要是个JSON字符串所以这里加双引号 Param("providerNameJSON", strconv.Quote(providerName)). // 查询的需要是个JSON字符串所以这里加双引号
ResultPk(). Result(RegionProviderField_ValueId).
FindInt64Col(0) FindInt64Col(0)
} }
@@ -96,7 +96,20 @@ func (this *RegionProviderDAO) CreateProvider(tx *dbs.Tx, name string) (int64, e
return 0, err return 0, err
} }
op.Codes = codesJSON op.Codes = codesJSON
return this.SaveInt64(tx, op) providerId, err := this.SaveInt64(tx, op)
if err != nil {
return 0, err
}
err = this.Query(tx).
Pk(providerId).
Set(RegionProviderField_ValueId, providerId).
UpdateQuickly()
if err != nil {
return 0, err
}
return providerId, nil
} }
// FindAllEnabledProviders 查找所有服务商 // FindAllEnabledProviders 查找所有服务商
@@ -119,7 +132,7 @@ func (this *RegionProviderDAO) UpdateProviderCustom(tx *dbs.Tx, providerId int64
} }
return this.Query(tx). return this.Query(tx).
Pk(providerId). Attr("valueId", providerId).
Set("customName", customName). Set("customName", customName).
Set("customCodes", customCodesJSON). Set("customCodes", customCodesJSON).
UpdateQuickly() UpdateQuickly()

View File

@@ -2,9 +2,20 @@ package regions
import "github.com/iwind/TeaGo/dbs" import "github.com/iwind/TeaGo/dbs"
const (
RegionProviderField_Id dbs.FieldName = "id" // ID
RegionProviderField_ValueId dbs.FieldName = "valueId" // 实际ID
RegionProviderField_Name dbs.FieldName = "name" // 名称
RegionProviderField_Codes dbs.FieldName = "codes" // 代号
RegionProviderField_CustomName dbs.FieldName = "customName" // 自定义名称
RegionProviderField_CustomCodes dbs.FieldName = "customCodes" // 自定义代号
RegionProviderField_State dbs.FieldName = "state" // 状态
)
// RegionProvider 区域-运营商 // RegionProvider 区域-运营商
type RegionProvider struct { type RegionProvider struct {
Id uint32 `field:"id"` // ID Id1 uint32 `field:"id"` // ID
ValueId uint32 `field:"valueId"` // 实际ID
Name string `field:"name"` // 名称 Name string `field:"name"` // 名称
Codes dbs.JSON `field:"codes"` // 代号 Codes dbs.JSON `field:"codes"` // 代号
CustomName string `field:"customName"` // 自定义名称 CustomName string `field:"customName"` // 自定义名称
@@ -13,12 +24,13 @@ type RegionProvider struct {
} }
type RegionProviderOperator struct { type RegionProviderOperator struct {
Id interface{} // ID Id any // ID
Name interface{} // 名称 ValueId any // 实际ID
Codes interface{} // 代号 Name any // 名称
CustomName interface{} // 自定义名称 Codes any // 代号
CustomCodes interface{} // 自定义代号 CustomName any // 自定义名称
State interface{} // 状态 CustomCodes any // 自定义代号
State any // 状态
} }
func NewRegionProviderOperator() *RegionProviderOperator { func NewRegionProviderOperator() *RegionProviderOperator {

View File

@@ -11,6 +11,7 @@ import (
"github.com/iwind/TeaGo/types" "github.com/iwind/TeaGo/types"
"sort" "sort"
"strconv" "strconv"
"strings"
) )
const ( const (
@@ -18,6 +19,8 @@ const (
RegionProvinceStateDisabled = 0 // 已禁用 RegionProvinceStateDisabled = 0 // 已禁用
) )
var RegionProvinceSuffixes = []string{"省", "州", "区", "大区", "特区", "港", "岛", "环礁", "谷地", "山", "口岸", "郡", "县", "城", "河", "河畔", "市"}
type RegionProvinceDAO dbs.DAO type RegionProvinceDAO dbs.DAO
func NewRegionProvinceDAO() *RegionProvinceDAO { func NewRegionProvinceDAO() *RegionProvinceDAO {
@@ -42,7 +45,7 @@ func init() {
// EnableRegionProvince 启用条目 // EnableRegionProvince 启用条目
func (this *RegionProvinceDAO) EnableRegionProvince(tx *dbs.Tx, id int64) error { func (this *RegionProvinceDAO) EnableRegionProvince(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx). _, err := this.Query(tx).
Pk(id). Attr("valueId", id).
Set("state", RegionProvinceStateEnabled). Set("state", RegionProvinceStateEnabled).
Update() Update()
return err return err
@@ -51,7 +54,7 @@ func (this *RegionProvinceDAO) EnableRegionProvince(tx *dbs.Tx, id int64) error
// DisableRegionProvince 禁用条目 // DisableRegionProvince 禁用条目
func (this *RegionProvinceDAO) DisableRegionProvince(tx *dbs.Tx, id int64) error { func (this *RegionProvinceDAO) DisableRegionProvince(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx). _, err := this.Query(tx).
Pk(id). Attr("valueId", id).
Set("state", RegionProvinceStateDisabled). Set("state", RegionProvinceStateDisabled).
Update() Update()
return err return err
@@ -60,7 +63,7 @@ func (this *RegionProvinceDAO) DisableRegionProvince(tx *dbs.Tx, id int64) error
// FindEnabledRegionProvince 查找启用中的条目 // FindEnabledRegionProvince 查找启用中的条目
func (this *RegionProvinceDAO) FindEnabledRegionProvince(tx *dbs.Tx, id int64) (*RegionProvince, error) { func (this *RegionProvinceDAO) FindEnabledRegionProvince(tx *dbs.Tx, id int64) (*RegionProvince, error) {
result, err := this.Query(tx). result, err := this.Query(tx).
Pk(id). Attr("valueId", id).
Attr("state", RegionProvinceStateEnabled). Attr("state", RegionProvinceStateEnabled).
Find() Find()
if result == nil { if result == nil {
@@ -72,27 +75,66 @@ func (this *RegionProvinceDAO) FindEnabledRegionProvince(tx *dbs.Tx, id int64) (
// FindRegionProvinceName 根据主键查找名称 // FindRegionProvinceName 根据主键查找名称
func (this *RegionProvinceDAO) FindRegionProvinceName(tx *dbs.Tx, id int64) (string, error) { func (this *RegionProvinceDAO) FindRegionProvinceName(tx *dbs.Tx, id int64) (string, error) {
return this.Query(tx). return this.Query(tx).
Pk(id). Attr("valueId", id).
Result("name"). Result("name").
FindStringCol("") FindStringCol("")
} }
// FindRegionCountryId 获取省份对应的国家|地区
func (this *RegionProvinceDAO) FindRegionCountryId(tx *dbs.Tx, provinceId int64) (int64, error) {
return this.Query(tx).
Attr("valueId", provinceId).
Result("countryId").
FindInt64Col(0)
}
// FindProvinceIdWithDataId 根据数据ID查找省份 // FindProvinceIdWithDataId 根据数据ID查找省份
func (this *RegionProvinceDAO) FindProvinceIdWithDataId(tx *dbs.Tx, dataId string) (int64, error) { func (this *RegionProvinceDAO) FindProvinceIdWithDataId(tx *dbs.Tx, dataId string) (int64, error) {
return this.Query(tx). return this.Query(tx).
Attr("dataId", dataId). Attr("dataId", dataId).
ResultPk(). Result(RegionProvinceField_ValueId).
FindInt64Col(0) FindInt64Col(0)
} }
// FindProvinceIdWithName 根据省份名查找省份ID // FindProvinceIdWithName 根据省份名查找省份ID
func (this *RegionProvinceDAO) FindProvinceIdWithName(tx *dbs.Tx, countryId int64, provinceName string) (int64, error) { func (this *RegionProvinceDAO) FindProvinceIdWithName(tx *dbs.Tx, countryId int64, provinceName string) (int64, error) {
{
provinceId, err := this.findProvinceIdWithExactName(tx, countryId, provinceName)
if err != nil {
return 0, err
}
if provinceId > 0 {
return provinceId, nil
}
}
// 候选词
for _, suffix := range RegionProvinceSuffixes {
var name string
if strings.HasSuffix(provinceName, suffix) {
name = strings.TrimSuffix(provinceName, suffix)
} else {
name = provinceName + suffix
}
provinceId, err := this.findProvinceIdWithExactName(tx, countryId, name)
if err != nil {
return 0, err
}
if provinceId > 0 {
return provinceId, nil
}
}
return 0, nil
}
func (this *RegionProvinceDAO) findProvinceIdWithExactName(tx *dbs.Tx, countryId int64, provinceName string) (int64, error) {
return this.Query(tx). return this.Query(tx).
Attr("countryId", countryId). Attr("countryId", countryId).
Where("(name=:provinceName OR customName=:provinceName OR JSON_CONTAINS(codes, :provinceNameJSON) OR JSON_CONTAINS(customCodes, :provinceNameJSON))"). Where("(name=:provinceName OR customName=:provinceName OR JSON_CONTAINS(codes, :provinceNameJSON) OR JSON_CONTAINS(customCodes, :provinceNameJSON))").
Param("provinceName", provinceName). Param("provinceName", provinceName).
Param("provinceNameJSON", strconv.Quote(provinceName)). // 查询的需要是个JSON字符串所以这里加双引号 Param("provinceNameJSON", strconv.Quote(provinceName)). // 查询的需要是个JSON字符串所以这里加双引号
ResultPk(). Result(RegionProvinceField_ValueId).
FindInt64Col(0) FindInt64Col(0)
} }
@@ -104,7 +146,7 @@ func (this *RegionProvinceDAO) CreateProvince(tx *dbs.Tx, countryId int64, name
op.DataId = dataId op.DataId = dataId
op.State = RegionProvinceStateEnabled op.State = RegionProvinceStateEnabled
codes := []string{name} var codes = []string{name}
codesJSON, err := json.Marshal(codes) codesJSON, err := json.Marshal(codes)
if err != nil { if err != nil {
return 0, err return 0, err
@@ -114,7 +156,17 @@ func (this *RegionProvinceDAO) CreateProvince(tx *dbs.Tx, countryId int64, name
if err != nil { if err != nil {
return 0, err return 0, err
} }
return types.Int64(op.Id), nil var provinceId = types.Int64(op.Id)
err = this.Query(tx).
Pk(provinceId).
Set(RegionProvinceField_ValueId, provinceId).
UpdateQuickly()
if err != nil {
return 0, err
}
return provinceId, nil
} }
// FindAllEnabledProvincesWithCountryId 查找某个国家/地区的所有省份 // FindAllEnabledProvincesWithCountryId 查找某个国家/地区的所有省份
@@ -122,7 +174,7 @@ func (this *RegionProvinceDAO) FindAllEnabledProvincesWithCountryId(tx *dbs.Tx,
_, err = this.Query(tx). _, err = this.Query(tx).
State(RegionProvinceStateEnabled). State(RegionProvinceStateEnabled).
Attr("countryId", countryId). Attr("countryId", countryId).
AscPk(). Asc(RegionProvinceField_ValueId).
Slice(&result). Slice(&result).
FindAll() FindAll()
return return
@@ -132,7 +184,7 @@ func (this *RegionProvinceDAO) FindAllEnabledProvincesWithCountryId(tx *dbs.Tx,
func (this *RegionProvinceDAO) FindAllEnabledProvinces(tx *dbs.Tx) (result []*RegionProvince, err error) { func (this *RegionProvinceDAO) FindAllEnabledProvinces(tx *dbs.Tx) (result []*RegionProvince, err error) {
_, err = this.Query(tx). _, err = this.Query(tx).
State(RegionProvinceStateEnabled). State(RegionProvinceStateEnabled).
AscPk(). Asc(RegionProvinceField_ValueId).
Slice(&result). Slice(&result).
FindAll() FindAll()
return return
@@ -149,7 +201,7 @@ func (this *RegionProvinceDAO) UpdateProvinceCustom(tx *dbs.Tx, provinceId int64
} }
return this.Query(tx). return this.Query(tx).
Pk(provinceId). Attr("valueId", provinceId).
Set("customName", customName). Set("customName", customName).
Set("customCodes", customCodesJSON). Set("customCodes", customCodesJSON).
UpdateQuickly() UpdateQuickly()

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