Compare commits

...

95 Commits

Author SHA1 Message Date
刘祥超
5789b1afb9 修改用户版本号 2022-01-16 20:38:53 +08:00
刘祥超
6f9f9dfb6f 源站支持客户端证书 2022-01-16 19:51:54 +08:00
刘祥超
171bafce6a WAF策略简要信息中增加serverId 2022-01-14 10:43:22 +08:00
刘祥超
7c7fecab26 可以使用集群搜索WAF策略、缓存策略 2022-01-11 15:46:41 +08:00
刘祥超
7afdf5a2d9 上传SQL 2022-01-11 15:29:03 +08:00
刘祥超
ee9718ac77 运行日志可以使用集群、节点筛选 2022-01-11 14:59:32 +08:00
刘祥超
15e10182da 访问日志可以使用集群和节点搜索 2022-01-11 12:03:20 +08:00
刘祥超
4341c5b738 增加读取当日拦截数API 2022-01-11 09:46:37 +08:00
刘祥超
cf9b31b8eb 自动升级SYN Flood配置 2022-01-10 20:07:26 +08:00
刘祥超
564d440cd1 实现自动SYN Flood防护 2022-01-10 19:54:37 +08:00
刘祥超
bad9231ab3 API相关HTTP请求增加User-Agent 2022-01-10 09:58:34 +08:00
刘祥超
a78333c490 上传SQL 2022-01-09 20:13:30 +08:00
刘祥超
ace44173ec WAF策略增加是否使用本地防火墙设置 2022-01-09 17:05:36 +08:00
刘祥超
5155ce97af 节点统计数据只保留两个小时 2022-01-09 11:14:05 +08:00
刘祥超
e2bf3ba1a4 节点配置增加产品信息 2022-01-09 10:44:17 +08:00
刘祥超
9abc65bd2b IP名单增加未读状态 2022-01-08 16:48:17 +08:00
刘祥超
a99156ea49 自动加入名单不需要即时更新 2022-01-08 15:24:51 +08:00
刘祥超
d35db163ae 增加城市/ISP查询接口 2022-01-06 16:25:23 +08:00
刘祥超
e7b0f0df90 部分API支持用户平台调用 2022-01-05 20:12:37 +08:00
刘祥超
eab09fa37a 用户创建服务时刻自动添加到分组 2022-01-05 15:55:02 +08:00
刘祥超
ec3f89ecf5 用户列表可以使用待审核、关键词搜索 2022-01-05 11:12:24 +08:00
刘祥超
d58106c7ca 实现用户注册/审核功能 2022-01-05 10:45:19 +08:00
刘祥超
5da924118d 更新数据库 2022-01-04 18:29:25 +08:00
刘祥超
51e37f0c52 可以在集群中配置是否自动在firewalld中开放端口 2022-01-03 16:27:50 +08:00
刘祥超
7e9e764322 节点日志可以使用标签筛选/级别可以填写多个,用逗号分隔 2022-01-03 11:04:14 +08:00
刘祥超
75e41fff4d 缩短节点运行日志保存时间 2022-01-03 10:19:17 +08:00
刘祥超
8a9842edaf 优化检查节点更新代码 2022-01-01 17:36:01 +08:00
刘祥超
4e8629d74a 实现初版边缘脚本 2021-12-31 15:21:27 +08:00
刘祥超
2e02aa556e 修改版本为0.4.0 2021-12-31 15:19:32 +08:00
刘祥超
974c95c20a 修改版本号0.3.8 2021-12-31 11:38:00 +08:00
刘祥超
3c0e6a900b 访问日志requestBody和responseBody字段从blob改为mediumblob 2021-12-22 21:08:01 +08:00
刘祥超
fb420f8fce 访问日志requestBody和responseBody字段从blob改为mediumblob 2021-12-22 20:39:15 +08:00
刘祥超
705e80f6c7 修改版本号为0.4.0 2021-12-20 20:02:07 +08:00
刘祥超
9a4be1f5a7 修复当数据库设置为lower_case_table_names=1时无法查询访问日志的Bug 2021-12-20 16:20:33 +08:00
刘祥超
7d82c3abf6 节点因阈值切换到备用IP时保持在线状态 2021-12-16 10:09:19 +08:00
刘祥超
55034efa87 优化代码 2021-12-15 20:45:51 +08:00
刘祥超
3ff45d6f49 优化ip2region查询代码 2021-12-15 20:17:01 +08:00
刘祥超
2b2c285c90 修改DNS版本为0.2.1 2021-12-15 10:47:32 +08:00
刘祥超
29ad1bc741 HTTP Header:实现请求方法、域名、状态码等限制,实现内容替换功能 2021-12-15 09:56:06 +08:00
刘祥超
a263ab5938 修改测试用例 2021-12-14 14:55:22 +08:00
刘祥超
6b34d32a8f 实现访问日志队列 2021-12-14 12:44:57 +08:00
刘祥超
3ff6ca5905 优化代码/增加edge-api goman命令 2021-12-14 10:49:29 +08:00
刘祥超
59ce71f72e WAF策略:可以修改分组代号/导入时可以根据名称合并 2021-12-12 20:24:36 +08:00
刘祥超
49da653ed8 上传SQL 2021-12-12 17:14:21 +08:00
刘祥超
12db6910dc 服务分组可以设置请求限制 2021-12-12 17:07:21 +08:00
刘祥超
3ce1aa14b5 路由规则增加专属域名设置 2021-12-12 16:38:31 +08:00
刘祥超
c67fbfa849 全局IP名单不即时通知节点更新 2021-12-12 14:38:57 +08:00
刘祥超
72b94c5035 实现请求连接数等限制 2021-12-12 11:46:23 +08:00
刘祥超
01ea13d283 IP名单添加新IP时删除老的IP内容 2021-12-10 11:12:34 +08:00
刘祥超
91378b26dd 上传SQL 2021-12-09 18:51:22 +08:00
刘祥超
46dc74195b 支持设置单节点最大线程数、单节点TCP最大连接数 2021-12-09 18:49:51 +08:00
刘祥超
63316b6b23 缩短各项统计清理时间 2021-12-08 10:30:14 +08:00
刘祥超
1b8600e04d 增加批量增加节点IP接口 2021-12-07 18:22:19 +08:00
刘祥超
11141d022f 访问日志实现记录requestBody 2021-12-07 14:57:55 +08:00
刘祥超
8fd29bd81c SSH认证支持sudo 2021-12-06 19:27:11 +08:00
刘祥超
820779e614 自动删除过期的IP 2021-12-06 11:24:18 +08:00
刘祥超
d45dc35ea6 自动删除过期的IP 2021-12-06 11:21:18 +08:00
刘祥超
b3280ee290 缩短监控数据清理时间为2天 2021-12-06 10:15:32 +08:00
刘祥超
ff0e89aa5f 自动将API节点的IP加入到白名单,防止误封 2021-12-06 10:09:37 +08:00
刘祥超
6ff5ba67e8 服务看板增加区域地图 2021-12-06 09:16:18 +08:00
刘祥超
e7474523ba 更新SQL 2021-12-05 21:14:57 +08:00
刘祥超
0b928aed14 优化代码 2021-12-05 19:53:37 +08:00
刘祥超
52dcd3da1f 非商业版不用记录每日地区统计 2021-12-05 19:43:33 +08:00
刘祥超
0260d6f735 商业版WAF看板增加地图 2021-12-05 19:38:32 +08:00
刘祥超
364636ea61 国家/地区统计时上传流量、攻击量等信息/多个统计增加自动清理程序 2021-12-05 18:58:14 +08:00
刘祥超
12f816cb5e 增加GRPC最大能接收的消息尺寸为128M 2021-12-04 19:02:12 +08:00
刘祥超
ef70d3465d 增加若干个统计数据清理程序 2021-12-04 11:31:07 +08:00
刘祥超
a3a9e726cb 更新sql 2021-12-03 15:46:21 +08:00
刘祥超
0c65774c82 首页看板显示未审核的服务数 2021-12-03 14:53:47 +08:00
刘祥超
fa31e547a2 优化指标数据清理 2021-12-03 12:16:00 +08:00
刘祥超
c406536511 优化指标数据清理 2021-12-03 12:06:13 +08:00
刘祥超
35f48e4f5d 增加统计指标自动清理 2021-12-03 10:57:40 +08:00
刘祥超
851e982ab9 修复通过IP查询IP名单时没有过滤已删除IP的Bug 2021-12-02 17:12:13 +08:00
刘祥超
956186613f WAF规则集中增加是否忽略局域网IP 2021-12-02 16:08:59 +08:00
刘祥超
aeb2a0c4f9 使用请求ID搜索访问日志时自动去除多余的空格和句号 2021-12-02 14:44:20 +08:00
刘祥超
4f05671af2 支持使用请求ID搜索访问日志 2021-12-02 11:50:16 +08:00
刘祥超
6b6170b183 当用户提交待审核域名时,给管理员发送消息 2021-12-01 17:20:09 +08:00
刘祥超
29f2aa2654 审核中服务增加提交审核时间 2021-12-01 17:06:16 +08:00
刘祥超
ed234b58bc 优化节点日志 2021-11-30 16:43:16 +08:00
刘祥超
cfcad531f4 用户账单增加多个API 2021-11-29 14:33:51 +08:00
刘祥超
12ffdfd849 健康检查不使用密钥加密 2021-11-29 09:52:47 +08:00
刘祥超
c2be7d70b8 完善套餐 2021-11-28 20:11:36 +08:00
刘祥超
efe723bc51 多个API支持查询用户查询 2021-11-28 14:28:00 +08:00
刘祥超
501559e3b8 版本号为0.3.7 2021-11-28 14:27:23 +08:00
刘祥超
de8b3c2195 节点任务查询时增加排除的任务类型 2021-11-27 17:07:01 +08:00
刘祥超
31aeb21e09 修复新启动节点时获取不到最新配置的Bug 2021-11-26 13:53:34 +08:00
刘祥超
f1cf89a78c 提交SQL 2021-11-26 10:45:02 +08:00
刘祥超
d6df5e1c48 提升全局IP名单变更通知速度 2021-11-26 10:39:50 +08:00
刘祥超
049b9b52dd 限制非商业版本从用户端登录 2021-11-24 20:06:43 +08:00
刘祥超
c53259008e 修改用户节点版本为0.2.0 2021-11-24 19:57:15 +08:00
刘祥超
6e0a0a099d 用户端实现UDP设置/优化检查端口是否已被使用API 2021-11-24 19:46:59 +08:00
刘祥超
f4313de55f 缩短统计指标数据保留时间 2021-11-24 19:45:51 +08:00
刘祥超
fd3823255c 服务增加是否合并URL中的多余分隔符选项 2021-11-24 14:49:51 +08:00
刘祥超
79823f4317 版本号改为0.3.6 2021-11-24 14:04:11 +08:00
刘祥超
99fd015066 优化API命名 2021-11-24 12:00:38 +08:00
172 changed files with 4860 additions and 1183 deletions

View File

@@ -11,6 +11,7 @@ import (
"github.com/iwind/TeaGo/Tea"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/gosock/pkg/gosock"
"log"
"os"
)
@@ -68,6 +69,20 @@ func main() {
}
fmt.Println("done")
})
app.On("goman", func() {
var sock = gosock.NewTmpSock(teaconst.ProcessName)
reply, err := sock.Send(&gosock.Command{Code: "goman"})
if err != nil {
fmt.Println("[ERROR]" + err.Error())
} else {
instancesJSON, err := json.MarshalIndent(reply.Params, "", " ")
if err != nil {
fmt.Println("[ERROR]" + err.Error())
} else {
fmt.Println(string(instancesJSON))
}
}
})
app.Run(func() {
nodes.NewAPINode().Start()
})

View File

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

View File

@@ -3,6 +3,7 @@ package accounts
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
_ "github.com/go-sql-driver/mysql"
@@ -16,7 +17,7 @@ import (
func init() {
dbs.OnReadyDone(func() {
go func() {
goman.New(func() {
// 自动支付账单任务
var ticker = time.NewTicker(12 * time.Hour)
for range ticker.C {
@@ -29,7 +30,7 @@ func init() {
}
}
}
}()
})
})
}
@@ -108,7 +109,7 @@ func (this *UserAccountDAO) UpdateUserAccount(tx *dbs.Tx, accountId int64, delta
if account == nil {
return errors.New("invalid account id '" + types.String(accountId) + "'")
}
var userId = int64(account.Id)
var userId = int64(account.UserId)
var deltaFloat64 = float64(delta)
if deltaFloat64 < 0 && account.Total < -deltaFloat64 {
return errors.New("not enough account quota to decrease")
@@ -235,3 +236,18 @@ func (this *UserAccountDAO) PayBills(tx *dbs.Tx) error {
return nil
}
// CheckUserAccount 检查用户账户
func (this *UserAccountDAO) CheckUserAccount(tx *dbs.Tx, userId int64, accountId int64) error {
exists, err := this.Query(tx).
Pk(accountId).
Attr("userId", userId).
Exist()
if err != nil {
return err
}
if !exists {
return models.ErrNotFound
}
return nil
}

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
acmeutils "github.com/TeaOSLab/EdgeAPI/internal/acme"
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/dns"
dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils"
@@ -400,6 +401,7 @@ func (this *ACMETaskDAO) runTaskWithoutLog(tx *dbs.Tx, taskId int64) (isOk bool,
client := utils.SharedHttpClient(5 * time.Second)
req, err := http.NewRequest(http.MethodPost, task.AuthURL, bytes.NewReader(authJSON))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", teaconst.ProductName+"/"+teaconst.Version)
if err != nil {
remotelogs.Error("ACME", "parse auth url failed '"+task.AuthURL+"': "+err.Error())
} else {

View File

@@ -8,9 +8,11 @@ import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
"net"
"strconv"
"strings"
)
@@ -321,3 +323,51 @@ func (this *APINodeDAO) CountAllEnabledAPINodesWithSSLPolicyIds(tx *dbs.Tx, sslP
Param("policyIds", strings.Join(policyStringIds, ",")).
Count()
}
// FindAllEnabledAPIAccessIPs 获取所有的API可访问IP地址
func (this *APINodeDAO) FindAllEnabledAPIAccessIPs(tx *dbs.Tx, cacheMap *utils.CacheMap) ([]string, error) {
var cacheKey = this.Table + ":FindAllEnabledAPIAccessIPs"
if cacheMap != nil {
cache, ok := cacheMap.Get(cacheKey)
if ok {
return cache.([]string), nil
}
}
ones, _, err := this.Query(tx).
State(APINodeStateEnabled).
Result("JSON_EXTRACT(accessAddrs, '$[*].host') AS host").
FindOnes()
if err != nil {
return nil, err
}
var result = []string{}
for _, one := range ones {
var host = one.GetString("host")
if len(host) == 0 {
continue
}
var ips = []string{}
err = json.Unmarshal([]byte(host), &ips)
if err != nil {
continue
}
for _, ip := range ips {
if !lists.ContainsString(result, ip) {
if net.ParseIP(ip) == nil {
continue
}
result = append(result, ip)
}
}
}
if cacheMap != nil {
cacheMap.Put(cacheKey, result)
}
return result, nil
}

View File

@@ -1,6 +1,7 @@
package models
import (
"github.com/TeaOSLab/EdgeAPI/internal/utils"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/dbs"
"runtime"
@@ -27,6 +28,12 @@ func TestAPINodeDAO_FindEnabledAPINodeIdWithAddr(t *testing.T) {
}
}
func TestAPINodeDAO_FindAllEnabledAPIAccessIPs(t *testing.T) {
var cacheMap = utils.NewCacheMap()
t.Log(NewAPINodeDAO().FindAllEnabledAPIAccessIPs(nil, cacheMap))
t.Log(NewAPINodeDAO().FindAllEnabledAPIAccessIPs(nil, cacheMap))
}
func BenchmarkAPINodeDAO_New(b *testing.B) {
runtime.GOMAXPROCS(1)
for i := 0; i < b.N; i++ {

View File

@@ -3,7 +3,9 @@ package models
import (
"fmt"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/lists"
@@ -49,7 +51,9 @@ type NSAccessLogDAOWrapper struct {
func init() {
initializer := NewDBNodeInitializer()
dbs.OnReadyDone(func() {
go initializer.Start()
goman.New(func() {
initializer.Start()
})
})
}
@@ -138,7 +142,7 @@ func findNSAccessLogTableName(db *dbs.DB, day string) (tableName string, ok bool
return tableName, false, err
}
return tableName, lists.ContainsString(tableNames, tableName), nil
return tableName, utils.ContainsStringInsensitive(tableNames, tableName), nil
}
// 根据日期获取表名
@@ -165,7 +169,7 @@ func findHTTPAccessLogTable(db *dbs.DB, day string, force bool) (*httpAccessLogD
return nil, err
}
if lists.ContainsString(tableNames, tableName) {
if utils.ContainsStringInsensitive(tableNames, tableName) {
table, err := db.FindTable(tableName)
if err != nil {
return nil, err
@@ -193,7 +197,7 @@ func findHTTPAccessLogTable(db *dbs.DB, day string, force bool) (*httpAccessLogD
}
// 创建表格
_, 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` blob COMMENT '请求内容',\n `responseBody` blob 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(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='访问日志';")
if err != nil {
return nil, err
}
@@ -233,7 +237,7 @@ func findNSAccessLogTable(db *dbs.DB, day string, force bool) (string, error) {
return tableName, err
}
if lists.ContainsString(tableNames, tableName) {
if utils.ContainsStringInsensitive(tableNames, tableName) {
accessLogLocker.Lock()
nsAccessLogTableMapping[cacheKey] = true
accessLogLocker.Unlock()
@@ -353,7 +357,7 @@ func (this *DBNodeInitializer) loop() error {
remotelogs.Error("DB_NODE", "create first table in database node failed: "+err.Error())
// 创建节点日志
createLogErr := SharedNodeLogDAO.CreateLog(nil, nodeconfigs.NodeRoleDatabase, nodeId, 0, 0, "error", "ACCESS_LOG", "can not create access log table: "+err.Error(), time.Now().Unix())
createLogErr := SharedNodeLogDAO.CreateLog(nil, nodeconfigs.NodeRoleDatabase, nodeId, 0, 0, "error", "ACCESS_LOG", "can not create access log table: "+err.Error(), time.Now().Unix(), "", nil)
if createLogErr != nil {
remotelogs.Error("NODE_LOG", createLogErr.Error())
}
@@ -361,6 +365,7 @@ func (this *DBNodeInitializer) loop() error {
continue
} else {
err = nil
continue
}
}
@@ -397,7 +402,7 @@ func (this *DBNodeInitializer) loop() error {
remotelogs.Error("DB_NODE", "create first table in database node failed: "+err.Error())
// 创建节点日志
createLogErr := SharedNodeLogDAO.CreateLog(nil, nodeconfigs.NodeRoleDatabase, nodeId, 0, 0, "error", "ACCESS_LOG", "can not create access log table: "+err.Error(), time.Now().Unix())
createLogErr := SharedNodeLogDAO.CreateLog(nil, nodeconfigs.NodeRoleDatabase, nodeId, 0, 0, "error", "ACCESS_LOG", "can not create access log table: "+err.Error(), time.Now().Unix(), "", nil)
if createLogErr != nil {
remotelogs.Error("NODE_LOG", createLogErr.Error())
}

View File

@@ -1,16 +1,23 @@
package models
import (
"bytes"
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeAPI/internal/zero"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"net"
@@ -26,10 +33,53 @@ type HTTPAccessLogDAO dbs.DAO
var SharedHTTPAccessLogDAO *HTTPAccessLogDAO
// 队列
var oldAccessLogQueue = make(chan *pb.HTTPAccessLog)
var accessLogQueue = make(chan *pb.HTTPAccessLog, 10_000)
var accessLogQueueMaxLength = 100_000
var accessLogQueuePercent = 100 // 0-100
var accessLogCountPerSecond = 10_000 // 0 表示不限制
var accessLogConfigJSON = []byte{}
var accessLogQueueChanged = make(chan zero.Zero, 1)
func init() {
dbs.OnReady(func() {
SharedHTTPAccessLogDAO = NewHTTPAccessLogDAO()
})
// 队列相关
dbs.OnReadyDone(func() {
// 检查队列变化
goman.New(func() {
var ticker = time.NewTicker(60 * time.Second)
// 先执行一次初始化
SharedHTTPAccessLogDAO.SetupQueue()
// 循环执行
for {
select {
case <-ticker.C:
SharedHTTPAccessLogDAO.SetupQueue()
case <-accessLogQueueChanged:
SharedHTTPAccessLogDAO.SetupQueue()
}
}
})
// 导出队列内容
goman.New(func() {
var ticker = time.NewTicker(1 * time.Second)
for range ticker.C {
var tx *dbs.Tx
err := SharedHTTPAccessLogDAO.DumpAccessLogsFromQueue(tx, accessLogCountPerSecond)
if err != nil {
remotelogs.Error("HTTP_ACCESS_LOG_QUEUE", "dump access logs failed: "+err.Error())
}
}
})
})
}
func NewHTTPAccessLogDAO() *HTTPAccessLogDAO {
@@ -45,6 +95,31 @@ func NewHTTPAccessLogDAO() *HTTPAccessLogDAO {
// CreateHTTPAccessLogs 创建访问日志
func (this *HTTPAccessLogDAO) CreateHTTPAccessLogs(tx *dbs.Tx, accessLogs []*pb.HTTPAccessLog) error {
// 写入队列
var queue = accessLogQueue // 这样写非常重要,防止在写入过程中队列有切换
for _, accessLog := range accessLogs {
if accessLog.FirewallPolicyId == 0 { // 如果是WAF记录则采取采样率
// 采样率
if accessLogQueuePercent <= 0 {
return nil
}
if accessLogQueuePercent < 100 && rands.Int(1, 100) > accessLogQueuePercent {
return nil
}
}
select {
case queue <- accessLog:
default:
// 超出的丢弃
}
}
return nil
}
// DumpAccessLogsFromQueue 从队列导入访问日志
func (this *HTTPAccessLogDAO) DumpAccessLogsFromQueue(tx *dbs.Tx, size int) error {
dao := randomHTTPAccessLogDAO()
if dao == nil {
dao = &HTTPAccessLogDAOWrapper{
@@ -52,75 +127,102 @@ func (this *HTTPAccessLogDAO) CreateHTTPAccessLogs(tx *dbs.Tx, accessLogs []*pb.
NodeId: 0,
}
}
return this.CreateHTTPAccessLogsWithDAO(tx, dao, accessLogs)
if size <= 0 {
size = 1_000_000
}
// 复制变量,防止中途改变
var oldQueue = oldAccessLogQueue
var newQueue = accessLogQueue
Loop:
for i := 0; i < size; i++ {
// old
select {
case accessLog := <-oldQueue:
err := this.CreateHTTPAccessLog(tx, dao.DAO, accessLog)
if err != nil {
return err
}
continue Loop
default:
}
// new
select {
case accessLog := <-newQueue:
err := this.CreateHTTPAccessLog(tx, dao.DAO, accessLog)
if err != nil {
return err
}
continue Loop
default:
break Loop
}
}
return nil
}
// CreateHTTPAccessLogsWithDAO 使用特定的DAO创建访问日志
func (this *HTTPAccessLogDAO) CreateHTTPAccessLogsWithDAO(tx *dbs.Tx, daoWrapper *HTTPAccessLogDAOWrapper, accessLogs []*pb.HTTPAccessLog) error {
if daoWrapper == nil {
return errors.New("dao should not be nil")
}
if len(accessLogs) == 0 {
return nil
// CreateHTTPAccessLog 写入单条访问日志
func (this *HTTPAccessLogDAO) CreateHTTPAccessLog(tx *dbs.Tx, dao *HTTPAccessLogDAO, accessLog *pb.HTTPAccessLog) error {
day := timeutil.Format("Ymd", time.Unix(accessLog.Timestamp, 0))
tableDef, err := findHTTPAccessLogTable(dao.Instance, day, false)
if err != nil {
return err
}
dao := daoWrapper.DAO
fields := map[string]interface{}{}
fields["serverId"] = accessLog.ServerId
fields["nodeId"] = accessLog.NodeId
fields["status"] = accessLog.Status
fields["createdAt"] = accessLog.Timestamp
fields["requestId"] = accessLog.RequestId
fields["firewallPolicyId"] = accessLog.FirewallPolicyId
fields["firewallRuleGroupId"] = accessLog.FirewallRuleGroupId
fields["firewallRuleSetId"] = accessLog.FirewallRuleSetId
fields["firewallRuleId"] = accessLog.FirewallRuleId
// TODO 改成事务批量提交,以加快速度
if len(accessLog.RequestBody) > 0 {
fields["requestBody"] = accessLog.RequestBody
accessLog.RequestBody = nil
}
for _, accessLog := range accessLogs {
day := timeutil.Format("Ymd", time.Unix(accessLog.Timestamp, 0))
tableDef, err := findHTTPAccessLogTable(dao.Instance, day, false)
if err != nil {
return err
}
if tableDef.HasRemoteAddr {
fields["remoteAddr"] = accessLog.RemoteAddr
}
if tableDef.HasDomain {
fields["domain"] = accessLog.Host
}
fields := map[string]interface{}{}
fields["serverId"] = accessLog.ServerId
fields["nodeId"] = accessLog.NodeId
fields["status"] = accessLog.Status
fields["createdAt"] = accessLog.Timestamp
fields["requestId"] = accessLog.RequestId
fields["firewallPolicyId"] = accessLog.FirewallPolicyId
fields["firewallRuleGroupId"] = accessLog.FirewallRuleGroupId
fields["firewallRuleSetId"] = accessLog.FirewallRuleSetId
fields["firewallRuleId"] = accessLog.FirewallRuleId
content, err := json.Marshal(accessLog)
if err != nil {
return err
}
fields["content"] = content
// TODO 根据集群、服务设置获取IP
if tableDef.HasRemoteAddr {
fields["remoteAddr"] = accessLog.RemoteAddr
}
if tableDef.HasDomain {
fields["domain"] = accessLog.Host
}
content, err := json.Marshal(accessLog)
if err != nil {
return err
}
fields["content"] = content
_, err = dao.Query(tx).
Table(tableDef.Name).
Sets(fields).
Insert()
if err != nil {
// 是否为 Error 1146: Table 'xxx.xxx' doesn't exist 如果是,则创建表之后重试
if strings.Contains(err.Error(), "1146") {
tableDef, err = findHTTPAccessLogTable(dao.Instance, day, true)
if err != nil {
return err
}
_, err = dao.Query(tx).
Table(tableDef.Name).
Sets(fields).
Insert()
if err != nil {
return err
}
} else {
logs.Println("HTTP_ACCESS_LOG", err.Error())
_, err = dao.Query(tx).
Table(tableDef.Name).
Sets(fields).
Insert()
if err != nil {
// 是否为 Error 1146: Table 'xxx.xxx' doesn't exist 如果是,则创建表之后重试
if strings.Contains(err.Error(), "1146") {
tableDef, err = findHTTPAccessLogTable(dao.Instance, day, true)
if err != nil {
return err
}
_, err = dao.Query(tx).
Table(tableDef.Name).
Sets(fields).
Insert()
if err != nil {
return err
}
} else {
remotelogs.Error("HTTP_ACCESS_LOG", err.Error())
}
}
@@ -131,6 +233,8 @@ func (this *HTTPAccessLogDAO) CreateHTTPAccessLogsWithDAO(tx *dbs.Tx, daoWrapper
func (this *HTTPAccessLogDAO) ListAccessLogs(tx *dbs.Tx, lastRequestId string,
size int64,
day string,
clusterId int64,
nodeId int64,
serverId int64,
reverse bool,
hasError bool,
@@ -151,18 +255,18 @@ func (this *HTTPAccessLogDAO) ListAccessLogs(tx *dbs.Tx, lastRequestId string,
size = 1000
}
result, nextLastRequestId, err = this.listAccessLogs(tx, lastRequestId, size, day, serverId, reverse, hasError, firewallPolicyId, firewallRuleGroupId, firewallRuleSetId, hasFirewallPolicy, userId, keyword, ip, domain)
result, nextLastRequestId, err = this.listAccessLogs(tx, lastRequestId, size, day, clusterId, nodeId, serverId, reverse, hasError, firewallPolicyId, firewallRuleGroupId, firewallRuleSetId, hasFirewallPolicy, userId, keyword, ip, domain)
if err != nil || int64(len(result)) < size {
return
}
moreResult, _, _ := this.listAccessLogs(tx, nextLastRequestId, 1, day, serverId, reverse, hasError, firewallPolicyId, firewallRuleGroupId, firewallRuleSetId, hasFirewallPolicy, userId, keyword, ip, domain)
moreResult, _, _ := this.listAccessLogs(tx, nextLastRequestId, 1, day, clusterId, nodeId, serverId, reverse, hasError, firewallPolicyId, firewallRuleGroupId, firewallRuleSetId, hasFirewallPolicy, userId, keyword, ip, domain)
hasMore = len(moreResult) > 0
return
}
// 读取往前的单页访问日志
func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx, lastRequestId string, size int64, day string, serverId int64, reverse bool, hasError bool, firewallPolicyId int64, firewallRuleGroupId int64, firewallRuleSetId int64, hasFirewallPolicy bool, userId int64, keyword string, ip string, domain string) (result []*HTTPAccessLog, nextLastRequestId string, err error) {
func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx, lastRequestId string, size int64, day string, clusterId int64, nodeId int64, serverId int64, reverse bool, hasError bool, firewallPolicyId int64, firewallRuleGroupId int64, firewallRuleSetId int64, hasFirewallPolicy bool, userId int64, keyword string, ip string, domain string) (result []*HTTPAccessLog, nextLastRequestId string, err error) {
if size <= 0 {
return nil, lastRequestId, nil
}
@@ -204,18 +308,42 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx, lastRequestId string, s
dao := daoWrapper.DAO
tableName, hasRemoteAddrField, hasDomainField, exists, err := findHTTPAccessLogTableName(dao.Instance, day)
if !exists {
// 表格不存在则跳过
return
}
if err != nil {
logs.Println("[DB_NODE]" + err.Error())
return
}
if !exists {
// 表格不存在则跳过
return
}
query := dao.Query(tx)
// 条件
if nodeId > 0 {
query.Attr("nodeId", nodeId)
} else if clusterId > 0 {
nodeIds, err := SharedNodeDAO.FindAllEnabledNodeIdsWithClusterId(tx, clusterId)
if err != nil {
remotelogs.Error("DBNODE", err.Error())
return
}
if len(nodeIds) > 0 {
sort.Slice(nodeIds, func(i, j int) bool {
return nodeIds[i] < nodeIds[j]
})
var nodeIdStrings = []string{}
for _, subNodeId := range nodeIds {
nodeIdStrings = append(nodeIdStrings, types.String(subNodeId))
}
query.Where("nodeId IN (" + strings.Join(nodeIdStrings, ",") + ")")
query.Reuse(false)
} else {
// 如果没有节点,则直接返回空
return
}
}
if serverId > 0 {
query.Attr("serverId", serverId)
} else if userId > 0 && len(serverIds) > 0 {
@@ -330,6 +458,11 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx, lastRequestId string, s
query.Param("intKeyword2", types.Int(pieces[1]))
}
if regexp.MustCompile(`^\d{20,}\s*\.?$`).MatchString(keyword) {
where += " OR requestId=:requestId"
query.Param("requestId", strings.TrimRight(keyword, ". "))
}
query.Where("("+where+")").
Param("keyword", "%"+keyword+"%")
if useOriginKeyword {
@@ -459,3 +592,42 @@ func (this *HTTPAccessLogDAO) FindAccessLogWithRequestId(tx *dbs.Tx, requestId s
wg.Wait()
return result, nil
}
// SetupQueue 建立队列
func (this *HTTPAccessLogDAO) SetupQueue() {
configJSON, err := SharedSysSettingDAO.ReadSetting(nil, systemconfigs.SettingCodeAccessLogQueue)
if err != nil {
remotelogs.Error("HTTP_ACCESS_LOG_QUEUE", "read settings failed: "+err.Error())
return
}
if len(configJSON) == 0 {
return
}
if bytes.Compare(accessLogConfigJSON, configJSON) == 0 {
return
}
accessLogConfigJSON = configJSON
var config = &serverconfigs.AccessLogQueueConfig{}
err = json.Unmarshal(configJSON, config)
if err != nil {
remotelogs.Error("HTTP_ACCESS_LOG_QUEUE", "decode settings failed: "+err.Error())
return
}
accessLogQueuePercent = config.Percent
accessLogCountPerSecond = config.CountPerSecond
if config.MaxLength <= 0 {
config.MaxLength = 100_000
}
if accessLogQueueMaxLength != config.MaxLength {
accessLogQueueMaxLength = config.MaxLength
oldAccessLogQueue = accessLogQueue
accessLogQueue = make(chan *pb.HTTPAccessLog, config.MaxLength)
}
remotelogs.Println("HTTP_ACCESS_LOG_QUEUE", "change queue max length: "+types.String(config.MaxLength)+", percent: "+types.String(config.Percent)+", countPerSecond: "+types.String(config.CountPerSecond))
}

View File

@@ -10,7 +10,9 @@ import (
"time"
)
func TestCreateHTTPAccessLogs(t *testing.T) {
func TestCreateHTTPAccessLog(t *testing.T) {
dbs.NotifyReady()
var tx *dbs.Tx
err := NewDBNodeInitializer().loop()
@@ -26,9 +28,19 @@ func TestCreateHTTPAccessLogs(t *testing.T) {
}
dao := randomHTTPAccessLogDAO()
t.Log("dao:", dao)
err = SharedHTTPAccessLogDAO.CreateHTTPAccessLogsWithDAO(tx, dao, []*pb.HTTPAccessLog{accessLog})
if err != nil {
t.Fatal(err)
// 先初始化
_ = SharedHTTPAccessLogDAO.CreateHTTPAccessLog(tx, dao.DAO, accessLog)
var before = time.Now()
defer func() {
t.Log(time.Since(before).Seconds()*1000, "ms")
}()
for i := 0; i < 1000; i++ {
err = SharedHTTPAccessLogDAO.CreateHTTPAccessLog(tx, dao.DAO, accessLog)
if err != nil {
t.Fatal(err)
}
}
t.Log("ok")
}

View File

@@ -13,5 +13,6 @@ func (this *HTTPAccessLog) ToPB() (*pb.HTTPAccessLog, error) {
return nil, err
}
p.RequestId = this.RequestId
p.RequestBody = []byte(this.RequestBody)
return p, nil
}

View File

@@ -300,9 +300,13 @@ func (this *HTTPCachePolicyDAO) ComposeCachePolicy(tx *dbs.Tx, policyId int64, c
}
// CountAllEnabledHTTPCachePolicies 计算可用缓存策略数量
func (this *HTTPCachePolicyDAO) CountAllEnabledHTTPCachePolicies(tx *dbs.Tx, keyword string) (int64, error) {
func (this *HTTPCachePolicyDAO) CountAllEnabledHTTPCachePolicies(tx *dbs.Tx, clusterId int64, keyword string) (int64, error) {
query := this.Query(tx).
State(HTTPCachePolicyStateEnabled)
if clusterId > 0 {
query.Where("id IN (SELECT cachePolicyId FROM " + SharedNodeClusterDAO.Table + " WHERE id=:clusterId)")
query.Param("clusterId", clusterId)
}
if len(keyword) > 0 {
query.Where("(name LIKE :keyword)").
Param("keyword", "%"+keyword+"%")
@@ -311,9 +315,13 @@ func (this *HTTPCachePolicyDAO) CountAllEnabledHTTPCachePolicies(tx *dbs.Tx, key
}
// ListEnabledHTTPCachePolicies 列出单页的缓存策略
func (this *HTTPCachePolicyDAO) ListEnabledHTTPCachePolicies(tx *dbs.Tx, keyword string, offset int64, size int64) ([]*serverconfigs.HTTPCachePolicy, error) {
func (this *HTTPCachePolicyDAO) ListEnabledHTTPCachePolicies(tx *dbs.Tx, clusterId int64, keyword string, offset int64, size int64) ([]*serverconfigs.HTTPCachePolicy, error) {
query := this.Query(tx).
State(HTTPCachePolicyStateEnabled)
if clusterId > 0 {
query.Where("id IN (SELECT cachePolicyId FROM " + SharedNodeClusterDAO.Table + " WHERE id=:clusterId)")
query.Param("clusterId", clusterId)
}
if len(keyword) > 0 {
query.Where("(name LIKE :keyword)").
Param("keyword", "%"+keyword+"%")

View File

@@ -130,6 +130,16 @@ func (this *HTTPFirewallPolicyDAO) CreateFirewallPolicy(tx *dbs.Tx, userId int64
if len(outboundJSON) > 0 {
op.Outbound = outboundJSON
}
op.UseLocalFirewall = true
{
synFloodJSON, err := json.Marshal(firewallconfigs.DefaultSYNFloodConfig())
if err != nil {
return 0, err
}
op.SynFlood = synFloodJSON
}
err := this.Save(tx, op)
return types.Int64(op.Id), err
}
@@ -249,7 +259,7 @@ func (this *HTTPFirewallPolicyDAO) UpdateFirewallPolicyInbound(tx *dbs.Tx, polic
}
// UpdateFirewallPolicy 修改策略
func (this *HTTPFirewallPolicyDAO) UpdateFirewallPolicy(tx *dbs.Tx, policyId int64, isOn bool, name string, description string, inboundJSON []byte, outboundJSON []byte, blockOptionsJSON []byte, mode firewallconfigs.FirewallMode) error {
func (this *HTTPFirewallPolicyDAO) UpdateFirewallPolicy(tx *dbs.Tx, policyId int64, isOn bool, name string, description string, inboundJSON []byte, outboundJSON []byte, blockOptionsJSON []byte, mode firewallconfigs.FirewallMode, useLocalFirewall bool, synFloodConfig *firewallconfigs.SYNFloodConfig) error {
if policyId <= 0 {
return errors.New("invalid policyId")
}
@@ -272,6 +282,18 @@ func (this *HTTPFirewallPolicyDAO) UpdateFirewallPolicy(tx *dbs.Tx, policyId int
if len(blockOptionsJSON) > 0 {
op.BlockOptions = blockOptionsJSON
}
if synFloodConfig != nil {
synFloodConfigJSON, err := json.Marshal(synFloodConfig)
if err != nil {
return err
}
op.SynFlood = synFloodConfigJSON
} else {
op.SynFlood = "null"
}
op.UseLocalFirewall = useLocalFirewall
err := this.Save(tx, op)
if err != nil {
return err
@@ -281,8 +303,12 @@ func (this *HTTPFirewallPolicyDAO) UpdateFirewallPolicy(tx *dbs.Tx, policyId int
}
// CountAllEnabledFirewallPolicies 计算所有可用的策略数量
func (this *HTTPFirewallPolicyDAO) CountAllEnabledFirewallPolicies(tx *dbs.Tx, keyword string) (int64, error) {
func (this *HTTPFirewallPolicyDAO) CountAllEnabledFirewallPolicies(tx *dbs.Tx, clusterId int64, keyword string) (int64, error) {
query := this.Query(tx)
if clusterId > 0 {
query.Where("id IN (SELECT httpFirewallPolicyId FROM " + SharedNodeClusterDAO.Table + " WHERE id=:clusterId)")
query.Param("clusterId", clusterId)
}
if len(keyword) > 0 {
query.Where("(name LIKE :keyword)").
Param("keyword", "%"+keyword+"%")
@@ -296,8 +322,12 @@ func (this *HTTPFirewallPolicyDAO) CountAllEnabledFirewallPolicies(tx *dbs.Tx, k
}
// ListEnabledFirewallPolicies 列出单页的策略
func (this *HTTPFirewallPolicyDAO) ListEnabledFirewallPolicies(tx *dbs.Tx, keyword string, offset int64, size int64) (result []*HTTPFirewallPolicy, err error) {
func (this *HTTPFirewallPolicyDAO) ListEnabledFirewallPolicies(tx *dbs.Tx, clusterId int64, keyword string, offset int64, size int64) (result []*HTTPFirewallPolicy, err error) {
query := this.Query(tx)
if clusterId > 0 {
query.Where("id IN (SELECT httpFirewallPolicyId FROM " + SharedNodeClusterDAO.Table + " WHERE id=:clusterId)")
query.Param("clusterId", clusterId)
}
if len(keyword) > 0 {
query.Where("(name LIKE :keyword)").
Param("keyword", "%"+keyword+"%")
@@ -339,6 +369,7 @@ func (this *HTTPFirewallPolicyDAO) ComposeFirewallPolicy(tx *dbs.Tx, policyId in
config.IsOn = policy.IsOn == 1
config.Name = policy.Name
config.Description = policy.Description
config.UseLocalFirewall = policy.UseLocalFirewall == 1
if len(policy.Mode) == 0 {
policy.Mode = firewallconfigs.FirewallModeDefend
@@ -411,6 +442,16 @@ func (this *HTTPFirewallPolicyDAO) ComposeFirewallPolicy(tx *dbs.Tx, policyId in
config.BlockOptions = blockAction
}
// syn flood
if len(policy.SynFlood) > 0 {
var synFloodConfig = &firewallconfigs.SYNFloodConfig{}
err = json.Unmarshal([]byte(policy.SynFlood), synFloodConfig)
if err != nil {
return nil, err
}
config.SYNFlood = synFloodConfig
}
if cacheMap != nil {
cacheMap.Put(cacheKey, config)
}

View File

@@ -2,39 +2,43 @@ package models
// HTTPFirewallPolicy HTTP防火墙
type HTTPFirewallPolicy struct {
Id uint32 `field:"id"` // ID
TemplateId uint32 `field:"templateId"` // 模版ID
AdminId uint32 `field:"adminId"` // 管理员ID
UserId uint32 `field:"userId"` // 用户ID
ServerId uint32 `field:"serverId"` // 服务ID
GroupId uint32 `field:"groupId"` // 服务分组ID
State uint8 `field:"state"` // 状态
CreatedAt uint64 `field:"createdAt"` // 创建时间
IsOn uint8 `field:"isOn"` // 是否启用
Name string `field:"name"` // 名称
Description string `field:"description"` // 描述
Inbound string `field:"inbound"` // 入站规则
Outbound string `field:"outbound"` // 出站规则
BlockOptions string `field:"blockOptions"` // BLOCK选项
Mode string `field:"mode"` // 模式
Id uint32 `field:"id"` // ID
TemplateId uint32 `field:"templateId"` // 模版ID
AdminId uint32 `field:"adminId"` // 管理员ID
UserId uint32 `field:"userId"` // 用户ID
ServerId uint32 `field:"serverId"` // 服务ID
GroupId uint32 `field:"groupId"` // 服务分组ID
State uint8 `field:"state"` // 状态
CreatedAt uint64 `field:"createdAt"` // 创建时间
IsOn uint8 `field:"isOn"` // 是否启用
Name string `field:"name"` // 名称
Description string `field:"description"` // 描述
Inbound string `field:"inbound"` // 入站规则
Outbound string `field:"outbound"` // 出站规则
BlockOptions string `field:"blockOptions"` // BLOCK选项
Mode string `field:"mode"` // 模式
UseLocalFirewall uint8 `field:"useLocalFirewall"` // 是否自动使用本地防火墙
SynFlood string `field:"synFlood"` // SynFlood防御设置
}
type HTTPFirewallPolicyOperator struct {
Id interface{} // ID
TemplateId interface{} // 模版ID
AdminId interface{} // 管理员ID
UserId interface{} // 用户ID
ServerId interface{} // 服务ID
GroupId interface{} // 服务分组ID
State interface{} // 状态
CreatedAt interface{} // 创建时间
IsOn interface{} // 是否启用
Name interface{} // 名称
Description interface{} // 描述
Inbound interface{} // 入站规则
Outbound interface{} // 出站规则
BlockOptions interface{} // BLOCK选项
Mode interface{} // 模式
Id interface{} // ID
TemplateId interface{} // 模版ID
AdminId interface{} // 管理员ID
UserId interface{} // 用户ID
ServerId interface{} // 服务ID
GroupId interface{} // 服务分组ID
State interface{} // 状态
CreatedAt interface{} // 创建时间
IsOn interface{} // 是否启用
Name interface{} // 名称
Description interface{} // 描述
Inbound interface{} // 入站规则
Outbound interface{} // 出站规则
BlockOptions interface{} // BLOCK选项
Mode interface{} // 模式
UseLocalFirewall interface{} // 是否自动使用本地防火墙
SynFlood interface{} // SynFlood防御设置
}
func NewHTTPFirewallPolicyOperator() *HTTPFirewallPolicyOperator {

View File

@@ -95,6 +95,7 @@ func (this *HTTPFirewallRuleGroupDAO) ComposeFirewallRuleGroup(tx *dbs.Tx, group
config.Name = group.Name
config.Description = group.Description
config.Code = group.Code
config.IsTemplate = group.IsTemplate == 1
if IsNotNull(group.Sets) {
setRefs := []*firewallconfigs.HTTPFirewallRuleSetRef{}
@@ -125,6 +126,7 @@ func (this *HTTPFirewallRuleGroupDAO) CreateGroupFromConfig(tx *dbs.Tx, groupCon
op.Description = groupConfig.Description
op.State = HTTPFirewallRuleGroupStateEnabled
op.Code = groupConfig.Code
op.IsTemplate = groupConfig.IsTemplate
// sets
setRefs := []*firewallconfigs.HTTPFirewallRuleSetRef{}
@@ -178,7 +180,7 @@ func (this *HTTPFirewallRuleGroupDAO) CreateGroup(tx *dbs.Tx, isOn bool, name st
}
// UpdateGroup 修改分组
func (this *HTTPFirewallRuleGroupDAO) UpdateGroup(tx *dbs.Tx, groupId int64, isOn bool, name string, description string) error {
func (this *HTTPFirewallRuleGroupDAO) UpdateGroup(tx *dbs.Tx, groupId int64, isOn bool, name string, code string, description string) error {
if groupId <= 0 {
return errors.New("invalid groupId")
}
@@ -186,6 +188,7 @@ func (this *HTTPFirewallRuleGroupDAO) UpdateGroup(tx *dbs.Tx, groupId int64, isO
op.Id = groupId
op.IsOn = isOn
op.Name = name
op.Code = code
op.Description = description
err := this.Save(tx, op)
if err != nil {

View File

@@ -1,12 +1,13 @@
package models
// 防火墙规则分组
// HTTPFirewallRuleGroup 防火墙规则分组
type HTTPFirewallRuleGroup struct {
Id uint32 `field:"id"` // ID
IsOn uint8 `field:"isOn"` // 是否启用
Name string `field:"name"` // 名称
Description string `field:"description"` // 描述
Code string `field:"code"` // 代号
IsTemplate uint8 `field:"isTemplate"` // 是否为预置模板
AdminId uint32 `field:"adminId"` // 管理员ID
UserId uint32 `field:"userId"` // 用户ID
State uint8 `field:"state"` // 状态
@@ -20,6 +21,7 @@ type HTTPFirewallRuleGroupOperator struct {
Name interface{} // 名称
Description interface{} // 描述
Code interface{} // 代号
IsTemplate interface{} // 是否为预置模板
AdminId interface{} // 管理员ID
UserId interface{} // 用户ID
State interface{} // 状态

View File

@@ -99,6 +99,7 @@ func (this *HTTPFirewallRuleSetDAO) ComposeFirewallRuleSet(tx *dbs.Tx, setId int
config.Description = set.Description
config.Code = set.Code
config.Connector = set.Connector
config.IgnoreLocal = set.IgnoreLocal == 1
if IsNotNull(set.Rules) {
ruleRefs := []*firewallconfigs.HTTPFirewallRuleRef{}
@@ -139,6 +140,7 @@ func (this *HTTPFirewallRuleSetDAO) CreateOrUpdateSetFromConfig(tx *dbs.Tx, setC
op.Name = setConfig.Name
op.Description = setConfig.Description
op.Connector = setConfig.Connector
op.IgnoreLocal = setConfig.IgnoreLocal
if len(setConfig.Actions) == 0 {
op.Actions = "[]"

View File

@@ -16,6 +16,7 @@ type HTTPFirewallRuleSet struct {
Action string `field:"action"` // 执行的动作(过期)
ActionOptions string `field:"actionOptions"` // 动作的选项(过期)
Actions string `field:"actions"` // 一组动作
IgnoreLocal uint8 `field:"ignoreLocal"` // 忽略局域网请求
}
type HTTPFirewallRuleSetOperator struct {
@@ -33,6 +34,7 @@ type HTTPFirewallRuleSetOperator struct {
Action interface{} // 执行的动作(过期)
ActionOptions interface{} // 动作的选项(过期)
Actions interface{} // 一组动作
IgnoreLocal interface{} // 忽略局域网请求
}
func NewHTTPFirewallRuleSetOperator() *HTTPFirewallRuleSetOperator {

View File

@@ -2,7 +2,7 @@ package models
import (
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
@@ -80,22 +80,69 @@ func (this *HTTPHeaderDAO) FindHTTPHeaderName(tx *dbs.Tx, id int64) (string, err
}
// CreateHeader 创建Header
func (this *HTTPHeaderDAO) CreateHeader(tx *dbs.Tx, name string, value string) (int64, error) {
func (this *HTTPHeaderDAO) CreateHeader(tx *dbs.Tx, userId int64, name string, value string, status []int, disableRedirect bool, shouldAppend bool, shouldReplace bool, replaceValues []*shared.HTTPHeaderReplaceValue, methods []string, domains []string) (int64, error) {
op := NewHTTPHeaderOperator()
op.UserId = userId
op.State = HTTPHeaderStateEnabled
op.IsOn = true
op.Name = name
op.Value = value
statusConfig := &shared.HTTPStatusConfig{
Always: true,
// status
var statusConfig *shared.HTTPStatusConfig
if len(status) == 0 {
statusConfig = &shared.HTTPStatusConfig{
Always: true,
}
} else {
statusConfig = &shared.HTTPStatusConfig{
Always: false,
Codes: status,
}
}
statusJSON, err := json.Marshal(statusConfig)
if err != nil {
return 0, err
}
op.Status = statusJSON
op.DisableRedirect = disableRedirect
op.ShouldAppend = shouldAppend
op.ShouldReplace = shouldReplace
if len(replaceValues) == 0 {
op.ReplaceValues = "[]"
} else {
replaceValuesJSON, err := json.Marshal(replaceValues)
if err != nil {
return 0, err
}
op.ReplaceValues = replaceValuesJSON
}
// methods
if len(methods) == 0 {
op.Methods = "[]"
} else {
methodsJSON, err := json.Marshal(methods)
if err != nil {
return 0, err
}
op.Methods = methodsJSON
}
// domains
if len(domains) == 0 {
op.Domains = "[]"
} else {
domainsJSON, err := json.Marshal(domains)
if err != nil {
return 0, err
}
op.Domains = domainsJSON
}
err = this.Save(tx, op)
if err != nil {
return 0, err
@@ -104,7 +151,7 @@ func (this *HTTPHeaderDAO) CreateHeader(tx *dbs.Tx, name string, value string) (
}
// UpdateHeader 修改Header
func (this *HTTPHeaderDAO) UpdateHeader(tx *dbs.Tx, headerId int64, name string, value string) error {
func (this *HTTPHeaderDAO) UpdateHeader(tx *dbs.Tx, headerId int64, name string, value string, status []int, disableRedirect bool, shouldAppend bool, shouldReplace bool, replaceValues []*shared.HTTPHeaderReplaceValue, methods []string, domains []string) error {
if headerId <= 0 {
return errors.New("invalid headerId")
}
@@ -113,7 +160,63 @@ func (this *HTTPHeaderDAO) UpdateHeader(tx *dbs.Tx, headerId int64, name string,
op.Id = headerId
op.Name = name
op.Value = value
err := this.Save(tx, op)
// status
var statusConfig *shared.HTTPStatusConfig
if len(status) == 0 {
statusConfig = &shared.HTTPStatusConfig{
Always: true,
}
} else {
statusConfig = &shared.HTTPStatusConfig{
Always: false,
Codes: status,
}
}
statusJSON, err := json.Marshal(statusConfig)
if err != nil {
return err
}
op.Status = statusJSON
op.DisableRedirect = disableRedirect
op.ShouldAppend = shouldAppend
op.ShouldReplace = shouldReplace
if len(replaceValues) == 0 {
op.ReplaceValues = "[]"
} else {
replaceValuesJSON, err := json.Marshal(replaceValues)
if err != nil {
return err
}
op.ReplaceValues = replaceValuesJSON
}
// methods
if len(methods) == 0 {
op.Methods = "[]"
} else {
methodsJSON, err := json.Marshal(methods)
if err != nil {
return err
}
op.Methods = methodsJSON
}
// domains
if len(domains) == 0 {
op.Domains = "[]"
} else {
domainsJSON, err := json.Marshal(domains)
if err != nil {
return err
}
op.Domains = domainsJSON
}
err = this.Save(tx, op)
if err != nil {
return err
}
@@ -136,7 +239,21 @@ func (this *HTTPHeaderDAO) ComposeHeaderConfig(tx *dbs.Tx, headerId int64) (*sha
config.IsOn = header.IsOn == 1
config.Name = header.Name
config.Value = header.Value
config.DisableRedirect = header.DisableRedirect == 1
config.ShouldAppend = header.ShouldAppend == 1
// replace
config.ShouldReplace = header.ShouldReplace == 1
if len(header.ReplaceValues) > 0 {
var values = []*shared.HTTPHeaderReplaceValue{}
err = json.Unmarshal([]byte(header.ReplaceValues), &values)
if err != nil {
return nil, err
}
config.ReplaceValues = values
}
// status
if len(header.Status) > 0 {
status := &shared.HTTPStatusConfig{}
err = json.Unmarshal([]byte(header.Status), status)
@@ -146,6 +263,26 @@ func (this *HTTPHeaderDAO) ComposeHeaderConfig(tx *dbs.Tx, headerId int64) (*sha
config.Status = status
}
// methods
if len(header.Methods) > 0 {
var methods = []string{}
err = json.Unmarshal([]byte(header.Methods), &methods)
if err != nil {
return nil, err
}
config.Methods = methods
}
// domains
if len(header.Domains) > 0 {
var domains = []string{}
err = json.Unmarshal([]byte(header.Domains), &domains)
if err != nil {
return nil, err
}
config.Domains = domains
}
return config, nil
}

View File

@@ -1,32 +1,44 @@
package models
// HTTP Header
// HTTPHeader HTTP Header
type HTTPHeader struct {
Id uint32 `field:"id"` // ID
AdminId uint32 `field:"adminId"` // 管理员ID
UserId uint32 `field:"userId"` // 用户ID
TemplateId uint32 `field:"templateId"` // 模版ID
IsOn uint8 `field:"isOn"` // 是否启用
Name string `field:"name"` // 名称
Value string `field:"value"` // 值
Order uint32 `field:"order"` // 排序
Status string `field:"status"` // 状态码设置
State uint8 `field:"state"` // 状态
CreatedAt uint64 `field:"createdAt"` // 创建时间
Id uint32 `field:"id"` // ID
AdminId uint32 `field:"adminId"` // 管理员ID
UserId uint32 `field:"userId"` // 用户ID
TemplateId uint32 `field:"templateId"` // 模版ID
IsOn uint8 `field:"isOn"` // 是否启用
Name string `field:"name"` // 名称
Value string `field:"value"` // 值
Order uint32 `field:"order"` // 排序
Status string `field:"status"` // 状态码设置
DisableRedirect uint8 `field:"disableRedirect"` // 是否不支持跳转
ShouldAppend uint8 `field:"shouldAppend"` // 是否为附加
ShouldReplace uint8 `field:"shouldReplace"` // 是否替换变量
ReplaceValues string `field:"replaceValues"` // 替换的值
Methods string `field:"methods"` // 支持的方法
Domains string `field:"domains"` // 支持的域名
State uint8 `field:"state"` // 状态
CreatedAt uint64 `field:"createdAt"` // 创建时间
}
type HTTPHeaderOperator struct {
Id interface{} // ID
AdminId interface{} // 管理员ID
UserId interface{} // 用户ID
TemplateId interface{} // 模版ID
IsOn interface{} // 是否启用
Name interface{} // 名称
Value interface{} // 值
Order interface{} // 排序
Status interface{} // 状态码设置
State interface{} // 状态
CreatedAt interface{} // 创建时间
Id interface{} // ID
AdminId interface{} // 管理员ID
UserId interface{} // 用户ID
TemplateId interface{} // 模版ID
IsOn interface{} // 是否启用
Name interface{} // 名称
Value interface{} // 值
Order interface{} // 排序
Status interface{} // 状态码设置
DisableRedirect interface{} // 是否不支持跳转
ShouldAppend interface{} // 是否为附加
ShouldReplace interface{} // 是否替换变量
ReplaceValues interface{} // 替换的值
Methods interface{} // 支持的方法
Domains interface{} // 支持的域名
State interface{} // 状态
CreatedAt interface{} // 创建时间
}
func NewHTTPHeaderOperator() *HTTPHeaderOperator {

View File

@@ -186,48 +186,6 @@ func (this *HTTPHeaderPolicyDAO) ComposeHeaderPolicyConfig(tx *dbs.Tx, headerPol
config.Id = int64(policy.Id)
config.IsOn = policy.IsOn == 1
// AddHeaders
if len(policy.AddHeaders) > 0 {
refs := []*shared.HTTPHeaderRef{}
err = json.Unmarshal([]byte(policy.AddHeaders), &refs)
if err != nil {
return nil, err
}
if len(refs) > 0 {
for _, ref := range refs {
headerConfig, err := SharedHTTPHeaderDAO.ComposeHeaderConfig(tx, ref.HeaderId)
if err != nil {
return nil, err
}
config.AddHeaders = append(config.AddHeaders, headerConfig)
}
}
}
// AddTrailers
if len(policy.AddTrailers) > 0 {
refs := []*shared.HTTPHeaderRef{}
err = json.Unmarshal([]byte(policy.AddTrailers), &refs)
if err != nil {
return nil, err
}
if len(refs) > 0 {
resultRefs := []*shared.HTTPHeaderRef{}
for _, ref := range refs {
headerConfig, err := SharedHTTPHeaderDAO.ComposeHeaderConfig(tx, ref.HeaderId)
if err != nil {
return nil, err
}
if headerConfig == nil {
continue
}
resultRefs = append(resultRefs, ref)
config.AddTrailers = append(config.AddTrailers, headerConfig)
}
config.AddHeaderRefs = resultRefs
}
}
// SetHeaders
if len(policy.SetHeaders) > 0 {
refs := []*shared.HTTPHeaderRef{}
@@ -252,30 +210,6 @@ func (this *HTTPHeaderPolicyDAO) ComposeHeaderPolicyConfig(tx *dbs.Tx, headerPol
}
}
// ReplaceHeaders
if len(policy.ReplaceHeaders) > 0 {
refs := []*shared.HTTPHeaderRef{}
err = json.Unmarshal([]byte(policy.ReplaceHeaders), &refs)
if err != nil {
return nil, err
}
if len(refs) > 0 {
resultRefs := []*shared.HTTPHeaderRef{}
for _, ref := range refs {
headerConfig, err := SharedHTTPHeaderDAO.ComposeHeaderConfig(tx, ref.HeaderId)
if err != nil {
return nil, err
}
if headerConfig == nil {
continue
}
resultRefs = append(resultRefs, ref)
config.ReplaceHeaders = append(config.ReplaceHeaders, headerConfig)
}
config.ReplaceHeaderRefs = resultRefs
}
}
// Delete Headers
if len(policy.DeleteHeaders) > 0 {
headers := []string{}

View File

@@ -86,7 +86,7 @@ func (this *HTTPLocationDAO) FindHTTPLocationName(tx *dbs.Tx, id int64) (string,
}
// CreateLocation 创建路由规则
func (this *HTTPLocationDAO) CreateLocation(tx *dbs.Tx, parentId int64, name string, pattern string, description string, isBreak bool, condsJSON []byte) (int64, error) {
func (this *HTTPLocationDAO) CreateLocation(tx *dbs.Tx, parentId int64, name string, pattern string, description string, isBreak bool, condsJSON []byte, domains []string) (int64, error) {
op := NewHTTPLocationOperator()
op.IsOn = true
op.State = HTTPLocationStateEnabled
@@ -100,7 +100,16 @@ func (this *HTTPLocationDAO) CreateLocation(tx *dbs.Tx, parentId int64, name str
op.Conds = condsJSON
}
err := this.Save(tx, op)
if domains == nil {
domains = []string{}
}
domainsJSON, err := json.Marshal(domains)
if err != nil {
return 0, err
}
op.Domains = domainsJSON
err = this.Save(tx, op)
if err != nil {
return 0, err
}
@@ -108,7 +117,7 @@ func (this *HTTPLocationDAO) CreateLocation(tx *dbs.Tx, parentId int64, name str
}
// UpdateLocation 修改路由规则
func (this *HTTPLocationDAO) UpdateLocation(tx *dbs.Tx, locationId int64, name string, pattern string, description string, isOn bool, isBreak bool, condsJSON []byte) error {
func (this *HTTPLocationDAO) UpdateLocation(tx *dbs.Tx, locationId int64, name string, pattern string, description string, isOn bool, isBreak bool, condsJSON []byte, domains []string) error {
if locationId <= 0 {
return errors.New("invalid locationId")
}
@@ -124,7 +133,16 @@ func (this *HTTPLocationDAO) UpdateLocation(tx *dbs.Tx, locationId int64, name s
op.Conds = condsJSON
}
err := this.Save(tx, op)
if domains == nil {
domains = []string{}
}
domainsJSON, err := json.Marshal(domains)
if err != nil {
return err
}
op.Domains = domainsJSON
err = this.Save(tx, op)
if err != nil {
return err
}
@@ -195,6 +213,18 @@ func (this *HTTPLocationDAO) ComposeLocationConfig(tx *dbs.Tx, locationId int64,
config.Conds = conds
}
// domains
if len(location.Domains) > 0 {
var domains = []string{}
err = json.Unmarshal([]byte(location.Domains), &domains)
if err != nil {
return nil, err
}
if len(domains) > 0 {
config.Domains = domains
}
}
if cacheMap != nil {
cacheMap.Put(cacheKey, config)
}

View File

@@ -18,6 +18,7 @@ type HTTPLocation struct {
UrlPrefix string `field:"urlPrefix"` // URL前缀
IsBreak uint8 `field:"isBreak"` // 是否终止匹配
Conds string `field:"conds"` // 匹配条件
Domains string `field:"domains"` // 专属域名
}
type HTTPLocationOperator struct {
@@ -37,6 +38,7 @@ type HTTPLocationOperator struct {
UrlPrefix interface{} // URL前缀
IsBreak interface{} // 是否终止匹配
Conds interface{} // 匹配条件
Domains interface{} // 专属域名
}
func NewHTTPLocationOperator() *HTTPLocationOperator {

View File

@@ -419,6 +419,33 @@ func (this *HTTPWebDAO) ComposeWebConfig(tx *dbs.Tx, webId int64, cacheMap *util
config.RemoteAddr = remoteAddrConfig
}
// mergeSlashes
config.MergeSlashes = web.MergeSlashes == 1
// 请求限制
if len(web.RequestLimit) > 0 {
var requestLimitConfig = &serverconfigs.HTTPRequestLimitConfig{}
if len(web.RequestLimit) > 0 {
err = json.Unmarshal([]byte(web.RequestLimit), requestLimitConfig)
if err != nil {
return nil, err
}
config.RequestLimit = requestLimitConfig
}
}
// 请求脚本
if len(web.RequestScripts) > 0 {
var requestScriptsConfig = &serverconfigs.HTTPRequestScriptsConfig{}
if len(web.RequestScripts) > 0 {
err = json.Unmarshal([]byte(web.RequestScripts), requestScriptsConfig)
if err != nil {
return nil, err
}
config.RequestScripts = requestScriptsConfig
}
}
if cacheMap != nil {
cacheMap.Put(cacheKey, config)
}
@@ -1037,6 +1064,8 @@ func (this *HTTPWebDAO) UpdateWebHostRedirects(tx *dbs.Tx, webId int64, hostRedi
return this.NotifyUpdate(tx, webId)
}
// 通用设置
// FindWebHostRedirects 查找主机跳转
func (this *HTTPWebDAO) FindWebHostRedirects(tx *dbs.Tx, webId int64) ([]byte, error) {
col, err := this.Query(tx).
@@ -1049,6 +1078,96 @@ func (this *HTTPWebDAO) FindWebHostRedirects(tx *dbs.Tx, webId int64) ([]byte, e
return []byte(col), nil
}
// UpdateWebCommon 修改通用设置
func (this *HTTPWebDAO) UpdateWebCommon(tx *dbs.Tx, webId int64, mergeSlashes bool) error {
if webId <= 0 {
return errors.New("invalid webId")
}
var op = NewHTTPWebOperator()
op.Id = webId
op.MergeSlashes = mergeSlashes
err := this.Save(tx, op)
if err != nil {
return err
}
return this.NotifyUpdate(tx, webId)
}
// UpdateWebRequestLimit 修改服务的请求限制
func (this *HTTPWebDAO) UpdateWebRequestLimit(tx *dbs.Tx, webId int64, config *serverconfigs.HTTPRequestLimitConfig) error {
configJSON, err := json.Marshal(config)
if err != nil {
return err
}
err = this.Query(tx).
Pk(webId).
Set("requestLimit", configJSON).
UpdateQuickly()
if err != nil {
return err
}
return this.NotifyUpdate(tx, webId)
}
// FindWebRequestLimit 获取服务的请求限制
func (this *HTTPWebDAO) FindWebRequestLimit(tx *dbs.Tx, webId int64) (*serverconfigs.HTTPRequestLimitConfig, error) {
configString, err := this.Query(tx).
Pk(webId).
Result("requestLimit").
FindStringCol("")
if err != nil {
return nil, err
}
var config = &serverconfigs.HTTPRequestLimitConfig{}
if len(configString) == 0 {
return config, nil
}
err = json.Unmarshal([]byte(configString), config)
if err != nil {
return nil, err
}
return config, nil
}
// UpdateWebRequestScripts 修改服务的请求脚本设置
func (this *HTTPWebDAO) UpdateWebRequestScripts(tx *dbs.Tx, webId int64, config *serverconfigs.HTTPRequestScriptsConfig) error {
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)
}
// FindWebRequestScripts 查找服务的脚本设置
func (this *HTTPWebDAO) FindWebRequestScripts(tx *dbs.Tx, webId int64) (*serverconfigs.HTTPRequestScriptsConfig, error) {
configString, err := this.Query(tx).
Pk(webId).
Result("requestScripts").
FindStringCol("")
if err != nil {
return nil, err
}
var config = &serverconfigs.HTTPRequestScriptsConfig{}
if len(configString) == 0 {
return config, nil
}
err = json.Unmarshal([]byte(configString), config)
if err != nil {
return nil, err
}
return config, nil
}
// NotifyUpdate 通知更新
func (this *HTTPWebDAO) NotifyUpdate(tx *dbs.Tx, webId int64) error {
// server

View File

@@ -32,6 +32,9 @@ type HTTPWeb struct {
Auth string `field:"auth"` // 认证策略配置
Webp string `field:"webp"` // WebP配置
RemoteAddr string `field:"remoteAddr"` // 客户端IP配置
MergeSlashes uint8 `field:"mergeSlashes"` // 是否合并路径中的斜杠
RequestLimit string `field:"requestLimit"` // 请求限制
RequestScripts string `field:"requestScripts"` // 请求脚本
}
type HTTPWebOperator struct {
@@ -65,6 +68,9 @@ type HTTPWebOperator struct {
Auth interface{} // 认证策略配置
Webp interface{} // WebP配置
RemoteAddr interface{} // 客户端IP配置
MergeSlashes interface{} // 是否合并路径中的斜杠
RequestLimit interface{} // 请求限制
RequestScripts interface{} // 请求脚本
}
func NewHTTPWebOperator() *HTTPWebOperator {

View File

@@ -123,14 +123,16 @@ func (this *IPItemDAO) FindEnabledIPItem(tx *dbs.Tx, id int64) (*IPItem, error)
return result.(*IPItem), err
}
// DisableOldIPItem 根据IP删除以前的旧记录
func (this *IPItemDAO) DisableOldIPItem(tx *dbs.Tx, listId int64, ipFrom string, ipTo string) error {
return this.Query(tx).
// DeleteOldItem 根据IP删除以前的旧记录
func (this *IPItemDAO) DeleteOldItem(tx *dbs.Tx, listId int64, ipFrom string, ipTo string) error {
_, err := this.Query(tx).
UseIndex("ipFrom").
Attr("listId", listId).
Attr("ipFrom", ipFrom).
Attr("ipTo", ipTo).
Set("state", IPItemStateDisabled).
UpdateQuickly()
Delete()
// 这里不通知更新
return err
}
// CreateIPItem 创建IP
@@ -154,7 +156,7 @@ func (this *IPItemDAO) CreateIPItem(tx *dbs.Tx,
return 0, err
}
op := NewIPItemOperator()
var op = NewIPItemOperator()
op.ListId = listId
op.IpFrom = ipFrom
op.IpTo = ipTo
@@ -177,6 +179,11 @@ func (this *IPItemDAO) CreateIPItem(tx *dbs.Tx,
op.SourceHTTPFirewallRuleGroupId = sourceHTTPFirewallRuleGroupId
op.SourceHTTPFirewallRuleSetId = sourceHTTPFirewallRuleSetId
var autoAdded = listId == firewallconfigs.GlobalListId || sourceNodeId > 0 || sourceServerId > 0 || sourceHTTPFirewallPolicyId > 0
if autoAdded {
op.IsRead = 0
}
op.State = IPItemStateEnabled
err = this.Save(tx, op)
if err != nil {
@@ -184,6 +191,11 @@ func (this *IPItemDAO) CreateIPItem(tx *dbs.Tx,
}
itemId := types.Int64(op.Id)
// 自动加入名单不需要即时更新,防止数量过多而导致性能问题
if autoAdded {
return itemId, nil
}
err = this.NotifyUpdate(tx, itemId)
if err != nil {
return 0, err
@@ -279,6 +291,35 @@ func (this *IPItemDAO) ListIPItemsWithListId(tx *dbs.Tx, listId int64, keyword s
// ListIPItemsAfterVersion 根据版本号查找IP列表
func (this *IPItemDAO) ListIPItemsAfterVersion(tx *dbs.Tx, version int64, size int64) (result []*IPItem, err error) {
// 将过期的设置为已删除,这样是为了在 expiredAt<UNIX_TIMESTAMP()边缘节点让过期的IP有一个执行删除的机会
ones, _, err := this.Query(tx).
ResultPk().
Where("(expiredAt>0 AND expiredAt<=:timestamp)").
Param("timestamp", time.Now().Unix()).
State(IPItemStateEnabled).
Limit(100).
FindOnes()
if err != nil {
return nil, err
}
for _, one := range ones {
var expiredId = one.GetInt64("id")
newVersion, err := SharedIPListDAO.IncreaseVersion(tx)
if err != nil {
return nil, err
}
_, err = this.Query(tx).
Pk(expiredId).
Set("state", IPItemStateDisabled).
Set("expiredAt", 0).
Set("version", newVersion).
Update()
if err != nil {
return nil, err
}
}
_, err = this.Query(tx).
// 这里不要设置状态参数,因为我们要知道哪些是删除的
Gt("version", version).
@@ -322,6 +363,7 @@ func (this *IPItemDAO) FindEnabledItemContainsIP(tx *dbs.Tx, listId int64, ip ui
// FindEnabledItemsWithIP 根据IP查找Item
func (this *IPItemDAO) FindEnabledItemsWithIP(tx *dbs.Tx, ip string) (result []*IPItem, err error) {
_, err = this.Query(tx).
State(IPItemStateEnabled).
Attr("ipFrom", ip).
Attr("ipTo", "").
Where("(expiredAt=0 OR expiredAt>:nowTime)").
@@ -342,7 +384,7 @@ func (this *IPItemDAO) ExistsEnabledItem(tx *dbs.Tx, itemId int64) (bool, error)
}
// CountAllEnabledIPItems 计算数量
func (this *IPItemDAO) CountAllEnabledIPItems(tx *dbs.Tx, ip string, listId int64) (int64, error) {
func (this *IPItemDAO) CountAllEnabledIPItems(tx *dbs.Tx, ip string, listId int64, unread bool) (int64, error) {
var query = this.Query(tx)
if len(ip) > 0 {
query.Attr("ipFrom", ip)
@@ -352,6 +394,9 @@ func (this *IPItemDAO) CountAllEnabledIPItems(tx *dbs.Tx, ip string, listId int6
} 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)
}
return query.
State(IPItemStateEnabled).
Where("(expiredAt=0 OR expiredAt>:expiredAt)").
@@ -360,7 +405,7 @@ func (this *IPItemDAO) CountAllEnabledIPItems(tx *dbs.Tx, ip string, listId int6
}
// ListAllEnabledIPItems 搜索所有IP
func (this *IPItemDAO) ListAllEnabledIPItems(tx *dbs.Tx, ip string, listId int64, offset int64, size int64) (result []*IPItem, err error) {
func (this *IPItemDAO) ListAllEnabledIPItems(tx *dbs.Tx, ip string, listId int64, unread bool, offset int64, size int64) (result []*IPItem, err error) {
var query = this.Query(tx)
if len(ip) > 0 {
query.Attr("ipFrom", ip)
@@ -370,6 +415,9 @@ func (this *IPItemDAO) ListAllEnabledIPItems(tx *dbs.Tx, ip string, listId int64
} 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)
}
_, err = query.
State(IPItemStateEnabled).
Where("(expiredAt=0 OR expiredAt>:expiredAt)").
@@ -382,6 +430,14 @@ func (this *IPItemDAO) ListAllEnabledIPItems(tx *dbs.Tx, ip string, listId int64
return
}
// UpdateItemsRead 设置所有未已读
func (this *IPItemDAO) UpdateItemsRead(tx *dbs.Tx) error {
return this.Query(tx).
Attr("isRead", 0).
Set("isRead", 1).
UpdateQuickly()
}
// NotifyUpdate 通知更新
func (this *IPItemDAO) NotifyUpdate(tx *dbs.Tx, itemId int64) error {
// 获取ListId
@@ -394,6 +450,37 @@ func (this *IPItemDAO) NotifyUpdate(tx *dbs.Tx, itemId int64) error {
return nil
}
if listId == firewallconfigs.GlobalListId {
sourceNodeId, err := this.Query(tx).
Pk(itemId).
Result("sourceNodeId").
FindInt64Col(0)
if err != nil {
return err
}
if sourceNodeId > 0 {
clusterIds, err := SharedNodeDAO.FindEnabledNodeClusterIds(tx, sourceNodeId)
if err != nil {
return err
}
for _, clusterId := range clusterIds {
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, NodeTaskTypeIPItemChanged)
if err != nil {
return err
}
}
} else {
clusterIds, err := SharedNodeClusterDAO.FindAllEnabledNodeClusterIds(tx)
for _, clusterId := range clusterIds {
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, NodeTaskTypeIPItemChanged)
if err != nil {
return err
}
}
}
return nil
}
httpFirewallPolicyIds, err := SharedHTTPFirewallPolicyDAO.FindEnabledFirewallPolicyIdsWithIPListId(tx, listId)
if err != nil {
return err

View File

@@ -23,6 +23,7 @@ type IPItem struct {
SourceHTTPFirewallPolicyId uint32 `field:"sourceHTTPFirewallPolicyId"` // 来源策略ID
SourceHTTPFirewallRuleGroupId uint32 `field:"sourceHTTPFirewallRuleGroupId"` // 来源规则集分组ID
SourceHTTPFirewallRuleSetId uint32 `field:"sourceHTTPFirewallRuleSetId"` // 来源规则集ID
IsRead uint8 `field:"isRead"` // 是否已读
}
type IPItemOperator struct {
@@ -47,6 +48,7 @@ type IPItemOperator struct {
SourceHTTPFirewallPolicyId interface{} // 来源策略ID
SourceHTTPFirewallRuleGroupId interface{} // 来源规则集分组ID
SourceHTTPFirewallRuleSetId interface{} // 来源规则集ID
IsRead interface{} // 是否已读
}
func NewIPItemOperator() *IPItemOperator {

View File

@@ -26,6 +26,8 @@ const (
type MessageType = string
const (
// 这里的命名问题(首字母大写)为历史遗留问题,暂不修改
MessageTypeHealthCheckFailed MessageType = "HealthCheckFailed" // 节点健康检查失败
MessageTypeHealthCheckNodeUp MessageType = "HealthCheckNodeUp" // 因健康检查节点上线
MessageTypeHealthCheckNodeDown MessageType = "HealthCheckNodeDown" // 因健康检查节点下线
@@ -36,8 +38,9 @@ const (
MessageTypeSSLCertACMETaskFailed MessageType = "SSLCertACMETaskFailed" // SSL证书任务执行失败
MessageTypeSSLCertACMETaskSuccess MessageType = "SSLCertACMETaskSuccess" // SSL证书任务执行成功
MessageTypeLogCapacityOverflow MessageType = "LogCapacityOverflow" // 日志超出最大限制
MessageTypeServerNamesAuditingSuccess MessageType = "ServerNamesAuditingSuccess" // 服务域名审核成功
MessageTypeServerNamesAuditingFailed MessageType = "ServerNamesAuditingFailed" // 服务域名审核失败
MessageTypeServerNamesAuditingSuccess MessageType = "ServerNamesAuditingSuccess" // 服务域名审核成功(用户)
MessageTypeServerNamesAuditingFailed MessageType = "ServerNamesAuditingFailed" // 服务域名审核失败(用户)
MessageTypeServerNamesRequireAuditing MessageType = "serverNamesRequireAuditing" // 服务域名需要审核(管理员)
MessageTypeThresholdSatisfied MessageType = "ThresholdSatisfied" // 满足阈值
MessageTypeFirewallEvent MessageType = "FirewallEvent" // 防火墙事件
MessageTypeIPAddrUp MessageType = "IPAddrUp" // IP地址上线

View File

@@ -3,6 +3,7 @@ package models
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/remotelogs"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
_ "github.com/go-sql-driver/mysql"
@@ -46,14 +47,14 @@ func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
goman.New(func() {
for range ticker.C {
err := SharedMessageTaskDAO.CleanExpiredMessageTasks(nil, 30) // 只保留30天
if err != nil {
remotelogs.Error("SharedMessageTaskDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
})
}

View File

@@ -1,6 +1,7 @@
package models
import (
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
@@ -16,14 +17,14 @@ func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
goman.New(func() {
for range ticker.C {
err := SharedMessageTaskLogDAO.CleanExpiredLogs(nil, 30) // 只保留30天
if err != nil {
remotelogs.Error("SharedMessageTaskLogDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
})
}

View File

@@ -78,6 +78,12 @@ func (this *MetricItemDAO) DisableMetricItem(tx *dbs.Tx, itemId int64) error {
if err != nil {
return err
}
err = SharedMetricSumStatDAO.DeleteItemStats(tx, itemId)
if err != nil {
return err
}
return nil
}

View File

@@ -2,6 +2,8 @@ package models
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
@@ -20,14 +22,14 @@ func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
goman.New(func() {
for range ticker.C {
err := SharedMetricStatDAO.Clean(nil, 120) // 只保留120天
err := SharedMetricStatDAO.Clean(nil)
if err != nil {
logs.Println("SharedMetricStatDAO: clean expired data failed: " + err.Error())
}
}
}()
})
})
}
@@ -461,12 +463,39 @@ func (this *MetricStatDAO) FindLatestItemStatsWithServerId(tx *dbs.Tx, serverId
}
// Clean 清理数据
func (this *MetricStatDAO) Clean(tx *dbs.Tx, days int64) error {
_, err := this.Query(tx).
Lt("createdDay", timeutil.FormatTime("Ymd", time.Now().Unix()-days*86400)).
Delete()
if err != nil {
return err
func (this *MetricStatDAO) Clean(tx *dbs.Tx) error {
for _, category := range serverconfigs.FindAllMetricItemCategoryCodes() {
var offset int64 = 0
var size int64 = 100
for {
items, err := SharedMetricItemDAO.ListEnabledItems(tx, category, offset, size)
if err != nil {
return err
}
for _, item := range items {
var config = &serverconfigs.MetricItemConfig{
Id: int64(item.Id),
Period: int(item.Period),
PeriodUnit: item.PeriodUnit,
}
var expiresDay = config.ServerExpiresDay()
_, err := this.Query(tx).
Attr("itemId", item.Id).
Lte("createdDay", expiresDay).
UseIndex("createdDay").
Limit(100_000). // 一次性不要删除太多,防止阻塞其他操作
Delete()
if err != nil {
return err
}
}
if len(items) == 0 {
break
}
offset += size
}
}
return nil
}

View File

@@ -3,6 +3,7 @@ package models
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
@@ -10,14 +11,24 @@ import (
)
func TestNewMetricStatDAO_InsertMany(t *testing.T) {
for i := 0; i <= 10_000_000; i++ {
for i := 0; i <= 1; i++ {
err := NewMetricStatDAO().CreateStat(nil, types.String(i)+"_v1", 18, int64(rands.Int(0, 10000)), int64(rands.Int(0, 10000)), int64(rands.Int(0, 100)), []string{"/html" + types.String(i)}, 1, timeutil.Format("Ymd"), 0)
if err != nil {
t.Fatal(err)
}
if i % 10000 == 0 {
if i%10000 == 0 {
t.Log(i)
}
}
t.Log("done")
}
func TestMetricStatDAO_Clean(t *testing.T) {
dbs.NotifyReady()
err := NewMetricStatDAO().Clean(nil)
if err != nil {
t.Fatal(err)
}
t.Log("ok")
}

View File

@@ -1,14 +1,35 @@
package models
import (
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type MetricSumStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedMetricSumStatDAO.Clean(nil)
if err != nil {
logs.Println("SharedMetricSumStatDAO: clean expired data failed: " + err.Error())
}
}
})
})
}
func NewMetricSumStatDAO() *MetricSumStatDAO {
return dbs.NewDAO(&MetricSumStatDAO{
DAOObject: dbs.DAOObject{
@@ -32,14 +53,15 @@ func init() {
func (this *MetricSumStatDAO) UpdateSum(tx *dbs.Tx, clusterId int64, nodeId int64, serverId int64, time string, itemId int64, version int32, count int64, total float32) error {
return this.Query(tx).
InsertOrUpdateQuickly(maps.Map{
"clusterId": clusterId,
"nodeId": nodeId,
"serverId": serverId,
"itemId": itemId,
"version": version,
"time": time,
"count": count,
"total": total,
"clusterId": clusterId,
"nodeId": nodeId,
"serverId": serverId,
"itemId": itemId,
"version": version,
"time": time,
"count": count,
"total": total,
"createdDay": timeutil.Format("Ymd"),
}, maps.Map{
"count": count,
"total": total,
@@ -84,6 +106,7 @@ func (this *MetricSumStatDAO) FindSumAtTime(tx *dbs.Tx, time string, itemId int6
// FindServerSum 查找某个服务的统计数据
func (this *MetricSumStatDAO) FindServerSum(tx *dbs.Tx, serverId int64, time string, itemId int64, version int32) (count int64, total float32, err error) {
one, err := this.Query(tx).
UseIndex("server_item_time").
Attr("serverId", serverId).
Attr("time", time).
Attr("itemId", itemId).
@@ -102,6 +125,7 @@ func (this *MetricSumStatDAO) FindServerSum(tx *dbs.Tx, serverId int64, time str
// FindClusterSum 查找集群上的统计数据
func (this *MetricSumStatDAO) FindClusterSum(tx *dbs.Tx, clusterId int64, time string, itemId int64, version int32) (count int64, total float32, err error) {
one, err := this.Query(tx).
UseIndex("cluster_item_time").
Attr("clusterId", clusterId).
Attr("time", time).
Attr("itemId", itemId).
@@ -120,6 +144,7 @@ func (this *MetricSumStatDAO) FindClusterSum(tx *dbs.Tx, clusterId int64, time s
// FindNodeSum 查找节点上的统计数据
func (this *MetricSumStatDAO) FindNodeSum(tx *dbs.Tx, nodeId int64, time string, itemId int64, version int32) (count int64, total float32, err error) {
one, err := this.Query(tx).
UseIndex("node_item_time").
Attr("nodeId", nodeId).
Attr("time", time).
Attr("itemId", itemId).
@@ -134,3 +159,50 @@ func (this *MetricSumStatDAO) FindNodeSum(tx *dbs.Tx, nodeId int64, time string,
}
return int64(one.(*MetricSumStat).Count), float32(one.(*MetricSumStat).Total), nil
}
// DeleteItemStats 删除某个指标相关的统计数据
func (this *MetricSumStatDAO) DeleteItemStats(tx *dbs.Tx, itemId int64) error {
_, err := this.Query(tx).
Attr("itemId", itemId).
Delete()
return err
}
// Clean 清理数据
func (this *MetricSumStatDAO) Clean(tx *dbs.Tx) error {
for _, category := range serverconfigs.FindAllMetricItemCategoryCodes() {
var offset int64 = 0
var size int64 = 100
for {
items, err := SharedMetricItemDAO.ListEnabledItems(tx, category, offset, size)
if err != nil {
return err
}
for _, item := range items {
var config = &serverconfigs.MetricItemConfig{
Id: int64(item.Id),
Period: int(item.Period),
PeriodUnit: item.PeriodUnit,
}
var expiresDay = config.ServerExpiresDay()
_, err := this.Query(tx).
Attr("itemId", item.Id).
Where("(createdDay IS NULL OR createdDay<:day)").
Param("day", expiresDay).
UseIndex("createdDay").
Limit(100_000). // 一次性不要删除太多,防止阻塞其他操作
Delete()
if err != nil {
return err
}
}
if len(items) == 0 {
break
}
offset += size
}
}
return nil
}

View File

@@ -3,4 +3,15 @@ package models
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/dbs"
"testing"
)
func TestMetricSumStatDAO_Clean(t *testing.T) {
dbs.NotifyReady()
err := NewMetricSumStatDAO().Clean(nil)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -2,27 +2,29 @@ package models
// MetricSumStat 指标统计总和数据
type MetricSumStat struct {
Id uint64 `field:"id"` // ID
ClusterId uint32 `field:"clusterId"` // 集群ID
NodeId uint32 `field:"nodeId"` // 节点ID
ServerId uint32 `field:"serverId"` // 服务ID
ItemId uint64 `field:"itemId"` // 指标
Count uint64 `field:"count"` // 数量
Total float64 `field:"total"` // 总和
Time string `field:"time"` // 分钟值YYYYMMDDHHII
Version uint32 `field:"version"` // 版本号
Id uint64 `field:"id"` // ID
ClusterId uint32 `field:"clusterId"` // 集群ID
NodeId uint32 `field:"nodeId"` // 节点ID
ServerId uint32 `field:"serverId"` // 服务ID
ItemId uint64 `field:"itemId"` // 指标
Count uint64 `field:"count"` // 数量
Total float64 `field:"total"` // 总和
Time string `field:"time"` // 分钟值YYYYMMDDHHII
Version uint32 `field:"version"` // 版本号
CreatedDay string `field:"createdDay"` // 创建日期YYYYMMDD
}
type MetricSumStatOperator struct {
Id interface{} // ID
ClusterId interface{} // 集群ID
NodeId interface{} // 节点ID
ServerId interface{} // 服务ID
ItemId interface{} // 指标
Count interface{} // 数量
Total interface{} // 总和
Time interface{} // 分钟值YYYYMMDDHHII
Version interface{} // 版本号
Id interface{} // ID
ClusterId interface{} // 集群ID
NodeId interface{} // 节点ID
ServerId interface{} // 服务ID
ItemId interface{} // 指标
Count interface{} // 数量
Total interface{} // 总和
Time interface{} // 分钟值YYYYMMDDHHII
Version interface{} // 版本号
CreatedDay interface{} // 创建日期YYYYMMDD
}
func NewMetricSumStatOperator() *MetricSumStatOperator {

View File

@@ -2,6 +2,7 @@ package nameservers
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
_ "github.com/go-sql-driver/mysql"
@@ -19,14 +20,14 @@ func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
goman.New(func() {
for range ticker.C {
err := SharedNSRecordHourlyStatDAO.Clean(nil, 60) // 只保留60
err := SharedNSRecordHourlyStatDAO.Clean(nil, 30) // 只保留N
if err != nil {
remotelogs.Error("NodeClusterTrafficDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
})
}

View File

@@ -178,7 +178,7 @@ func (this *NodeClusterDAO) CreateCluster(tx *dbs.Tx, adminId int64, name string
}
// UpdateCluster 修改集群
func (this *NodeClusterDAO) UpdateCluster(tx *dbs.Tx, clusterId int64, name string, grantId int64, installDir string, timezone string) error {
func (this *NodeClusterDAO) UpdateCluster(tx *dbs.Tx, clusterId int64, name string, grantId int64, installDir string, timezone string, nodeMaxThreads int32, nodeTCPMaxConnections int32, autoOpenPorts bool) error {
if clusterId <= 0 {
return errors.New("invalid clusterId")
}
@@ -188,6 +188,18 @@ func (this *NodeClusterDAO) UpdateCluster(tx *dbs.Tx, clusterId int64, name stri
op.GrantId = grantId
op.InstallDir = installDir
op.TimeZone = timezone
if nodeMaxThreads < 0 {
nodeMaxThreads = 0
}
op.NodeMaxThreads = nodeMaxThreads
if nodeTCPMaxConnections < 0 {
nodeTCPMaxConnections = 0
}
op.NodeTCPMaxConnections = nodeTCPMaxConnections
op.AutoOpenPorts = autoOpenPorts
err := this.Save(tx, op)
if err != nil {
return err
@@ -588,6 +600,22 @@ func (this *NodeClusterDAO) FindAllEnabledNodeClusterIdsWithHTTPFirewallPolicyId
return
}
// FindAllEnabledNodeClusterIds 查找所有可用的集群
func (this *NodeClusterDAO) FindAllEnabledNodeClusterIds(tx *dbs.Tx) ([]int64, error) {
ones, err := this.Query(tx).
State(NodeClusterStateEnabled).
ResultPk().
FindAll()
if err != nil {
return nil, err
}
var result = []int64{}
for _, one := range ones {
result = append(result, int64(one.(*NodeCluster).Id))
}
return result, nil
}
// FindAllEnabledNodeClusterIdsWithCachePolicyId 查找使用缓存策略的所有集群Ids
func (this *NodeClusterDAO) FindAllEnabledNodeClusterIdsWithCachePolicyId(tx *dbs.Tx, cachePolicyId int64) (result []int64, err error) {
ones, err := this.Query(tx).
@@ -848,27 +876,27 @@ func (this *NodeClusterDAO) ExistsEnabledCluster(tx *dbs.Tx, clusterId int64) (b
Exist()
}
// FindClusterTimezone 查找时区
func (this *NodeClusterDAO) FindClusterTimezone(tx *dbs.Tx, clusterId int64, cacheMap *utils.CacheMap) (string, error) {
var cacheKey = this.Table + ":FindEnabledTimeZone:" + types.String(clusterId)
// FindClusterBasicInfo 查找集群基础信息
func (this *NodeClusterDAO) FindClusterBasicInfo(tx *dbs.Tx, clusterId int64, cacheMap *utils.CacheMap) (*NodeCluster, error) {
var cacheKey = this.Table + ":FindClusterBasicInfo:" + types.String(clusterId)
if cacheMap != nil {
cache, ok := cacheMap.Get(cacheKey)
if ok {
return cache.(string), nil
return cache.(*NodeCluster), nil
}
}
timeZone, err := this.Query(tx).
cluster, err := this.Query(tx).
Pk(clusterId).
Result("timeZone").
FindStringCol("")
if err != nil {
return "", err
Result("timeZone", "nodeMaxThreads", "nodeTCPMaxConnections", "cachePolicyId", "httpFirewallPolicyId", "autoOpenPorts").
Find()
if err != nil || cluster == nil {
return nil, err
}
if cacheMap != nil {
cacheMap.Put(cacheKey, timeZone)
cacheMap.Put(cacheKey, cluster)
}
return timeZone, nil
return cluster.(*NodeCluster), nil
}
// NotifyUpdate 通知更新

View File

@@ -2,59 +2,65 @@ package models
// NodeCluster 节点集群
type NodeCluster struct {
Id uint32 `field:"id"` // ID
AdminId uint32 `field:"adminId"` // 管理员ID
UserId uint32 `field:"userId"` // 用户ID
IsOn uint8 `field:"isOn"` // 是否启用
Name string `field:"name"` // 名称
UseAllAPINodes uint8 `field:"useAllAPINodes"` // 是否使用所有API节点
ApiNodes string `field:"apiNodes"` // 使用的API节点
InstallDir string `field:"installDir"` // 安装目录
Order uint32 `field:"order"` // 排序
CreatedAt uint64 `field:"createdAt"` // 创建时间
GrantId uint32 `field:"grantId"` // 默认认证方式
State uint8 `field:"state"` // 状态
AutoRegister uint8 `field:"autoRegister"` // 是否开启自动注册
UniqueId string `field:"uniqueId"` // 唯一ID
Secret string `field:"secret"` // 密钥
HealthCheck string `field:"healthCheck"` // 健康检查
DnsName string `field:"dnsName"` // DNS名称
DnsDomainId uint32 `field:"dnsDomainId"` // 域名ID
Dns string `field:"dns"` // DNS配置
Toa string `field:"toa"` // TOA配置
CachePolicyId uint32 `field:"cachePolicyId"` // 缓存策略ID
HttpFirewallPolicyId uint32 `field:"httpFirewallPolicyId"` // WAF策略ID
AccessLog string `field:"accessLog"` // 访问日志设置
SystemServices string `field:"systemServices"` // 系统服务设置
TimeZone string `field:"timeZone"` // 时区
Id uint32 `field:"id"` // ID
AdminId uint32 `field:"adminId"` // 管理员ID
UserId uint32 `field:"userId"` // 用户ID
IsOn uint8 `field:"isOn"` // 是否启用
Name string `field:"name"` // 名称
UseAllAPINodes uint8 `field:"useAllAPINodes"` // 是否使用所有API节点
ApiNodes string `field:"apiNodes"` // 使用的API节点
InstallDir string `field:"installDir"` // 安装目录
Order uint32 `field:"order"` // 排序
CreatedAt uint64 `field:"createdAt"` // 创建时间
GrantId uint32 `field:"grantId"` // 默认认证方式
State uint8 `field:"state"` // 状态
AutoRegister uint8 `field:"autoRegister"` // 是否开启自动注册
UniqueId string `field:"uniqueId"` // 唯一ID
Secret string `field:"secret"` // 密钥
HealthCheck string `field:"healthCheck"` // 健康检查
DnsName string `field:"dnsName"` // DNS名称
DnsDomainId uint32 `field:"dnsDomainId"` // 域名ID
Dns string `field:"dns"` // DNS配置
Toa string `field:"toa"` // TOA配置
CachePolicyId uint32 `field:"cachePolicyId"` // 缓存策略ID
HttpFirewallPolicyId uint32 `field:"httpFirewallPolicyId"` // WAF策略ID
AccessLog string `field:"accessLog"` // 访问日志设置
SystemServices string `field:"systemServices"` // 系统服务设置
TimeZone string `field:"timeZone"` // 时区
NodeMaxThreads uint32 `field:"nodeMaxThreads"` // 节点最大线程数
NodeTCPMaxConnections uint32 `field:"nodeTCPMaxConnections"` // TCP最大连接数
AutoOpenPorts uint8 `field:"autoOpenPorts"` // 是否自动尝试开放端口
}
type NodeClusterOperator struct {
Id interface{} // ID
AdminId interface{} // 管理员ID
UserId interface{} // 用户ID
IsOn interface{} // 是否启用
Name interface{} // 名称
UseAllAPINodes interface{} // 是否使用所有API节点
ApiNodes interface{} // 使用的API节点
InstallDir interface{} // 安装目录
Order interface{} // 排序
CreatedAt interface{} // 创建时间
GrantId interface{} // 默认认证方式
State interface{} // 状态
AutoRegister interface{} // 是否开启自动注册
UniqueId interface{} // 唯一ID
Secret interface{} // 密钥
HealthCheck interface{} // 健康检查
DnsName interface{} // DNS名称
DnsDomainId interface{} // 域名ID
Dns interface{} // DNS配置
Toa interface{} // TOA配置
CachePolicyId interface{} // 缓存策略ID
HttpFirewallPolicyId interface{} // WAF策略ID
AccessLog interface{} // 访问日志设置
SystemServices interface{} // 系统服务设置
TimeZone interface{} // 时区
Id interface{} // ID
AdminId interface{} // 管理员ID
UserId interface{} // 用户ID
IsOn interface{} // 是否启用
Name interface{} // 名称
UseAllAPINodes interface{} // 是否使用所有API节点
ApiNodes interface{} // 使用的API节点
InstallDir interface{} // 安装目录
Order interface{} // 排序
CreatedAt interface{} // 创建时间
GrantId interface{} // 默认认证方式
State interface{} // 状态
AutoRegister interface{} // 是否开启自动注册
UniqueId interface{} // 唯一ID
Secret interface{} // 密钥
HealthCheck interface{} // 健康检查
DnsName interface{} // DNS名称
DnsDomainId interface{} // 域名ID
Dns interface{} // DNS配置
Toa interface{} // TOA配置
CachePolicyId interface{} // 缓存策略ID
HttpFirewallPolicyId interface{} // WAF策略ID
AccessLog interface{} // 访问日志设置
SystemServices interface{} // 系统服务设置
TimeZone interface{} // 时区
NodeMaxThreads interface{} // 节点最大线程数
NodeTCPMaxConnections interface{} // TCP最大连接数
AutoOpenPorts interface{} // 是否自动尝试开放端口
}
func NewNodeClusterOperator() *NodeClusterOperator {

View File

@@ -519,9 +519,9 @@ func (this *NodeDAO) FindAllInactiveNodesWithClusterId(tx *dbs.Tx, clusterId int
_, err = this.Query(tx).
State(NodeStateEnabled).
Attr("clusterId", clusterId).
Attr("isOn", true). // 只监控启用的节点
Attr("isOn", true). // 只监控启用的节点
Attr("isInstalled", true). // 只监控已经安装的节点
Attr("isActive", true). // 当前已经在线的
Attr("isActive", true). // 当前已经在线的
Where("(status IS NULL OR (JSON_EXTRACT(status, '$.isActive')=false AND UNIX_TIMESTAMP()-JSON_EXTRACT(status, '$.updatedAt')>10) OR UNIX_TIMESTAMP()-JSON_EXTRACT(status, '$.updatedAt')>120)").
Result("id", "name").
Slice(&result).
@@ -727,6 +727,13 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, cacheMap *utils
RegionId: int64(node.RegionId),
}
// API节点IP
apiNodeIPs, err := SharedAPINodeDAO.FindAllEnabledAPIAccessIPs(tx, cacheMap)
if err != nil {
return nil, err
}
config.AllowedIPs = append(config.AllowedIPs, apiNodeIPs...)
// 获取所有的服务
servers, err := SharedServerDAO.FindAllEnabledServersWithNode(tx, int64(node.Id))
if err != nil {
@@ -775,11 +782,17 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, cacheMap *utils
var primaryClusterId = int64(node.ClusterId)
var clusterIds = []int64{primaryClusterId}
clusterIds = append(clusterIds, node.DecodeSecondaryClusterIds()...)
var clusterIndex = 0
for _, clusterId := range clusterIds {
httpFirewallPolicyId, err := SharedNodeClusterDAO.FindClusterHTTPFirewallPolicyId(tx, clusterId, cacheMap)
nodeCluster, err := SharedNodeClusterDAO.FindClusterBasicInfo(tx, clusterId, cacheMap)
if err != nil {
return nil, err
}
if nodeCluster == nil {
continue
}
var httpFirewallPolicyId = int64(nodeCluster.HttpFirewallPolicyId)
if httpFirewallPolicyId > 0 {
firewallPolicy, err := SharedHTTPFirewallPolicyDAO.ComposeFirewallPolicy(tx, httpFirewallPolicyId, cacheMap)
if err != nil {
@@ -791,10 +804,7 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, cacheMap *utils
}
// 缓存策略
httpCachePolicyId, err := SharedNodeClusterDAO.FindClusterHTTPCachePolicyId(tx, clusterId, cacheMap)
if err != nil {
return nil, err
}
var httpCachePolicyId = int64(nodeCluster.CachePolicyId)
if httpCachePolicyId > 0 {
cachePolicy, err := SharedHTTPCachePolicyDAO.ComposeCachePolicy(tx, httpCachePolicyId, cacheMap)
if err != nil {
@@ -806,13 +816,21 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, cacheMap *utils
}
// 时区
timeZone, err := SharedNodeClusterDAO.FindClusterTimezone(tx, clusterId, cacheMap)
if err != nil {
return nil, err
if len(config.TimeZone) == 0 {
var timeZone = nodeCluster.TimeZone
if len(timeZone) > 0 {
config.TimeZone = timeZone
}
}
if len(timeZone) > 0 {
config.TimeZone = timeZone
// 最大线程数、TCP连接数
if clusterIndex == 0 {
config.MaxThreads = int(nodeCluster.NodeMaxThreads)
config.TCPMaxConnections = int(nodeCluster.NodeTCPMaxConnections)
config.AutoOpenPorts = nodeCluster.AutoOpenPorts == 1
}
clusterIndex++
}
// 缓存最大容量设置
@@ -899,6 +917,18 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, cacheMap *utils
config.MetricItems = metricItems
// 产品
adminUIConfig, err := SharedSysSettingDAO.ReadAdminUIConfig(tx, cacheMap)
if err != nil {
return nil, err
}
if adminUIConfig != nil {
config.ProductConfig = &nodeconfigs.ProductConfig{
Name: adminUIConfig.ProductName,
Version: adminUIConfig.Version,
}
}
return config, nil
}

View File

@@ -73,7 +73,7 @@ func (this *NodeGrantDAO) FindNodeGrantName(tx *dbs.Tx, id uint32) (string, erro
}
// CreateGrant 创建认证信息
func (this *NodeGrantDAO) CreateGrant(tx *dbs.Tx, adminId int64, name string, method string, username string, password string, privateKey string, passphrase string, description string, nodeId int64) (grantId int64, err error) {
func (this *NodeGrantDAO) CreateGrant(tx *dbs.Tx, adminId int64, name string, method string, username string, password string, privateKey string, passphrase string, description string, nodeId int64, su bool) (grantId int64, err error) {
op := NewNodeGrantOperator()
op.AdminId = adminId
op.Name = name
@@ -83,12 +83,12 @@ func (this *NodeGrantDAO) CreateGrant(tx *dbs.Tx, adminId int64, name string, me
case "user":
op.Username = username
op.Password = password
op.Su = false // TODO 需要做到前端可以配置
case "privateKey":
op.Username = username
op.PrivateKey = privateKey
op.Passphrase = passphrase
}
op.Su = su
op.Description = description
op.NodeId = nodeId
op.State = NodeGrantStateEnabled
@@ -97,7 +97,7 @@ func (this *NodeGrantDAO) CreateGrant(tx *dbs.Tx, adminId int64, name string, me
}
// UpdateGrant 修改认证信息
func (this *NodeGrantDAO) UpdateGrant(tx *dbs.Tx, grantId int64, name string, method string, username string, password string, privateKey string, passphrase string, description string, nodeId int64) error {
func (this *NodeGrantDAO) UpdateGrant(tx *dbs.Tx, grantId int64, name string, method string, username string, password string, privateKey string, passphrase string, description string, nodeId int64, su bool) error {
if grantId <= 0 {
return errors.New("invalid grantId")
}
@@ -111,12 +111,12 @@ func (this *NodeGrantDAO) UpdateGrant(tx *dbs.Tx, grantId int64, name string, me
case "user":
op.Username = username
op.Password = password
op.Su = false // TODO 需要做到前端可以配置
case "privateKey":
op.Username = username
op.PrivateKey = privateKey
op.Passphrase = passphrase
}
op.Su = su
op.Description = description
op.NodeId = nodeId
err := this.Save(tx, op)

View File

@@ -119,7 +119,7 @@ func (this *NodeIPAddressDAO) FindAddressIsHealthy(tx *dbs.Tx, addressId int64)
}
// CreateAddress 创建IP地址
func (this *NodeIPAddressDAO) CreateAddress(tx *dbs.Tx, adminId int64, nodeId int64, role nodeconfigs.NodeRole, name string, ip string, canAccess bool, isUp bool) (addressId int64, err error) {
func (this *NodeIPAddressDAO) CreateAddress(tx *dbs.Tx, adminId int64, nodeId int64, role nodeconfigs.NodeRole, name string, ip string, canAccess bool, isUp bool, groupId int64) (addressId int64, err error) {
if len(role) == 0 {
role = nodeconfigs.NodeRoleNode
}
@@ -131,6 +131,7 @@ func (this *NodeIPAddressDAO) CreateAddress(tx *dbs.Tx, adminId int64, nodeId in
op.Ip = ip
op.CanAccess = canAccess
op.IsUp = isUp
op.GroupId = groupId
op.State = NodeIPAddressStateEnabled
addressId, err = this.SaveInt64(tx, op)
@@ -442,6 +443,7 @@ func (this *NodeIPAddressDAO) UpdateAddressBackupIP(tx *dbs.Tx, addressId int64,
return errors.New("invalid addressId")
}
var op = NewNodeIPAddressOperator()
op.IsUp = true // IP必须在线备用IP才会有用
op.Id = addressId
op.BackupThresholdId = thresholdId
op.BackupIP = ip

View File

@@ -0,0 +1,44 @@
package models
import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
)
type NodeIPAddressGroupDAO dbs.DAO
func NewNodeIPAddressGroupDAO() *NodeIPAddressGroupDAO {
return dbs.NewDAO(&NodeIPAddressGroupDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeNodeIPAddressGroups",
Model: new(NodeIPAddressGroup),
PkName: "id",
},
}).(*NodeIPAddressGroupDAO)
}
var SharedNodeIPAddressGroupDAO *NodeIPAddressGroupDAO
func init() {
dbs.OnReady(func() {
SharedNodeIPAddressGroupDAO = NewNodeIPAddressGroupDAO()
})
}
// FindNodeIPAddressGroupName 根据主键查找名称
func (this *NodeIPAddressGroupDAO) FindNodeIPAddressGroupName(tx *dbs.Tx, id uint32) (string, error) {
return this.Query(tx).
Pk(id).
Result("name").
FindStringCol("")
}
// CreateGroup 创建分组
func (this *NodeIPAddressGroupDAO) CreateGroup(tx *dbs.Tx, name string, value string) (int64, error) {
var op = NewNodeIPAddressGroupOperator()
op.Name = name
op.Value = value
return this.SaveInt64(tx, op)
}

View File

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

View File

@@ -0,0 +1,18 @@
package models
// NodeIPAddressGroup IP地址分组
type NodeIPAddressGroup struct {
Id uint32 `field:"id"` // ID
Name string `field:"name"` // 分组名
Value string `field:"value"` // IP值
}
type NodeIPAddressGroupOperator struct {
Id interface{} // ID
Name interface{} // 分组名
Value interface{} // IP值
}
func NewNodeIPAddressGroupOperator() *NodeIPAddressGroupOperator {
return &NodeIPAddressGroupOperator{}
}

View File

@@ -0,0 +1 @@
package models

View File

@@ -5,6 +5,7 @@ type NodeIPAddress struct {
Id uint32 `field:"id"` // ID
NodeId uint32 `field:"nodeId"` // 节点ID
Role string `field:"role"` // 节点角色
GroupId uint32 `field:"groupId"` // 所属分组ID
Name string `field:"name"` // 名称
Ip string `field:"ip"` // IP地址
Description string `field:"description"` // 描述
@@ -26,6 +27,7 @@ type NodeIPAddressOperator struct {
Id interface{} // ID
NodeId interface{} // 节点ID
Role interface{} // 节点角色
GroupId interface{} // 所属分组ID
Name interface{} // 名称
Ip interface{} // IP地址
Description interface{} // 描述

View File

@@ -41,8 +41,25 @@ func init() {
}
// CreateLog 创建日志
func (this *NodeLogDAO) CreateLog(tx *dbs.Tx, nodeRole nodeconfigs.NodeRole, nodeId int64, serverId int64, originId int64, level string, tag string, description string, createdAt int64) error {
hash := stringutil.Md5(nodeRole + "@" + types.String(nodeId) + "@" + types.String(serverId) + "@" + types.String(originId) + "@" + level + "@" + tag + "@" + description)
func (this *NodeLogDAO) CreateLog(tx *dbs.Tx, nodeRole nodeconfigs.NodeRole, nodeId int64, serverId int64, originId int64, level string, tag string, description string, createdAt int64, logType string, paramsJSON []byte) error {
// 修复以前同样的日志
if nodeId > 0 && level == "success" && len(logType) > 0 && len(paramsJSON) > 0 {
err := this.Query(tx).
Attr("nodeId", nodeId).
Attr("serverId", serverId).
Attr("type", logType).
Attr("level", "error").
Attr("isFixed", 0).
JSONContains("params", string(paramsJSON)).
Set("isFixed", 1).
Set("isRead", 1).
UpdateQuickly()
if err != nil {
return err
}
}
hash := stringutil.Md5(nodeRole + "@" + types.String(nodeId) + "@" + types.String(serverId) + "@" + types.String(originId) + "@" + level + "@" + tag + "@" + description + "@" + string(paramsJSON))
// 检查是否在重复最后一条,避免重复创建
lastLog, err := this.Query(tx).
@@ -76,6 +93,12 @@ func (this *NodeLogDAO) CreateLog(tx *dbs.Tx, nodeRole nodeconfigs.NodeRole, nod
op.Hash = hash
op.Count = 1
op.IsRead = level != "error"
op.Type = logType
if len(paramsJSON) > 0 {
op.Params = paramsJSON
}
err = this.Save(tx, op)
return err
}
@@ -112,6 +135,7 @@ func (this *NodeLogDAO) DeleteExpiredLogs(tx *dbs.Tx, days int) error {
// CountNodeLogs 计算节点日志数量
func (this *NodeLogDAO) CountNodeLogs(tx *dbs.Tx,
role string,
nodeClusterId int64,
nodeId int64,
serverId int64,
originId int64,
@@ -119,7 +143,8 @@ func (this *NodeLogDAO) CountNodeLogs(tx *dbs.Tx,
dayTo string,
keyword string,
level string,
isUnread bool) (int64, error) {
isUnread bool,
tag string) (int64, error) {
query := this.Query(tx)
if len(role) > 0 {
query.Attr("role", role)
@@ -127,11 +152,16 @@ func (this *NodeLogDAO) CountNodeLogs(tx *dbs.Tx,
if nodeId > 0 {
query.Attr("nodeId", nodeId)
} else {
switch role {
case nodeconfigs.NodeRoleNode:
query.Where("nodeId IN (SELECT id FROM " + SharedNodeDAO.Table + " WHERE state=1 AND clusterId>0)")
case nodeconfigs.NodeRoleDNS:
query.Where("nodeId IN (SELECT id FROM edgeNSNodes WHERE state=1 AND clusterId > 0)") // 没有用 SharedNSNodeDAO() 因为有包循环引用的问题
if nodeClusterId > 0 {
query.Where("nodeId IN (SELECT id FROM " + SharedNodeDAO.Table + " WHERE clusterId=:nodeClusterId AND state=1)")
query.Param("nodeClusterId", nodeClusterId)
} else {
switch role {
case nodeconfigs.NodeRoleNode:
query.Where("nodeId IN (SELECT id FROM " + SharedNodeDAO.Table + " WHERE state=1 AND clusterId>0)")
case nodeconfigs.NodeRoleDNS:
query.Where("nodeId IN (SELECT id FROM edgeNSNodes WHERE state=1 AND clusterId > 0)") // 没有用 SharedNSNodeDAO() 因为有包循环引用的问题
}
}
}
if serverId > 0 {
@@ -158,6 +188,9 @@ func (this *NodeLogDAO) CountNodeLogs(tx *dbs.Tx,
if isUnread {
query.Attr("isRead", 0)
}
if len(tag) > 0 {
query.Like("tag", "%"+tag+"%")
}
return query.Count()
}
@@ -165,6 +198,7 @@ func (this *NodeLogDAO) CountNodeLogs(tx *dbs.Tx,
// ListNodeLogs 列出单页日志
func (this *NodeLogDAO) ListNodeLogs(tx *dbs.Tx,
role string,
nodeClusterId int64,
nodeId int64,
serverId int64,
originId int64,
@@ -175,6 +209,7 @@ func (this *NodeLogDAO) ListNodeLogs(tx *dbs.Tx,
level string,
fixedState configutils.BoolState,
isUnread bool,
tag string,
offset int64,
size int64) (result []*NodeLog, err error) {
query := this.Query(tx)
@@ -184,11 +219,16 @@ func (this *NodeLogDAO) ListNodeLogs(tx *dbs.Tx,
if nodeId > 0 {
query.Attr("nodeId", nodeId)
} else {
switch role {
case nodeconfigs.NodeRoleNode:
query.Where("nodeId IN (SELECT id FROM " + SharedNodeDAO.Table + " WHERE state=1 AND clusterId>0)")
case nodeconfigs.NodeRoleDNS:
query.Where("nodeId IN (SELECT id FROM edgeNSNodes WHERE state=1 AND clusterId>0)") // 没有用 SharedNSNodeDAO() 因为有包循环引用的问题
if nodeClusterId > 0 {
query.Where("nodeId IN (SELECT id FROM " + SharedNodeDAO.Table + " WHERE clusterId=:nodeClusterId AND state=1)")
query.Param("nodeClusterId", nodeClusterId)
} else {
switch role {
case nodeconfigs.NodeRoleNode:
query.Where("nodeId IN (SELECT id FROM " + SharedNodeDAO.Table + " WHERE state=1 AND clusterId>0)")
case nodeconfigs.NodeRoleDNS:
query.Where("nodeId IN (SELECT id FROM edgeNSNodes WHERE state=1 AND clusterId>0)") // 没有用 SharedNSNodeDAO() 因为有包循环引用的问题
}
}
}
if serverId > 0 {
@@ -217,11 +257,19 @@ func (this *NodeLogDAO) ListNodeLogs(tx *dbs.Tx,
Param("keyword", "%"+keyword+"%")
}
if len(level) > 0 {
query.Attr("level", level)
var pieces = strings.Split(level, ",")
if len(pieces) == 1 {
query.Attr("level", pieces[0])
} else {
query.Attr("level", pieces)
}
}
if isUnread {
query.Attr("isRead", 0)
}
if len(tag) > 0 {
query.Like("tag", "%"+tag+"%")
}
_, err = query.
Offset(offset).
Limit(size).
@@ -253,6 +301,7 @@ func (this *NodeLogDAO) UpdateNodeLogFixed(tx *dbs.Tx, logId int64) error {
Attr("hash", hash).
Attr("isFixed", false).
Set("isFixed", true).
Set("isRead", true).
UpdateQuickly()
if err != nil {
return err

View File

@@ -4,6 +4,7 @@ package models
type NodeLog struct {
Id uint64 `field:"id"` // ID
Role string `field:"role"` // 节点角色
Type string `field:"type"` // 类型
CreatedAt uint64 `field:"createdAt"` // 创建时间
Tag string `field:"tag"` // 标签
Description string `field:"description"` // 描述
@@ -16,11 +17,13 @@ type NodeLog struct {
Count uint32 `field:"count"` // 重复次数
IsFixed uint8 `field:"isFixed"` // 是否已处理
IsRead uint8 `field:"isRead"` // 是否已读
Params string `field:"params"` // 参数
}
type NodeLogOperator struct {
Id interface{} // ID
Role interface{} // 节点角色
Type interface{} // 类型
CreatedAt interface{} // 创建时间
Tag interface{} // 标签
Description interface{} // 描述
@@ -33,6 +36,7 @@ type NodeLogOperator struct {
Count interface{} // 重复次数
IsFixed interface{} // 是否已处理
IsRead interface{} // 是否已读
Params interface{} // 参数
}
func NewNodeLogOperator() *NodeLogOperator {

View File

@@ -311,20 +311,30 @@ func (this *NodeTaskDAO) FindAllDoingNodeIds(tx *dbs.Tx, role string) ([]int64,
}
// ExistsDoingNodeTasks 检查是否有正在执行的任务
func (this *NodeTaskDAO) ExistsDoingNodeTasks(tx *dbs.Tx, role string) (bool, error) {
return this.Query(tx).
func (this *NodeTaskDAO) ExistsDoingNodeTasks(tx *dbs.Tx, role string, excludeTypes []NodeTaskType) (bool, error) {
var query = this.Query(tx).
Attr("role", role).
Where("(isDone=0 OR (isDone=1 AND isOk=0))").
Gt("nodeId", 0).
Exist()
Gt("nodeId", 0)
if len(excludeTypes) > 0 {
for _, excludeType := range excludeTypes {
query.Neq("type", excludeType)
}
}
return query.Exist()
}
// ExistsErrorNodeTasks 是否有错误的任务
func (this *NodeTaskDAO) ExistsErrorNodeTasks(tx *dbs.Tx, role string) (bool, error) {
return this.Query(tx).
func (this *NodeTaskDAO) ExistsErrorNodeTasks(tx *dbs.Tx, role string, excludeTypes []NodeTaskType) (bool, error) {
var query = this.Query(tx).
Attr("role", role).
Where("(isDone=1 AND isOk=0)").
Exist()
Where("(isDone=1 AND isOk=0)")
if len(excludeTypes) > 0 {
for _, excludeType := range excludeTypes {
query.Neq("type", excludeType)
}
}
return query.Exist()
}
// DeleteNodeTask 删除任务

View File

@@ -55,14 +55,12 @@ func (this *NodeValueDAO) CreateValue(tx *dbs.Tx, clusterId int64, role nodeconf
})
}
// DeleteExpiredValues 清除数据
func (this *NodeValueDAO) DeleteExpiredValues(tx *dbs.Tx) error {
// 删除N天之前的所有数据
expiredDays := 7
day := timeutil.Format("Ymd", time.Now().AddDate(0, 0, -expiredDays))
// Clean 清除数据
func (this *NodeValueDAO) Clean(tx *dbs.Tx) error {
var hour = timeutil.Format("YmdH", time.Now().Add(-2*time.Hour))
_, err := this.Query(tx).
Where("day<:day").
Param("day", day).
Where("hour<=:hour").
Param("hour", hour).
Delete()
if err != nil {
return err

View File

@@ -10,7 +10,7 @@ import (
)
func TestNodeValueDAO_CreateValue(t *testing.T) {
dao := NewNodeValueDAO()
var dao = NewNodeValueDAO()
m := maps.Map{
"hello": "world12344",
}
@@ -20,3 +20,12 @@ func TestNodeValueDAO_CreateValue(t *testing.T) {
}
t.Log("ok")
}
func TestNodeValueDAO_Clean(t *testing.T) {
var dao = NewNodeValueDAO()
err := dao.Clean(nil)
if err != nil {
t.Fatal(err)
}
t.Log("ok")
}

View File

@@ -87,7 +87,20 @@ func (this *OriginDAO) FindOriginName(tx *dbs.Tx, id int64) (string, error) {
}
// CreateOrigin 创建源站
func (this *OriginDAO) CreateOrigin(tx *dbs.Tx, adminId int64, userId int64, name string, addrJSON string, description string, weight int32, isOn bool, connTimeout *shared.TimeDuration, readTimeout *shared.TimeDuration, idleTimeout *shared.TimeDuration, maxConns int32, maxIdleConns int32, domains []string) (originId int64, err error) {
func (this *OriginDAO) CreateOrigin(tx *dbs.Tx,
adminId int64,
userId int64,
name string,
addrJSON string,
description string,
weight int32, isOn bool,
connTimeout *shared.TimeDuration,
readTimeout *shared.TimeDuration,
idleTimeout *shared.TimeDuration,
maxConns int32,
maxIdleConns int32,
certRef *sslconfigs.SSLCertRef,
domains []string) (originId int64, err error) {
op := NewOriginOperator()
op.AdminId = adminId
op.UserId = userId
@@ -133,6 +146,15 @@ func (this *OriginDAO) CreateOrigin(tx *dbs.Tx, adminId int64, userId int64, nam
}
op.Weight = weight
// cert
if certRef != nil {
certRefJSON, err := json.Marshal(certRef)
if err != nil {
return 0, err
}
op.Cert = certRefJSON
}
if len(domains) > 0 {
domainsJSON, err := json.Marshal(domains)
if err != nil {
@@ -152,7 +174,20 @@ func (this *OriginDAO) CreateOrigin(tx *dbs.Tx, adminId int64, userId int64, nam
}
// UpdateOrigin 修改源站
func (this *OriginDAO) UpdateOrigin(tx *dbs.Tx, originId int64, name string, addrJSON string, description string, weight int32, isOn bool, connTimeout *shared.TimeDuration, readTimeout *shared.TimeDuration, idleTimeout *shared.TimeDuration, maxConns int32, maxIdleConns int32, domains []string) error {
func (this *OriginDAO) UpdateOrigin(tx *dbs.Tx,
originId int64,
name string,
addrJSON string,
description string,
weight int32,
isOn bool,
connTimeout *shared.TimeDuration,
readTimeout *shared.TimeDuration,
idleTimeout *shared.TimeDuration,
maxConns int32,
maxIdleConns int32,
certRef *sslconfigs.SSLCertRef,
domains []string) error {
if originId <= 0 {
return errors.New("invalid originId")
}
@@ -201,6 +236,17 @@ func (this *OriginDAO) UpdateOrigin(tx *dbs.Tx, originId int64, name string, add
op.IsOn = isOn
op.Version = dbs.SQL("version+1")
// cert
if certRef != nil {
certRefJSON, err := json.Marshal(certRef)
if err != nil {
return err
}
op.Cert = certRefJSON
} else {
op.Cert = dbs.SQL("NULL")
}
if len(domains) > 0 {
domainsJSON, err := json.Marshal(domains)
if err != nil {

View File

@@ -38,7 +38,7 @@ func init() {
})
}
// 启用条目
// EnableRegionCity 启用条目
func (this *RegionCityDAO) EnableRegionCity(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
@@ -47,7 +47,7 @@ func (this *RegionCityDAO) EnableRegionCity(tx *dbs.Tx, id uint32) error {
return err
}
// 禁用条目
// DisableRegionCity 禁用条目
func (this *RegionCityDAO) DisableRegionCity(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
@@ -56,7 +56,7 @@ func (this *RegionCityDAO) DisableRegionCity(tx *dbs.Tx, id uint32) error {
return err
}
// 查找启用中的条目
// FindEnabledRegionCity 查找启用中的条目
func (this *RegionCityDAO) FindEnabledRegionCity(tx *dbs.Tx, id int64) (*RegionCity, error) {
result, err := this.Query(tx).
Pk(id).
@@ -68,7 +68,7 @@ func (this *RegionCityDAO) FindEnabledRegionCity(tx *dbs.Tx, id int64) (*RegionC
return result.(*RegionCity), err
}
// 根据主键查找名称
// FindRegionCityName 根据主键查找名称
func (this *RegionCityDAO) FindRegionCityName(tx *dbs.Tx, id uint32) (string, error) {
return this.Query(tx).
Pk(id).
@@ -76,7 +76,7 @@ func (this *RegionCityDAO) FindRegionCityName(tx *dbs.Tx, id uint32) (string, er
FindStringCol("")
}
// 根据数据ID查找城市
// FindCityWithDataId 根据数据ID查找城市
func (this *RegionCityDAO) FindCityWithDataId(tx *dbs.Tx, dataId string) (int64, error) {
return this.Query(tx).
Attr("dataId", dataId).
@@ -84,7 +84,7 @@ func (this *RegionCityDAO) FindCityWithDataId(tx *dbs.Tx, dataId string) (int64,
FindInt64Col(0)
}
// 创建城市
// CreateCity 创建城市
func (this *RegionCityDAO) CreateCity(tx *dbs.Tx, provinceId int64, name string, dataId string) (int64, error) {
op := NewRegionCityOperator()
op.ProvinceId = provinceId
@@ -105,7 +105,7 @@ func (this *RegionCityDAO) CreateCity(tx *dbs.Tx, provinceId int64, name string,
return types.Int64(op.Id), nil
}
// 根据城市名查找城市ID
// FindCityIdWithNameCacheable 根据城市名查找城市ID
func (this *RegionCityDAO) FindCityIdWithNameCacheable(tx *dbs.Tx, provinceId int64, cityName string) (int64, error) {
key := cityName + "@" + numberutils.FormatInt64(provinceId)
@@ -132,3 +132,12 @@ func (this *RegionCityDAO) FindCityIdWithNameCacheable(tx *dbs.Tx, provinceId in
return cityId, nil
}
// FindAllEnabledCities 获取所有城市信息
func (this *RegionCityDAO) FindAllEnabledCities(tx *dbs.Tx) (result []*RegionCity, err error) {
_, err = this.Query(tx).
State(RegionCityStateEnabled).
Slice(&result).
FindAll()
return
}

View File

@@ -1 +1,18 @@
package regions
import (
"encoding/json"
"github.com/iwind/TeaGo/logs"
)
func (this *RegionCity) DecodeCodes() []string {
if len(this.Codes) == 0 {
return []string{}
}
result := []string{}
err := json.Unmarshal([]byte(this.Codes), &result)
if err != nil {
logs.Error(err)
}
return result
}

View File

@@ -16,7 +16,8 @@ const (
RegionCountryStateDisabled = 0 // 已禁用
)
var regionCountryNameAndIdCacheMap = map[string]int64{} // country name => int
var regionCountryNameAndIdCacheMap = map[string]int64{} // country name => id
var regionCountryIdAndNameCacheMap = map[int64]string{} // country id => name
type RegionCountryDAO dbs.DAO
@@ -39,7 +40,7 @@ func init() {
})
}
// 启用条目
// EnableRegionCountry 启用条目
func (this *RegionCountryDAO) EnableRegionCountry(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
@@ -48,7 +49,7 @@ func (this *RegionCountryDAO) EnableRegionCountry(tx *dbs.Tx, id uint32) error {
return err
}
// 禁用条目
// DisableRegionCountry 禁用条目
func (this *RegionCountryDAO) DisableRegionCountry(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
@@ -57,7 +58,7 @@ func (this *RegionCountryDAO) DisableRegionCountry(tx *dbs.Tx, id int64) error {
return err
}
// 查找启用中的条目
// FindEnabledRegionCountry 查找启用中的条目
func (this *RegionCountryDAO) FindEnabledRegionCountry(tx *dbs.Tx, id int64) (*RegionCountry, error) {
result, err := this.Query(tx).
Pk(id).
@@ -69,15 +70,29 @@ func (this *RegionCountryDAO) FindEnabledRegionCountry(tx *dbs.Tx, id int64) (*R
return result.(*RegionCountry), err
}
// 根据主键查找名称
// FindRegionCountryName 根据主键查找名称
func (this *RegionCountryDAO) FindRegionCountryName(tx *dbs.Tx, id int64) (string, error) {
return this.Query(tx).
SharedCacheLocker.Lock()
defer SharedCacheLocker.Unlock()
name, ok := regionCountryIdAndNameCacheMap[id]
if ok {
return name, nil
}
name, err := this.Query(tx).
Pk(id).
Result("name").
FindStringCol("")
if err != nil {
return "", err
}
regionCountryIdAndNameCacheMap[id] = name
return name, nil
}
// 根据数据ID查找国家
// FindCountryIdWithDataId 根据数据ID查找国家
func (this *RegionCountryDAO) FindCountryIdWithDataId(tx *dbs.Tx, dataId string) (int64, error) {
return this.Query(tx).
Attr("dataId", dataId).
@@ -85,7 +100,7 @@ func (this *RegionCountryDAO) FindCountryIdWithDataId(tx *dbs.Tx, dataId string)
FindInt64Col(0)
}
// 根据国家名查找国家ID
// FindCountryIdWithName 根据国家名查找国家ID
func (this *RegionCountryDAO) FindCountryIdWithName(tx *dbs.Tx, countryName string) (int64, error) {
return this.Query(tx).
Where("JSON_CONTAINS(codes, :countryName)").
@@ -94,7 +109,7 @@ func (this *RegionCountryDAO) FindCountryIdWithName(tx *dbs.Tx, countryName stri
FindInt64Col(0)
}
// 根据国家名查找国家ID并可使用缓存
// FindCountryIdWithNameCacheable 根据国家名查找国家ID并可使用缓存
func (this *RegionCountryDAO) FindCountryIdWithNameCacheable(tx *dbs.Tx, countryName string) (int64, error) {
SharedCacheLocker.RLock()
provinceId, ok := regionCountryNameAndIdCacheMap[countryName]
@@ -116,7 +131,7 @@ func (this *RegionCountryDAO) FindCountryIdWithNameCacheable(tx *dbs.Tx, country
return countryId, nil
}
// 根据数据ID创建国家
// CreateCountry 根据数据ID创建国家
func (this *RegionCountryDAO) CreateCountry(tx *dbs.Tx, name string, dataId string) (int64, error) {
op := NewRegionCountryOperator()
op.Name = name
@@ -145,7 +160,7 @@ func (this *RegionCountryDAO) CreateCountry(tx *dbs.Tx, name string, dataId stri
return types.Int64(op.Id), nil
}
// 查找所有可用的国家
// FindAllEnabledCountriesOrderByPinyin 查找所有可用的国家
func (this *RegionCountryDAO) FindAllEnabledCountriesOrderByPinyin(tx *dbs.Tx) (result []*RegionCountry, err error) {
_, err = this.Query(tx).
State(RegionCountryStateEnabled).

View File

@@ -36,7 +36,7 @@ func init() {
})
}
// 启用条目
// EnableRegionProvider 启用条目
func (this *RegionProviderDAO) EnableRegionProvider(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
@@ -45,7 +45,7 @@ func (this *RegionProviderDAO) EnableRegionProvider(tx *dbs.Tx, id uint32) error
return err
}
// 禁用条目
// DisableRegionProvider 禁用条目
func (this *RegionProviderDAO) DisableRegionProvider(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
@@ -54,8 +54,8 @@ func (this *RegionProviderDAO) DisableRegionProvider(tx *dbs.Tx, id uint32) erro
return err
}
// 查找启用中的条目
func (this *RegionProviderDAO) FindEnabledRegionProvider(tx *dbs.Tx, id uint32) (*RegionProvider, error) {
// FindEnabledRegionProvider 查找启用中的条目
func (this *RegionProviderDAO) FindEnabledRegionProvider(tx *dbs.Tx, id int64) (*RegionProvider, error) {
result, err := this.Query(tx).
Pk(id).
Attr("state", RegionProviderStateEnabled).
@@ -66,7 +66,7 @@ func (this *RegionProviderDAO) FindEnabledRegionProvider(tx *dbs.Tx, id uint32)
return result.(*RegionProvider), err
}
// 根据主键查找名称
// FindRegionProviderName 根据主键查找名称
func (this *RegionProviderDAO) FindRegionProviderName(tx *dbs.Tx, id uint32) (string, error) {
return this.Query(tx).
Pk(id).
@@ -74,7 +74,7 @@ func (this *RegionProviderDAO) FindRegionProviderName(tx *dbs.Tx, id uint32) (st
FindStringCol("")
}
// 根据服务商名称查找服务商ID
// FindProviderIdWithNameCacheable 根据服务商名称查找服务商ID
func (this *RegionProviderDAO) FindProviderIdWithNameCacheable(tx *dbs.Tx, providerName string) (int64, error) {
SharedCacheLocker.RLock()
providerId, ok := regionProviderNameAndIdCacheMap[providerName]
@@ -100,7 +100,7 @@ func (this *RegionProviderDAO) FindProviderIdWithNameCacheable(tx *dbs.Tx, provi
return providerId, nil
}
// 创建Provider
// CreateProvider 创建Provider
func (this *RegionProviderDAO) CreateProvider(tx *dbs.Tx, name string) (int64, error) {
op := NewRegionProviderOperator()
op.Name = name
@@ -112,3 +112,12 @@ func (this *RegionProviderDAO) CreateProvider(tx *dbs.Tx, name string) (int64, e
op.Codes = codesJSON
return this.SaveInt64(tx, op)
}
// FindAllEnabledProviders 查找所有服务商
func (this *RegionProviderDAO) FindAllEnabledProviders(tx *dbs.Tx) (result []*RegionProvider, err error) {
_, err = this.Query(tx).
State(RegionProviderStateEnabled).
Slice(&result).
FindAll()
return
}

View File

@@ -1 +1,18 @@
package regions
import (
"encoding/json"
"github.com/iwind/TeaGo/logs"
)
func (this *RegionProvider) DecodeCodes() []string {
if len(this.Codes) == 0 {
return []string{}
}
result := []string{}
err := json.Unmarshal([]byte(this.Codes), &result)
if err != nil {
logs.Error(err)
}
return result
}

View File

@@ -2,6 +2,7 @@ package models
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
_ "github.com/go-sql-driver/mysql"
@@ -21,14 +22,14 @@ func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
goman.New(func() {
for range ticker.C {
err := SharedServerDailyStatDAO.Clean(nil, 60) // 只保留60
err := SharedServerDailyStatDAO.Clean(nil, 30) // 只保留N
if err != nil {
logs.Println("ServerDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
})
}

View File

@@ -169,6 +169,7 @@ func (this *ServerDAO) CreateServer(tx *dbs.Tx,
op.ServerNames = serverNamesJSON
}
op.IsAuditing = isAuditing
op.AuditingAt = time.Now().Unix()
if len(auditingServerNamesJSON) > 0 {
op.AuditingServerNames = auditingServerNamesJSON
}
@@ -533,22 +534,22 @@ func (this *ServerDAO) InitServerWeb(tx *dbs.Tx, serverId int64) (int64, error)
}
// FindServerServerNames 查找ServerNames配置
func (this *ServerDAO) FindServerServerNames(tx *dbs.Tx, serverId int64) (serverNamesJSON []byte, isAuditing bool, auditingServerNamesJSON []byte, auditingResultJSON []byte, err error) {
func (this *ServerDAO) FindServerServerNames(tx *dbs.Tx, serverId int64) (serverNamesJSON []byte, isAuditing bool, auditingAt int64, auditingServerNamesJSON []byte, auditingResultJSON []byte, err error) {
if serverId <= 0 {
return
}
one, err := this.Query(tx).
Pk(serverId).
Result("serverNames", "isAuditing", "auditingServerNames", "auditingResult").
Result("serverNames", "isAuditing", "auditingAt", "auditingServerNames", "auditingResult").
Find()
if err != nil {
return nil, false, nil, nil, err
return nil, false, 0, nil, nil, err
}
if one == nil {
return
}
server := one.(*Server)
return []byte(server.ServerNames), server.IsAuditing == 1, []byte(server.AuditingServerNames), []byte(server.AuditingResult), nil
return []byte(server.ServerNames), server.IsAuditing == 1, int64(server.AuditingAt), []byte(server.AuditingServerNames), []byte(server.AuditingResult), nil
}
// UpdateServerNames 修改ServerNames配置
@@ -580,6 +581,9 @@ func (this *ServerDAO) UpdateAuditingServerNames(tx *dbs.Tx, serverId int64, isA
op := NewServerOperator()
op.Id = serverId
op.IsAuditing = isAuditing
if isAuditing {
op.AuditingAt = time.Now().Unix()
}
if len(auditingServerNamesJSON) == 0 {
op.AuditingServerNames = "[]"
} else {
@@ -652,7 +656,7 @@ func (this *ServerDAO) CountAllEnabledServers(tx *dbs.Tx) (int64, error) {
}
// CountAllEnabledServersMatch 计算所有可用服务数量
func (this *ServerDAO) CountAllEnabledServersMatch(tx *dbs.Tx, groupId int64, keyword string, userId int64, clusterId int64, auditingFlag configutils.BoolState, protocolFamily string) (int64, error) {
func (this *ServerDAO) CountAllEnabledServersMatch(tx *dbs.Tx, groupId int64, keyword string, userId int64, clusterId int64, auditingFlag configutils.BoolState, protocolFamilies []string) (int64, error) {
query := this.Query(tx).
State(ServerStateEnabled)
if groupId > 0 {
@@ -678,16 +682,27 @@ func (this *ServerDAO) CountAllEnabledServersMatch(tx *dbs.Tx, groupId int64, ke
if auditingFlag == configutils.BoolStateYes {
query.Attr("isAuditing", true)
}
if protocolFamily == "http" {
query.Where("(http IS NOT NULL OR https IS NOT NULL)")
} else if protocolFamily == "tcp" {
query.Where("(tcp IS NOT NULL OR tls IS NOT NULL)")
var protocolConds = []string{}
for _, family := range protocolFamilies {
switch family {
case "http":
protocolConds = append(protocolConds, "(http IS NOT NULL OR https IS NOT NULL)")
case "tcp":
protocolConds = append(protocolConds, "(tcp IS NOT NULL OR tls IS NOT NULL)")
case "udp":
protocolConds = append(protocolConds, "(udp IS NOT NULL)")
}
}
if len(protocolConds) > 0 {
query.Where("(" + strings.Join(protocolConds, " OR ") + ")")
}
return query.Count()
}
// ListEnabledServersMatch 列出单页的服务
func (this *ServerDAO) ListEnabledServersMatch(tx *dbs.Tx, offset int64, size int64, groupId int64, keyword string, userId int64, clusterId int64, auditingFlag int32, protocolFamily string) (result []*Server, err error) {
func (this *ServerDAO) ListEnabledServersMatch(tx *dbs.Tx, offset int64, size int64, groupId int64, keyword string, userId int64, clusterId int64, auditingFlag int32, protocolFamilies []string) (result []*Server, err error) {
query := this.Query(tx).
State(ServerStateEnabled).
Offset(offset).
@@ -718,10 +733,19 @@ func (this *ServerDAO) ListEnabledServersMatch(tx *dbs.Tx, offset int64, size in
if auditingFlag == 1 {
query.Attr("isAuditing", true)
}
if protocolFamily == "http" {
query.Where("(http IS NOT NULL OR https IS NOT NULL)")
} else if protocolFamily == "tcp" {
query.Where("(tcp IS NOT NULL OR tls IS NOT NULL)")
var protocolConds = []string{}
for _, family := range protocolFamilies {
switch family {
case "http":
protocolConds = append(protocolConds, "(http IS NOT NULL OR https IS NOT NULL)")
case "tcp":
protocolConds = append(protocolConds, "(tcp IS NOT NULL OR tls IS NOT NULL)")
case "udp":
protocolConds = append(protocolConds, "(udp IS NOT NULL)")
}
}
if len(protocolConds) > 0 {
query.Where("(" + strings.Join(protocolConds, " OR ") + ")")
}
_, err = query.FindAll()
@@ -1074,7 +1098,8 @@ func (this *ServerDAO) ComposeServerConfig(tx *dbs.Tx, server *Server, cacheMap
config.UserPlan = &serverconfigs.UserPlanConfig{
DayTo: userPlan.DayTo,
Plan: &serverconfigs.PlanConfig{
Id: int64(plan.Id),
Id: int64(plan.Id),
Name: plan.Name,
},
}
@@ -1237,9 +1262,13 @@ func (this *ServerDAO) CountAllEnabledServersWithNodeClusterId(tx *dbs.Tx, clust
}
// CountAllEnabledServersWithGroupId 计算使用某个分组的服务数量
func (this *ServerDAO) CountAllEnabledServersWithGroupId(tx *dbs.Tx, groupId int64) (int64, error) {
return this.Query(tx).
State(ServerStateEnabled).
func (this *ServerDAO) CountAllEnabledServersWithGroupId(tx *dbs.Tx, groupId int64, userId int64) (int64, error) {
var query = this.Query(tx).
State(ServerStateEnabled)
if userId > 0 {
query.Attr("userId", userId)
}
return query.
Where("JSON_CONTAINS(groupIds, :groupId)").
Param("groupId", numberutils.FormatInt64(groupId)).
Count()
@@ -1536,16 +1565,67 @@ func (this *ServerDAO) FindEnabledServerIdWithReverseProxyId(tx *dbs.Tx, reverse
FindInt64Col(0)
}
// CheckTCPPortIsUsing 检查TCP端口是否被使用
func (this *ServerDAO) CheckTCPPortIsUsing(tx *dbs.Tx, clusterId int64, port int, excludeServerId int64, excludeProtocol string) (bool, error) {
// CheckPortIsUsing 检查端口是否被使用
// protocolFamily支持tcp和udp
func (this *ServerDAO) CheckPortIsUsing(tx *dbs.Tx, clusterId int64, protocolFamily string, port int, excludeServerId int64, excludeProtocol string) (bool, error) {
// 检查是否在别的协议中
if excludeServerId > 0 {
one, err := this.Query(tx).
Pk(excludeServerId).
Result("tcp", "tls", "udp", "http", "https").
Find()
if err != nil {
return false, err
}
if one != nil {
var server = one.(*Server)
for _, protocol := range []string{"http", "https", "tcp", "tls", "udp"} {
if protocol == excludeProtocol {
continue
}
switch protocol {
case "http":
if protocolFamily == "tcp" && lists.ContainsInt(server.DecodeHTTPPorts(), port) {
return true, nil
}
case "https":
if protocolFamily == "tcp" && lists.ContainsInt(server.DecodeHTTPSPorts(), port) {
return true, nil
}
case "tcp":
if protocolFamily == "tcp" && lists.ContainsInt(server.DecodeTCPPorts(), port) {
return true, nil
}
case "tls":
if protocolFamily == "tcp" && lists.ContainsInt(server.DecodeTLSPorts(), port) {
return true, nil
}
case "udp":
// 不需要判断
}
}
}
}
// 其他服务中
query := this.Query(tx).
Attr("clusterId", clusterId).
State(ServerStateEnabled).
Param("port", types.String(port))
if excludeServerId <= 0 {
query.Where("JSON_CONTAINS(tcpPorts, :port)")
switch protocolFamily {
case "tcp", "http", "":
query.Where("JSON_CONTAINS(tcpPorts, :port)")
case "udp":
query.Where("JSON_CONTAINS(udpPorts, :port)")
}
} else {
query.Where("(id!=:serverId AND JSON_CONTAINS(tcpPorts, :port))")
switch protocolFamily {
case "tcp", "http", "":
query.Where("(id!=:serverId AND JSON_CONTAINS(tcpPorts, :port))")
case "udp":
query.Where("(id!=:serverId AND JSON_CONTAINS(udpPorts, :port))")
}
query.Param("serverId", excludeServerId)
}
return query.
@@ -1967,6 +2047,7 @@ func (this *ServerDAO) UpdateServerTrafficLimitConfig(tx *dbs.Tx, serverId int64
return this.UpdateServerTrafficLimitStatus(tx, trafficLimitConfig, serverId, true)
}
// UpdateServerTrafficLimitStatus 修改服务的流量限制状态
func (this *ServerDAO) UpdateServerTrafficLimitStatus(tx *dbs.Tx, trafficLimitConfig *serverconfigs.TrafficLimitConfig, serverId int64, isUpdatingConfig bool) error {
if !trafficLimitConfig.IsOn {
if isUpdatingConfig {
@@ -2075,7 +2156,7 @@ func (this *ServerDAO) ResetServerTotalTraffic(tx *dbs.Tx, serverId int64) error
UpdateQuickly()
}
// FindEnabledServerIdWithUserPlanId 查找使用某个套餐的服务
// FindEnabledServerIdWithUserPlanId 查找使用某个套餐的服务ID
func (this *ServerDAO) FindEnabledServerIdWithUserPlanId(tx *dbs.Tx, userPlanId int64) (int64, error) {
return this.Query(tx).
State(ServerStateEnabled).
@@ -2084,6 +2165,19 @@ func (this *ServerDAO) FindEnabledServerIdWithUserPlanId(tx *dbs.Tx, userPlanId
FindInt64Col(0)
}
// FindEnabledServerWithUserPlanId 查找使用某个套餐的服务
func (this *ServerDAO) FindEnabledServerWithUserPlanId(tx *dbs.Tx, userPlanId int64) (*Server, error) {
one, err := this.Query(tx).
State(ServerStateEnabled).
Attr("userPlanId", userPlanId).
Result("id", "name", "serverNames", "type").
Find()
if err != nil || one == nil {
return nil, err
}
return one.(*Server), nil
}
// UpdateServersClusterIdWithPlanId 修改套餐所在集群
func (this *ServerDAO) UpdateServersClusterIdWithPlanId(tx *dbs.Tx, planId int64, clusterId int64) error {
return this.Query(tx).
@@ -2095,6 +2189,40 @@ func (this *ServerDAO) UpdateServersClusterIdWithPlanId(tx *dbs.Tx, planId int64
// UpdateServerUserPlanId 设置服务所属套餐
func (this *ServerDAO) UpdateServerUserPlanId(tx *dbs.Tx, serverId int64, userPlanId int64) error {
// 取消套餐
if userPlanId <= 0 {
// 所属用户
userId, err := this.Query(tx).
Pk(serverId).
Result("userId").
FindInt64Col(0)
if err != nil {
return err
}
if userId <= 0 {
return errors.New("can not cancel the server plan, because the server has no user")
}
clusterId, err := SharedUserDAO.FindUserClusterId(tx, userId)
if err != nil {
return err
}
if clusterId <= 0 {
return errors.New("can not cancel the server plan, because the server use does not have a cluster")
}
err = this.Query(tx).
Pk(serverId).
Set("userPlanId", userPlanId).
Set("clusterId", clusterId).
UpdateQuickly()
if err != nil {
return err
}
return this.NotifyUpdate(tx, serverId)
}
// 设置新套餐
userPlan, err := SharedUserPlanDAO.FindEnabledUserPlan(tx, userPlanId, nil)
if err != nil {
return err

View File

@@ -100,7 +100,7 @@ func TestServerDAO_CheckPortIsUsing(t *testing.T) {
// t.Log("isUsing:", isUsing)
//}
{
isUsing, err := SharedServerDAO.CheckTCPPortIsUsing(tx, 18, 3306, 0, "tcp")
isUsing, err := SharedServerDAO.CheckPortIsUsing(tx, 18, "tcp", 3306, 0, "")
if err != nil {
t.Fatal(err)
}

View File

@@ -77,10 +77,11 @@ func (this *ServerGroupDAO) FindServerGroupName(tx *dbs.Tx, id int64) (string, e
}
// CreateGroup 创建分组
func (this *ServerGroupDAO) CreateGroup(tx *dbs.Tx, name string) (groupId int64, err error) {
func (this *ServerGroupDAO) CreateGroup(tx *dbs.Tx, name string, userId int64) (groupId int64, err error) {
op := NewServerGroupOperator()
op.State = ServerGroupStateEnabled
op.Name = name
op.UserId = userId
op.IsOn = true
err = this.Save(tx, op)
if err != nil {
@@ -102,9 +103,15 @@ func (this *ServerGroupDAO) UpdateGroup(tx *dbs.Tx, groupId int64, name string)
}
// FindAllEnabledGroups 查找所有分组
func (this *ServerGroupDAO) FindAllEnabledGroups(tx *dbs.Tx) (result []*ServerGroup, err error) {
_, err = this.Query(tx).
State(ServerGroupStateEnabled).
func (this *ServerGroupDAO) FindAllEnabledGroups(tx *dbs.Tx, userId int64) (result []*ServerGroup, err error) {
var query = this.Query(tx).
State(ServerGroupStateEnabled)
if userId > 0 {
query.Attr("userId", userId)
} else {
query.Attr("userId", 0)
}
_, err = query.
Desc("order").
AscPk().
Slice(&result).
@@ -113,9 +120,13 @@ func (this *ServerGroupDAO) FindAllEnabledGroups(tx *dbs.Tx) (result []*ServerGr
}
// UpdateGroupOrders 修改分组排序
func (this *ServerGroupDAO) UpdateGroupOrders(tx *dbs.Tx, groupIds []int64) error {
func (this *ServerGroupDAO) UpdateGroupOrders(tx *dbs.Tx, groupIds []int64, userId int64) error {
for index, groupId := range groupIds {
_, err := this.Query(tx).
var query = this.Query(tx)
if userId > 0 {
query.Attr("userId", userId)
}
_, err := query.
Pk(groupId).
Set("order", len(groupIds)-index).
Update()
@@ -383,6 +394,22 @@ func (this *ServerGroupDAO) FindEnabledGroupIdWithReverseProxyId(tx *dbs.Tx, rev
FindInt64Col(0)
}
// CheckUserGroup 检查用户分组
func (this *ServerGroupDAO) CheckUserGroup(tx *dbs.Tx, userId int64, groupId int64) error {
b, err := this.Query(tx).
Pk(groupId).
Attr("userId", userId).
State(ServerGroupStateEnabled).
Exist()
if err != nil {
return err
}
if !b {
return ErrNotFound
}
return nil
}
// NotifyUpdate 通知更新
func (this *ServerGroupDAO) NotifyUpdate(tx *dbs.Tx, groupId int64) error {
serverIds, err := SharedServerDAO.FindAllEnabledServerIdsWithGroupId(tx, groupId)

View File

@@ -10,6 +10,7 @@ type Server struct {
Name string `field:"name"` // 名称
Description string `field:"description"` // 描述
ServerNames string `field:"serverNames"` // 域名列表
AuditingAt uint64 `field:"auditingAt"` // 审核提交时间
AuditingServerNames string `field:"auditingServerNames"` // 审核中的域名
IsAuditing uint8 `field:"isAuditing"` // 是否正在审核
AuditingResult string `field:"auditingResult"` // 审核结果
@@ -53,6 +54,7 @@ type ServerOperator struct {
Name interface{} // 名称
Description interface{} // 描述
ServerNames interface{} // 域名列表
AuditingAt interface{} // 审核提交时间
AuditingServerNames interface{} // 审核中的域名
IsAuditing interface{} // 是否正在审核
AuditingResult interface{} // 审核结果

View File

@@ -3,6 +3,7 @@ package models
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
)
// DecodeGroupIds 解析服务所属分组ID
@@ -19,3 +20,108 @@ func (this *Server) DecodeGroupIds() []int64 {
}
return result
}
// DecodeHTTPPorts 获取HTTP所有端口
func (this *Server) DecodeHTTPPorts() (ports []int) {
if len(this.Http) > 0 && this.Http != "null" {
config := &serverconfigs.HTTPProtocolConfig{}
err := json.Unmarshal([]byte(this.Http), config)
if err != nil {
return nil
}
err = config.Init()
if err != nil {
return nil
}
for _, listen := range config.Listen {
for i := listen.MinPort; i <= listen.MaxPort; i++ {
ports = append(ports, i)
}
}
}
return
}
// DecodeHTTPSPorts 获取HTTPS所有端口
func (this *Server) DecodeHTTPSPorts() (ports []int) {
if len(this.Https) > 0 && this.Https != "null" {
config := &serverconfigs.HTTPSProtocolConfig{}
err := json.Unmarshal([]byte(this.Https), config)
if err != nil {
return nil
}
err = config.Init()
if err != nil {
return nil
}
for _, listen := range config.Listen {
for i := listen.MinPort; i <= listen.MaxPort; i++ {
ports = append(ports, i)
}
}
}
return
}
// DecodeTCPPorts 获取TCP所有端口
func (this *Server) DecodeTCPPorts() (ports []int) {
if len(this.Tcp) > 0 && this.Tcp != "null" {
config := &serverconfigs.TCPProtocolConfig{}
err := json.Unmarshal([]byte(this.Tcp), config)
if err != nil {
return nil
}
err = config.Init()
if err != nil {
return nil
}
for _, listen := range config.Listen {
for i := listen.MinPort; i <= listen.MaxPort; i++ {
ports = append(ports, i)
}
}
}
return
}
// DecodeTLSPorts 获取TLS所有端口
func (this *Server) DecodeTLSPorts() (ports []int) {
if len(this.Tls) > 0 && this.Tls != "null" {
config := &serverconfigs.TLSProtocolConfig{}
err := json.Unmarshal([]byte(this.Tls), config)
if err != nil {
return nil
}
err = config.Init()
if err != nil {
return nil
}
for _, listen := range config.Listen {
for i := listen.MinPort; i <= listen.MaxPort; i++ {
ports = append(ports, i)
}
}
}
return
}
// DecodeUDPPorts 获取UDP所有端口
func (this *Server) DecodeUDPPorts() (ports []int) {
if len(this.Udp) > 0 && this.Udp != "null" {
config := &serverconfigs.UDPProtocolConfig{}
err := json.Unmarshal([]byte(this.Udp), config)
if err != nil {
return nil
}
err = config.Init()
if err != nil {
return nil
}
for _, listen := range config.Listen {
for i := listen.MinPort; i <= listen.MaxPort; i++ {
ports = append(ports, i)
}
}
}
return
}

View File

@@ -2,6 +2,7 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
_ "github.com/go-sql-driver/mysql"
@@ -19,14 +20,14 @@ func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
goman.New(func() {
for range ticker.C {
err := SharedNodeClusterTrafficDailyStatDAO.Clean(nil, 60) // 只保留60
err := SharedNodeClusterTrafficDailyStatDAO.Clean(nil, 30) // 只保留N
if err != nil {
remotelogs.Error("NodeClusterTrafficDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
})
}

View File

@@ -2,6 +2,7 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
_ "github.com/go-sql-driver/mysql"
@@ -19,14 +20,14 @@ func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
goman.New(func() {
for range ticker.C {
err := SharedNodeTrafficDailyStatDAO.Clean(nil, 60) // 只保留60
err := SharedNodeTrafficDailyStatDAO.Clean(nil, 30) // 只保留N
if err != nil {
remotelogs.Error("NodeTrafficDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
})
}

View File

@@ -2,6 +2,7 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
_ "github.com/go-sql-driver/mysql"
@@ -19,14 +20,14 @@ func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
goman.New(func() {
for range ticker.C {
err := SharedNodeTrafficHourlyStatDAO.Clean(nil, 60) // 只保留60
err := SharedNodeTrafficHourlyStatDAO.Clean(nil, 15) // 只保留N
if err != nil {
remotelogs.Error("NodeTrafficHourlyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
})
}

View File

@@ -2,12 +2,32 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedServerClientBrowserMonthlyStatDAO.Clean(nil)
if err != nil {
remotelogs.Error("ServerClientBrowserMonthlyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
type ServerClientBrowserMonthlyStatDAO dbs.DAO
func NewServerClientBrowserMonthlyStatDAO() *ServerClientBrowserMonthlyStatDAO {
@@ -29,7 +49,7 @@ func init() {
})
}
// 增加数量
// IncreaseMonthlyCount 增加数量
func (this *ServerClientBrowserMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx, serverId int64, browserId int64, version string, month string, count int64) error {
if len(month) != 6 {
return errors.New("invalid month '" + month + "'")
@@ -51,7 +71,7 @@ func (this *ServerClientBrowserMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx,
return nil
}
// 查找单页数据
// ListStats 查找单页数据
func (this *ServerClientBrowserMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId int64, month string, offset int64, size int64) (result []*ServerClientBrowserMonthlyStat, err error) {
query := this.Query(tx).
Attr("serverId", serverId).
@@ -63,3 +83,13 @@ func (this *ServerClientBrowserMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId in
_, err = query.FindAll()
return
}
// Clean 清理统计数据
func (this *ServerClientBrowserMonthlyStatDAO) Clean(tx *dbs.Tx) error {
// 只保留两个月的
var month = timeutil.Format("Ym", time.Now().AddDate(0, -2, 0))
_, err := this.Query(tx).
Lte("month", month).
Delete()
return err
}

View File

@@ -17,3 +17,12 @@ func TestServerClientBrowserMonthlyStatDAO_IncreaseMonthlyCount(t *testing.T) {
}
t.Log("ok")
}
func TestServerClientBrowserMonthlyStatDAO_Clean(t *testing.T) {
var dao = NewServerClientBrowserMonthlyStatDAO()
err := dao.Clean(nil)
if err != nil {
t.Fatal(err)
}
t.Log("ok")
}

View File

@@ -2,12 +2,32 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedServerClientSystemMonthlyStatDAO.Clean(nil)
if err != nil {
remotelogs.Error("ServerClientSystemMonthlyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
type ServerClientSystemMonthlyStatDAO dbs.DAO
func NewServerClientSystemMonthlyStatDAO() *ServerClientSystemMonthlyStatDAO {
@@ -29,7 +49,7 @@ func init() {
})
}
// 增加数量
// IncreaseMonthlyCount 增加数量
func (this *ServerClientSystemMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx, serverId int64, systemId int64, version string, month string, count int64) error {
if len(month) != 6 {
return errors.New("invalid month '" + month + "'")
@@ -51,7 +71,7 @@ func (this *ServerClientSystemMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx, s
return nil
}
// 查找单页数据
// ListStats 查找单页数据
func (this *ServerClientSystemMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId int64, month string, offset int64, size int64) (result []*ServerClientSystemMonthlyStat, err error) {
query := this.Query(tx).
Attr("serverId", serverId).
@@ -63,3 +83,13 @@ func (this *ServerClientSystemMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId int
_, err = query.FindAll()
return
}
// Clean 清理统计数据
func (this *ServerClientSystemMonthlyStatDAO) Clean(tx *dbs.Tx) error {
// 只保留两个月的
var month = timeutil.Format("Ym", time.Now().AddDate(0, -2, 0))
_, err := this.Query(tx).
Lte("month", month).
Delete()
return err
}

View File

@@ -2,4 +2,14 @@ package stats
import (
_ "github.com/go-sql-driver/mysql"
"testing"
)
func TestServerClientSystemMonthlyStatDAO_Clean(t *testing.T) {
var dao = NewServerClientSystemMonthlyStatDAO()
err := dao.Clean(nil)
if err != nil {
t.Fatal(err)
}
t.Log("ok")
}

View File

@@ -2,6 +2,7 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
@@ -22,14 +23,14 @@ func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
goman.New(func() {
for range ticker.C {
err := SharedServerDomainHourlyStatDAO.Clean(nil, 7) // 只保留7天
if err != nil {
remotelogs.Error("ServerDomainHourlyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
})
}

View File

@@ -3,6 +3,7 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
@@ -19,14 +20,14 @@ func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
goman.New(func() {
for range ticker.C {
err := SharedServerHTTPFirewallDailyStatDAO.Clean(nil, 60) // 只保留60
err := SharedServerHTTPFirewallDailyStatDAO.Clean(nil, 30) // 只保留N
if err != nil {
remotelogs.Error("ServerHTTPFirewallDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
})
}
@@ -133,4 +134,3 @@ func (this *ServerHTTPFirewallDailyStatDAO) Clean(tx *dbs.Tx, days int) error {
Delete()
return err
}

View File

@@ -3,6 +3,7 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
@@ -19,14 +20,14 @@ func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
goman.New(func() {
for range ticker.C {
err := SharedServerHTTPFirewallHourlyStatDAO.Clean(nil, 60) // 只保留60
err := SharedServerHTTPFirewallHourlyStatDAO.Clean(nil, 15) // 只保留N
if err != nil {
remotelogs.Error("ServerHTTPFirewallHourlyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
})
}

View File

@@ -3,12 +3,32 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models/regions"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedServerRegionCityMonthlyStatDAO.Clean(nil)
if err != nil {
remotelogs.Error("SharedServerRegionCityMonthlyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
type ServerRegionCityMonthlyStatDAO dbs.DAO
func NewServerRegionCityMonthlyStatDAO() *ServerRegionCityMonthlyStatDAO {
@@ -30,7 +50,7 @@ func init() {
})
}
// 增加数量
// IncreaseMonthlyCount 增加数量
func (this *ServerRegionCityMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx, serverId int64, cityId int64, month string, count int64) error {
if len(month) != 6 {
return errors.New("invalid month '" + month + "'")
@@ -51,7 +71,7 @@ func (this *ServerRegionCityMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx, ser
return nil
}
// 查找单页数据
// ListStats 查找单页数据
func (this *ServerRegionCityMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId int64, month string, countryId int64, provinceId int64, offset int64, size int64) (result []*ServerRegionCityMonthlyStat, err error) {
query := this.Query(tx).
Attr("serverId", serverId).
@@ -71,3 +91,13 @@ func (this *ServerRegionCityMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId int64
_, err = query.FindAll()
return
}
// Clean 清理统计数据
func (this *ServerRegionCityMonthlyStatDAO) Clean(tx *dbs.Tx) error {
// 只保留两个月的
var month = timeutil.Format("Ym", time.Now().AddDate(0, -2, 0))
_, err := this.Query(tx).
Lte("month", month).
Delete()
return err
}

View File

@@ -2,4 +2,14 @@ package stats
import (
_ "github.com/go-sql-driver/mysql"
"testing"
)
func TestServerRegionCityMonthlyStatDAO_Clean(t *testing.T) {
var dao = NewServerRegionCityMonthlyStatDAO()
err := dao.Clean(nil)
if err != nil {
t.Fatal(err)
}
t.Log("ok")
}

View File

@@ -0,0 +1,173 @@
package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedServerRegionCountryDailyStatDAO.Clean(nil)
if err != nil {
remotelogs.Error("ServerRegionCountryDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
type ServerRegionCountryDailyStatDAO dbs.DAO
func NewServerRegionCountryDailyStatDAO() *ServerRegionCountryDailyStatDAO {
return dbs.NewDAO(&ServerRegionCountryDailyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeServerRegionCountryDailyStats",
Model: new(ServerRegionCountryDailyStat),
PkName: "id",
},
}).(*ServerRegionCountryDailyStatDAO)
}
var SharedServerRegionCountryDailyStatDAO *ServerRegionCountryDailyStatDAO
func init() {
dbs.OnReady(func() {
SharedServerRegionCountryDailyStatDAO = NewServerRegionCountryDailyStatDAO()
})
}
// IncreaseDailyStat 增加统计
func (this *ServerRegionCountryDailyStatDAO) IncreaseDailyStat(tx *dbs.Tx, serverId int64, countryId int64, day string, bytes int64, countRequests int64, attackBytes int64, countAttackRequests int64) error {
if len(day) != 8 {
return errors.New("invalid day '" + day + "'")
}
err := this.Query(tx).
Param("bytes", bytes).
Param("countRequests", countRequests).
Param("attackBytes", attackBytes).
Param("countAttackRequests", countAttackRequests).
InsertOrUpdateQuickly(maps.Map{
"serverId": serverId,
"countryId": countryId,
"day": day,
"bytes": bytes,
"attackBytes": attackBytes,
"countRequests": countRequests,
"countAttackRequests": countAttackRequests,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
})
if err != nil {
return err
}
return nil
}
// ListServerStats 查找单页数据
func (this *ServerRegionCountryDailyStatDAO) ListServerStats(tx *dbs.Tx, serverId int64, day string, orderField string, offset int64, size int64) (result []*ServerRegionCountryDailyStat, err error) {
query := this.Query(tx).
Attr("serverId", serverId).
Attr("day", day).
Offset(offset).
Limit(size).
Slice(&result)
switch orderField {
case "bytes":
query.Desc("bytes")
case "countRequests":
query.Desc("countRequests")
case "attackBytes":
query.Desc("attackBytes")
query.Gt("attackBytes", 0)
case "countAttackRequests":
query.Desc("countAttackRequests")
query.Gt("countAttackRequests", 0)
}
_, err = query.FindAll()
return
}
// ListSumStats 查找总体数据
func (this *ServerRegionCountryDailyStatDAO) ListSumStats(tx *dbs.Tx, day string, orderField string, offset int64, size int64) (result []*ServerRegionCountryDailyStat, err error) {
query := this.Query(tx).
Attr("day", day).
Result("countryId", "SUM(bytes) AS bytes", "SUM(countRequests) AS countRequests", "SUM(attackBytes) AS attackBytes", "SUM(countAttackRequests) AS countAttackRequests").
Group("countryId").
Offset(offset).
Limit(size).
Slice(&result)
switch orderField {
case "bytes":
query.Desc("bytes")
case "countRequests":
query.Desc("countRequests")
case "attackBytes":
query.Desc("attackBytes")
query.Gt("attackBytes", 0)
case "countAttackRequests":
query.Desc("countAttackRequests")
query.Gt("countAttackRequests", 0)
}
_, err = query.FindAll()
return
}
// SumDailyTotalBytes 计算总流量
func (this *ServerRegionCountryDailyStatDAO) SumDailyTotalBytes(tx *dbs.Tx, day string) (int64, error) {
return this.Query(tx).
Attr("day", day).
SumInt64("bytes", 0)
}
// SumDailyTotalAttackRequests 计算总攻击次数
func (this *ServerRegionCountryDailyStatDAO) SumDailyTotalAttackRequests(tx *dbs.Tx, day string) (int64, error) {
return this.Query(tx).
Attr("day", day).
SumInt64("countAttackRequests", 0)
}
// SumDailyTotalBytesWithServerId 计算单个服务的总流量
func (this *ServerRegionCountryDailyStatDAO) SumDailyTotalBytesWithServerId(tx *dbs.Tx, day string, serverId int64) (int64, error) {
return this.Query(tx).
Attr("day", day).
Attr("serverId", serverId).
SumInt64("bytes", 0)
}
// SumDailyTotalAttackRequestsWithServerId 计算单个服务的总攻击次数
func (this *ServerRegionCountryDailyStatDAO) SumDailyTotalAttackRequestsWithServerId(tx *dbs.Tx, day string, serverId int64) (int64, error) {
return this.Query(tx).
Attr("day", day).
Attr("serverId", serverId).
SumInt64("countAttackRequests", 0)
}
// Clean 清理统计数据
func (this *ServerRegionCountryDailyStatDAO) Clean(tx *dbs.Tx) error {
// 只保留7天的
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -7))
_, err := this.Query(tx).
Lte("day", day).
Delete()
return err
}

View File

@@ -0,0 +1,34 @@
package stats
import (
"encoding/json"
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/dbs"
timeutil "github.com/iwind/TeaGo/utils/time"
"testing"
)
func TestServerRegionCountryDailyStatDAO_IncreaseDailyStat(t *testing.T) {
var tx *dbs.Tx
err := NewServerRegionCountryDailyStatDAO().IncreaseDailyStat(tx, 1, 3, timeutil.Format("Ymd"), 2, 2, 1, 1)
if err != nil {
t.Fatal(err)
}
t.Log("ok")
}
func TestServerRegionCountryDailyStatDAO_ListSumStats(t *testing.T) {
var tx *dbs.Tx
stats, err := NewServerRegionCountryDailyStatDAO().ListSumStats(tx, timeutil.Format("Ymd"), "countAttackRequests", 0, 10)
if err != nil {
t.Fatal(err)
}
for _, stat := range stats {
statJSON, err := json.Marshal(stat)
if err != nil {
t.Fatal(err)
}
t.Log(string(statJSON))
}
}

View File

@@ -0,0 +1,28 @@
package stats
// ServerRegionCountryDailyStat 服务用户区域分布统计(按天)
type ServerRegionCountryDailyStat struct {
Id uint64 `field:"id"` // ID
ServerId uint32 `field:"serverId"` // 服务ID
CountryId uint32 `field:"countryId"` // 国家/区域ID
Day string `field:"day"` // 日期YYYYMMDD
CountRequests uint64 `field:"countRequests"` // 请求数量
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击数量
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
Bytes uint64 `field:"bytes"` // 总流量
}
type ServerRegionCountryDailyStatOperator struct {
Id interface{} // ID
ServerId interface{} // 服务ID
CountryId interface{} // 国家/区域ID
Day interface{} // 日期YYYYMMDD
CountRequests interface{} // 请求数量
CountAttackRequests interface{} // 攻击数量
AttackBytes interface{} // 攻击流量
Bytes interface{} // 总流量
}
func NewServerRegionCountryDailyStatOperator() *ServerRegionCountryDailyStatOperator {
return &ServerRegionCountryDailyStatOperator{}
}

View File

@@ -0,0 +1 @@
package stats

View File

@@ -2,12 +2,32 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedServerRegionCountryMonthlyStatDAO.Clean(nil)
if err != nil {
remotelogs.Error("SharedServerRegionCountryMonthlyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
type ServerRegionCountryMonthlyStatDAO dbs.DAO
func NewServerRegionCountryMonthlyStatDAO() *ServerRegionCountryMonthlyStatDAO {
@@ -29,7 +49,7 @@ func init() {
})
}
// 增加数量
// IncreaseMonthlyCount 增加数量
func (this *ServerRegionCountryMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx, serverId int64, countryId int64, month string, count int64) error {
if len(month) != 6 {
return errors.New("invalid month '" + month + "'")
@@ -50,7 +70,7 @@ func (this *ServerRegionCountryMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx,
return nil
}
// 查找单页数据
// ListStats 查找单页数据
func (this *ServerRegionCountryMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId int64, month string, offset int64, size int64) (result []*ServerRegionCountryMonthlyStat, err error) {
query := this.Query(tx).
Attr("serverId", serverId).
@@ -62,3 +82,13 @@ func (this *ServerRegionCountryMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId in
_, err = query.FindAll()
return
}
// Clean 清理统计数据
func (this *ServerRegionCountryMonthlyStatDAO) Clean(tx *dbs.Tx) error {
// 只保留两个月的
var month = timeutil.Format("Ym", time.Now().AddDate(0, -2, 0))
_, err := this.Query(tx).
Lte("month", month).
Delete()
return err
}

View File

@@ -2,12 +2,32 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedServerRegionProviderMonthlyStatDAO.Clean(nil)
if err != nil {
remotelogs.Error("SharedServerRegionProviderMonthlyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
type ServerRegionProviderMonthlyStatDAO dbs.DAO
func NewServerRegionProviderMonthlyStatDAO() *ServerRegionProviderMonthlyStatDAO {
@@ -29,7 +49,7 @@ func init() {
})
}
// 增加数量
// IncreaseMonthlyCount 增加数量
func (this *ServerRegionProviderMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx, serverId int64, providerId int64, month string, count int64) error {
if len(month) != 6 {
return errors.New("invalid month '" + month + "'")
@@ -50,7 +70,7 @@ func (this *ServerRegionProviderMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx,
return nil
}
// 查找单页数据
// ListStats 查找单页数据
func (this *ServerRegionProviderMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId int64, month string, offset int64, size int64) (result []*ServerRegionProviderMonthlyStat, err error) {
query := this.Query(tx).
Attr("serverId", serverId).
@@ -62,3 +82,13 @@ func (this *ServerRegionProviderMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId i
_, err = query.FindAll()
return
}
// Clean 清理统计数据
func (this *ServerRegionProviderMonthlyStatDAO) Clean(tx *dbs.Tx) error {
// 只保留两个月的
var month = timeutil.Format("Ym", time.Now().AddDate(0, -2, 0))
_, err := this.Query(tx).
Lte("month", month).
Delete()
return err
}

View File

@@ -2,4 +2,15 @@ package stats
import (
_ "github.com/go-sql-driver/mysql"
"testing"
)
func TestServerRegionProviderMonthlyStatDAO_Clean(t *testing.T) {
var dao = NewServerRegionProviderMonthlyStatDAO()
err := dao.Clean(nil)
if err != nil {
t.Fatal(err)
}
t.Log("ok")
}

View File

@@ -3,12 +3,32 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models/regions"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedServerRegionProvinceMonthlyStatDAO.Clean(nil)
if err != nil {
remotelogs.Error("SharedServerRegionProvinceMonthlyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
type ServerRegionProvinceMonthlyStatDAO dbs.DAO
func NewServerRegionProvinceMonthlyStatDAO() *ServerRegionProvinceMonthlyStatDAO {
@@ -30,7 +50,7 @@ func init() {
})
}
// 增加数量
// IncreaseMonthlyCount 增加数量
func (this *ServerRegionProvinceMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx, serverId int64, provinceId int64, month string, count int64) error {
if len(month) != 6 {
return errors.New("invalid month '" + month + "'")
@@ -51,7 +71,7 @@ func (this *ServerRegionProvinceMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx,
return nil
}
// 查找单页数据
// ListStats 查找单页数据
func (this *ServerRegionProvinceMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId int64, month string, countryId int64, offset int64, size int64) (result []*ServerRegionProvinceMonthlyStat, err error) {
query := this.Query(tx).
Attr("serverId", serverId).
@@ -68,3 +88,13 @@ func (this *ServerRegionProvinceMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId i
_, err = query.FindAll()
return
}
// Clean 清理统计数据
func (this *ServerRegionProvinceMonthlyStatDAO) Clean(tx *dbs.Tx) error {
// 只保留两个月的
var month = timeutil.Format("Ym", time.Now().AddDate(0, -2, 0))
_, err := this.Query(tx).
Lte("month", month).
Delete()
return err
}

View File

@@ -2,6 +2,7 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
_ "github.com/go-sql-driver/mysql"
@@ -19,14 +20,14 @@ func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
goman.New(func() {
for range ticker.C {
err := SharedTrafficDailyStatDAO.Clean(nil, 60) // 只保留60
err := SharedTrafficDailyStatDAO.Clean(nil, 30) // 只保留N
if err != nil {
remotelogs.Error("TrafficDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
})
}

View File

@@ -2,6 +2,7 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
_ "github.com/go-sql-driver/mysql"
@@ -19,14 +20,14 @@ func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
goman.New(func() {
for range ticker.C {
err := SharedTrafficHourlyStatDAO.Clean(nil, 60) // 只保留60
err := SharedTrafficHourlyStatDAO.Clean(nil, 15) // 只保留N
if err != nil {
remotelogs.Error("TrafficHourlyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
})
}

View File

@@ -3,8 +3,12 @@ package models
import (
"encoding/json"
"fmt"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeAPI/internal/zero"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
@@ -40,6 +44,16 @@ func (this *SysSettingDAO) UpdateSetting(tx *dbs.Tx, codeFormat string, valueJSO
countRetries := 3
var lastErr error
defer func() {
if lastErr == nil {
err := this.NotifyUpdate(tx, codeFormat)
if err != nil {
remotelogs.Error("SysSettingDAO", "notify update failed: "+err.Error())
}
}
}()
for i := 0; i < countRetries; i++ {
settingId, err := this.Query(tx).
Attr("code", codeFormat).
@@ -61,6 +75,8 @@ func (this *SysSettingDAO) UpdateSetting(tx *dbs.Tx, codeFormat string, valueJSO
// 因为错误的原因可能是因为code冲突所以这里我们继续执行
continue
}
lastErr = nil
return nil
}
@@ -72,6 +88,8 @@ func (this *SysSettingDAO) UpdateSetting(tx *dbs.Tx, codeFormat string, valueJSO
if err != nil {
return err
}
lastErr = nil
break
}
return lastErr
@@ -121,3 +139,60 @@ func (this *SysSettingDAO) ReadGlobalConfig(tx *dbs.Tx) (*serverconfigs.GlobalCo
}
return config, nil
}
// ReadUserServerConfig 读取用户服务配置
func (this *SysSettingDAO) ReadUserServerConfig(tx *dbs.Tx) (*userconfigs.UserServerConfig, error) {
valueJSON, err := this.ReadSetting(tx, systemconfigs.SettingCodeUserServerConfig)
if err != nil {
return nil, err
}
if len(valueJSON) == 0 {
return userconfigs.DefaultUserServerConfig(), nil
}
var config = userconfigs.DefaultUserServerConfig()
err = json.Unmarshal(valueJSON, config)
if err != nil {
return nil, err
}
return config, nil
}
// ReadAdminUIConfig 读取管理员界面配置
func (this *SysSettingDAO) ReadAdminUIConfig(tx *dbs.Tx, cacheMap *utils.CacheMap) (*systemconfigs.AdminUIConfig, error) {
var cacheKey = this.Table + ":ReadAdminUIConfig"
if cacheMap != nil {
cache, ok := cacheMap.Get(cacheKey)
if ok && cache != nil {
return cache.(*systemconfigs.AdminUIConfig), nil
}
}
valueJSON, err := this.ReadSetting(tx, systemconfigs.SettingCodeAdminUIConfig)
if err != nil {
return nil, err
}
if len(valueJSON) > 0 {
var config = &systemconfigs.AdminUIConfig{}
err = json.Unmarshal(valueJSON, config)
if err != nil {
return nil, err
}
if cacheMap != nil {
cacheMap.Put(cacheKey, config)
}
return config, nil
}
return &systemconfigs.AdminUIConfig{}, nil
}
// NotifyUpdate 通知更改
func (this *SysSettingDAO) NotifyUpdate(tx *dbs.Tx, code string) error {
switch code {
case systemconfigs.SettingCodeAccessLogQueue:
accessLogQueueChanged <- zero.New()
}
return nil
}

View File

@@ -2,6 +2,7 @@ package models
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
@@ -20,7 +21,7 @@ func init() {
dbs.OnReadyDone(func() {
var generatedMonths = []string{}
go func() {
goman.New(func() {
// 自动生成账单任务
var ticker = time.NewTicker(1 * time.Minute)
for range ticker.C {
@@ -37,7 +38,7 @@ func init() {
generatedMonths = append(generatedMonths, lastMonth)
}
}
}()
})
})
}
@@ -68,6 +69,17 @@ func init() {
})
}
// FindUserBill 查找单个账单
func (this *UserBillDAO) FindUserBill(tx *dbs.Tx, billId int64) (*UserBill, error) {
one, err := this.Query(tx).
Pk(billId).
Find()
if err != nil || one == nil {
return nil, err
}
return one.(*UserBill), nil
}
// CountAllUserBills 计算账单数量
func (this *UserBillDAO) CountAllUserBills(tx *dbs.Tx, isPaid int32, userId int64, month string) (int64, error) {
query := this.Query(tx)
@@ -330,3 +342,33 @@ func (this *UserBillDAO) GenerateBillCode(tx *dbs.Tx) (string, error) {
}
return this.GenerateBillCode(tx)
}
// CheckUserBill 检查用户账单
func (this *UserBillDAO) CheckUserBill(tx *dbs.Tx, userId int64, billId int64) error {
if userId <= 0 || billId <= 0 {
return ErrNotFound
}
exists, err := this.Query(tx).
Pk(billId).
Attr("userId", userId).
Exist()
if err != nil {
return err
}
if !exists {
return ErrNotFound
}
return nil
}
// SumUnpaidUserBill 计算某个用户未支付总额
func (this *UserBillDAO) SumUnpaidUserBill(tx *dbs.Tx, userId int64) (float32, error) {
sum, err := this.Query(tx).
Attr("userId", userId).
Attr("isPaid", 0).
Sum("amount", 0)
if err != nil {
return 0, err
}
return float32(sum), nil
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
@@ -103,7 +104,18 @@ func (this *UserDAO) FindUserFullname(tx *dbs.Tx, userId int64) (string, error)
}
// CreateUser 创建用户
func (this *UserDAO) CreateUser(tx *dbs.Tx, username string, password string, fullname string, mobile string, tel string, email string, remark string, source string, clusterId int64) (int64, error) {
func (this *UserDAO) CreateUser(tx *dbs.Tx, username string,
password string,
fullname string,
mobile string,
tel string,
email string,
remark string,
source string,
clusterId int64,
features []string,
registeredIP string,
isVerified bool) (int64, error) {
op := NewUserOperator()
op.Username = username
op.Password = stringutil.Md5(password)
@@ -111,10 +123,24 @@ func (this *UserDAO) CreateUser(tx *dbs.Tx, username string, password string, fu
op.Mobile = mobile
op.Tel = tel
op.Email = email
op.EmailIsVerified = false
op.Remark = remark
op.Source = source
op.ClusterId = clusterId
op.Day = timeutil.Format("Ymd")
op.IsVerified = isVerified
op.RegisteredIP = registeredIP
// features
if len(features) == 0 {
op.Features = "[]"
} else {
featuresJSON, err := json.Marshal(features)
if err != nil {
return 0, err
}
op.Features = featuresJSON
}
op.IsOn = true
op.State = UserStateEnabled
@@ -148,13 +174,15 @@ func (this *UserDAO) UpdateUser(tx *dbs.Tx, userId int64, username string, passw
}
// UpdateUserInfo 修改用户基本信息
func (this *UserDAO) UpdateUserInfo(tx *dbs.Tx, userId int64, fullname string) error {
func (this *UserDAO) UpdateUserInfo(tx *dbs.Tx, userId int64, fullname string, mobile string, email string) error {
if userId <= 0 {
return errors.New("invalid userId")
}
op := NewUserOperator()
op.Id = userId
op.Fullname = fullname
op.Mobile = mobile
op.Email = email
return this.Save(tx, op)
}
@@ -174,7 +202,7 @@ func (this *UserDAO) UpdateUserLogin(tx *dbs.Tx, userId int64, username string,
}
// CountAllEnabledUsers 计算用户数量
func (this *UserDAO) CountAllEnabledUsers(tx *dbs.Tx, clusterId int64, keyword string) (int64, error) {
func (this *UserDAO) CountAllEnabledUsers(tx *dbs.Tx, clusterId int64, keyword string, isVerifying bool) (int64, error) {
query := this.Query(tx)
query.State(UserStateEnabled)
if clusterId > 0 {
@@ -184,11 +212,22 @@ func (this *UserDAO) CountAllEnabledUsers(tx *dbs.Tx, clusterId int64, keyword s
query.Where("(username LIKE :keyword OR fullname LIKE :keyword OR mobile LIKE :keyword OR email LIKE :keyword OR tel LIKE :keyword OR remark LIKE :keyword)").
Param("keyword", "%"+keyword+"%")
}
if isVerifying {
query.Attr("isVerified", 0)
}
return query.Count()
}
// CountAllVerifyingUsers 获取等待审核的用户数
func (this *UserDAO) CountAllVerifyingUsers(tx *dbs.Tx) (int64, error) {
query := this.Query(tx)
query.State(UserStateEnabled)
query.Attr("isVerified", 0)
return query.Count()
}
// ListEnabledUsers 列出单页用户
func (this *UserDAO) ListEnabledUsers(tx *dbs.Tx, clusterId int64, keyword string, offset int64, size int64) (result []*User, err error) {
func (this *UserDAO) ListEnabledUsers(tx *dbs.Tx, clusterId int64, keyword string, isVerifying bool, offset int64, size int64) (result []*User, err error) {
query := this.Query(tx)
query.State(UserStateEnabled)
if clusterId > 0 {
@@ -198,6 +237,9 @@ func (this *UserDAO) ListEnabledUsers(tx *dbs.Tx, clusterId int64, keyword strin
query.Where("(username LIKE :keyword OR fullname LIKE :keyword OR mobile LIKE :keyword OR email LIKE :keyword OR tel LIKE :keyword OR remark LIKE :keyword)").
Param("keyword", "%"+keyword+"%")
}
if isVerifying {
query.Attr("isVerified", 0)
}
_, err = query.
DescPk().
Offset(offset).
@@ -276,7 +318,7 @@ func (this *UserDAO) UpdateUserFeatures(tx *dbs.Tx, userId int64, featuresJSON [
}
// FindUserFeatures 查找用户Features
func (this *UserDAO) FindUserFeatures(tx *dbs.Tx, userId int64) ([]*UserFeature, error) {
func (this *UserDAO) FindUserFeatures(tx *dbs.Tx, userId int64) ([]*userconfigs.UserFeature, error) {
featuresJSON, err := this.Query(tx).
Pk(userId).
Result("features").
@@ -295,12 +337,12 @@ func (this *UserDAO) FindUserFeatures(tx *dbs.Tx, userId int64) ([]*UserFeature,
}
// 检查是否还存在以及设置名称
result := []*UserFeature{}
result := []*userconfigs.UserFeature{}
if len(featureCodes) > 0 {
for _, featureCode := range featureCodes {
f := FindUserFeature(featureCode)
f := userconfigs.FindUserFeature(featureCode)
if f != nil {
result = append(result, &UserFeature{Name: f.Name, Code: f.Code, Description: f.Description})
result = append(result, &userconfigs.UserFeature{Name: f.Name, Code: f.Code, Description: f.Description})
}
}
}
@@ -354,3 +396,16 @@ func (this *UserDAO) CountDailyUsers(tx *dbs.Tx, dayFrom string, dayTo string) (
return result, nil
}
// UpdateUserIsVerified 审核用户
func (this *UserDAO) UpdateUserIsVerified(tx *dbs.Tx, userId int64, isRejected bool, rejectReason string) error {
if userId <= 0 {
return errors.New("invalid userId")
}
var op = NewUserOperator()
op.Id = userId
op.IsRejected = isRejected
op.RejectReason = rejectReason
op.IsVerified = true
return this.Save(tx, op)
}

View File

@@ -1,70 +0,0 @@
package models
import "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
var (
// 所有功能列表,注意千万不能在运行时进行修改
allUserFeatures = []*UserFeature{
{
Name: "记录访问日志",
Code: "server.accessLog",
Description: "用户可以开启服务的访问日志",
},
{
Name: "查看访问日志",
Code: "server.viewAccessLog",
Description: "用户可以查看服务的访问日志",
},
{
Name: "转发访问日志",
Code: "server.accessLog.forward",
Description: "用户可以配置访问日志转发到自定义的API",
},
{
Name: "负载均衡",
Code: "server.tcp",
Description: "用户可以添加TCP/TLS负载均衡服务",
},
{
Name: "自定义负载均衡端口",
Code: "server.tcp.port",
Description: "用户可以自定义TCP端口",
},
{
Name: "开启WAF",
Code: "server.waf",
Description: "用户可以开启WAF功能并可以设置黑白名单等",
},
{
Name: "费用账单",
Code: "finance",
Description: "开启费用账单相关功能",
},
}
)
// 用户功能
type UserFeature struct {
Name string `json:"name"`
Code string `json:"code"`
Description string `json:"description"`
}
func (this *UserFeature) ToPB() *pb.UserFeature {
return &pb.UserFeature{Name: this.Name, Code: this.Code, Description: this.Description}
}
// 所有功能列表
func FindAllUserFeatures() []*UserFeature {
return allUserFeatures
}
// 查询单个功能
func FindUserFeature(code string) *UserFeature {
for _, feature := range allUserFeatures {
if feature.Code == code {
return feature
}
}
return nil
}

View File

@@ -2,43 +2,55 @@ package models
// User 用户
type User struct {
Id uint32 `field:"id"` // ID
IsOn uint8 `field:"isOn"` // 是否启用
Username string `field:"username"` // 用户名
Password string `field:"password"` // 密码
Fullname string `field:"fullname"` // 真实姓名
Mobile string `field:"mobile"` // 手机号
Tel string `field:"tel"` // 联系电话
Remark string `field:"remark"` // 备注
Email string `field:"email"` // 邮箱地址
AvatarFileId uint64 `field:"avatarFileId"` // 头像文件ID
CreatedAt uint64 `field:"createdAt"` // 创建时间
Day string `field:"day"` // YYYYMMDD
UpdatedAt uint64 `field:"updatedAt"` // 修改时间
State uint8 `field:"state"` // 状态
Source string `field:"source"` // 来源
ClusterId uint32 `field:"clusterId"` // 集群ID
Features string `field:"features"` // 允许操作的特征
Id uint32 `field:"id"` // ID
IsOn uint8 `field:"isOn"` // 是否启用
Username string `field:"username"` // 用户名
Password string `field:"password"` // 密码
Fullname string `field:"fullname"` // 真实姓名
Mobile string `field:"mobile"` // 手机号
Tel string `field:"tel"` // 联系电话
Remark string `field:"remark"` // 备注
Email string `field:"email"` // 邮箱地址
EmailIsVerified uint8 `field:"emailIsVerified"` // 邮箱是否已验证
AvatarFileId uint64 `field:"avatarFileId"` // 头像文件ID
CreatedAt uint64 `field:"createdAt"` // 创建时间
Day string `field:"day"` // YYYYMMDD
UpdatedAt uint64 `field:"updatedAt"` // 修改时间
State uint8 `field:"state"` // 状态
Source string `field:"source"` // 来源
ClusterId uint32 `field:"clusterId"` // 集群ID
Features string `field:"features"` // 允许操作的特征
RegisteredIP string `field:"registeredIP"` // 注册使用的IP
IsRejected uint8 `field:"isRejected"` // 是否已拒绝
RejectReason string `field:"rejectReason"` // 拒绝理由
IsVerified uint8 `field:"isVerified"` // 是否验证通过
RequirePlans uint8 `field:"requirePlans"` // 是否需要购买套餐
}
type UserOperator struct {
Id interface{} // ID
IsOn interface{} // 是否启用
Username interface{} // 用户名
Password interface{} // 密码
Fullname interface{} // 真实姓名
Mobile interface{} // 手机号
Tel interface{} // 联系电话
Remark interface{} // 备注
Email interface{} // 邮箱地址
AvatarFileId interface{} // 头像文件ID
CreatedAt interface{} // 创建时间
Day interface{} // YYYYMMDD
UpdatedAt interface{} // 修改时间
State interface{} // 状态
Source interface{} // 来源
ClusterId interface{} // 集群ID
Features interface{} // 允许操作的特征
Id interface{} // ID
IsOn interface{} // 是否启用
Username interface{} // 用户名
Password interface{} // 密码
Fullname interface{} // 真实姓名
Mobile interface{} // 手机号
Tel interface{} // 联系电话
Remark interface{} // 备注
Email interface{} // 邮箱地址
EmailIsVerified interface{} // 邮箱是否已验证
AvatarFileId interface{} // 头像文件ID
CreatedAt interface{} // 创建时间
Day interface{} // YYYYMMDD
UpdatedAt interface{} // 修改时间
State interface{} // 状态
Source interface{} // 来源
ClusterId interface{} // 集群ID
Features interface{} // 允许操作的特征
RegisteredIP interface{} // 注册使用的IP
IsRejected interface{} // 是否已拒绝
RejectReason interface{} // 拒绝理由
IsVerified interface{} // 是否验证通过
RequirePlans interface{} // 是否需要购买套餐
}
func NewUserOperator() *UserOperator {

View File

@@ -86,11 +86,17 @@ func (this *UserPlanDAO) FindEnabledUserPlan(tx *dbs.Tx, userPlanId int64, cache
}
// CountAllEnabledUserPlans 计算套餐数量
func (this *UserPlanDAO) CountAllEnabledUserPlans(tx *dbs.Tx, isAvailable bool, isExpired bool, expiringDays int32) (int64, error) {
func (this *UserPlanDAO) CountAllEnabledUserPlans(tx *dbs.Tx, userId int64, isAvailable bool, isExpired bool, expiringDays int32) (int64, error) {
var query = this.Query(tx).
State(UserPlanStateEnabled).
Where("userId IN (SELECT id FROM " + SharedUserDAO.Table + " WHERE state=1)").
Where("planId IN (SELECT id FROM " + SharedPlanDAO.Table + " WHERE state=1)")
if userId > 0 {
query.Attr("userId", userId)
} else {
query.Where("userId IN (SELECT id FROM " + SharedUserDAO.Table + " WHERE state=1)")
}
var today = timeutil.Format("Y-m-d")
if isAvailable {
query.Gte("dayTo", today)
@@ -110,10 +116,11 @@ func (this *UserPlanDAO) CountAllEnabledUserPlans(tx *dbs.Tx, isAvailable bool,
func (this *UserPlanDAO) ListEnabledUserPlans(tx *dbs.Tx, userId int64, isAvailable bool, isExpired bool, expiringDays int32, offset int64, size int64) (result []*UserPlan, err error) {
var query = this.Query(tx).
State(UserPlanStateEnabled).
Where("userId IN (SELECT id FROM " + SharedUserDAO.Table + " WHERE state=1)").
Where("planId IN (SELECT id FROM " + SharedPlanDAO.Table + " WHERE state=1)")
if userId > 0 {
query.Attr("userId", userId)
} else {
query.Where("userId IN (SELECT id FROM " + SharedUserDAO.Table + " WHERE state=1)")
}
var today = timeutil.Format("Y-m-d")
if isAvailable {
@@ -137,10 +144,11 @@ func (this *UserPlanDAO) ListEnabledUserPlans(tx *dbs.Tx, userId int64, isAvaila
}
// CreateUserPlan 创建套餐
func (this *UserPlanDAO) CreateUserPlan(tx *dbs.Tx, userId int64, planId int64, dayTo string) (int64, error) {
func (this *UserPlanDAO) CreateUserPlan(tx *dbs.Tx, userId int64, planId int64, name string, dayTo string) (int64, error) {
var op = NewUserPlanOperator()
op.UserId = userId
op.PlanId = planId
op.Name = name
op.DayTo = dayTo
op.IsOn = true
op.State = UserStateEnabled
@@ -148,13 +156,14 @@ func (this *UserPlanDAO) CreateUserPlan(tx *dbs.Tx, userId int64, planId int64,
}
// UpdateUserPlan 修改套餐
func (this *UserPlanDAO) UpdateUserPlan(tx *dbs.Tx, userPlanId int64, planId int64, dayTo string, isOn bool) error {
func (this *UserPlanDAO) UpdateUserPlan(tx *dbs.Tx, userPlanId int64, planId int64, name string, dayTo string, isOn bool) error {
if userPlanId <= 0 {
return errors.New("invalid userPlanId")
}
var op = NewUserPlanOperator()
op.Id = userPlanId
op.PlanId = planId
op.Name = name
op.DayTo = dayTo
op.IsOn = isOn
err := this.Save(tx, op)
@@ -198,6 +207,22 @@ func (this *UserPlanDAO) FindAllEnabledPlansForServer(tx *dbs.Tx, userId int64,
return
}
// CheckUserPlan 检查用户套餐
func (this *UserPlanDAO) CheckUserPlan(tx *dbs.Tx, userId int64, userPlanId int64) error {
exists, err := this.Query(tx).
Pk(userPlanId).
Attr("userId", userId).
State(UserPlanStateEnabled).
Exist()
if err != nil {
return err
}
if !exists {
return ErrNotFound
}
return nil
}
// NotifyUpdate 通知更新
func (this *UserPlanDAO) NotifyUpdate(tx *dbs.Tx, userPlanId int64) error {
serverId, err := SharedServerDAO.FindEnabledServerIdWithUserPlanId(tx, userPlanId)

View File

@@ -6,6 +6,7 @@ type UserPlan struct {
UserId uint32 `field:"userId"` // 用户ID
PlanId uint32 `field:"planId"` // 套餐ID
IsOn uint8 `field:"isOn"` // 是否启用
Name string `field:"name"` // 名称
DayTo string `field:"dayTo"` // 结束日期
State uint8 `field:"state"` // 状态
}
@@ -15,6 +16,7 @@ type UserPlanOperator struct {
UserId interface{} // 用户ID
PlanId interface{} // 套餐ID
IsOn interface{} // 是否启用
Name interface{} // 名称
DayTo interface{} // 结束日期
State interface{} // 状态
}

View File

@@ -6,6 +6,7 @@ import (
"bytes"
"crypto/tls"
"encoding/json"
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/cloudflare"
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/dnstypes"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
@@ -257,6 +258,7 @@ func (this *CloudFlareProvider) doAPI(method string, apiPath string, args map[st
if err != nil {
return err
}
req.Header.Set("User-Agent", teaconst.ProductName+"/"+teaconst.Version)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Auth-Key", this.apiKey)
req.Header.Set("x-Auth-Email", this.email)

View File

@@ -165,7 +165,7 @@ func (this *CustomHTTPProvider) post(params maps.Map) (respData []byte, err erro
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
req.Header.Set("Timestamp", timestamp)
req.Header.Set("Token", fmt.Sprintf("%x", sha1.Sum([]byte(this.secret+"@"+timestamp))))
req.Header.Set("User-Agent", "GoEdge/"+teaconst.Version)
req.Header.Set("User-Agent", teaconst.ProductName+"/"+teaconst.Version)
resp, err := customHTTPClient.Do(req)
if err != nil {

View File

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

View File

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

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

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

View File

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

View File

@@ -8,4 +8,5 @@ type Credentials struct {
PrivateKey string
Passphrase string
Method string
Sudo bool
}

View File

@@ -9,19 +9,30 @@ import (
var SharedDeployManager = NewDeployManager()
// DeployManager 节点部署文件管理器
// 如果节点部署文件有变化需要重启API节点以便于生效
type DeployManager struct {
dir string
dir string
nodeFiles []*DeployFile
nsNodeFiles []*DeployFile
}
// NewDeployManager 节点部署文件管理器
// NewDeployManager 获取新节点部署文件管理器
func NewDeployManager() *DeployManager {
return &DeployManager{
var manager = &DeployManager{
dir: Tea.Root + "/deploy",
}
manager.LoadNodeFiles()
manager.LoadNSNodeFiles()
return manager
}
// LoadNodeFiles 加载所有边缘节点文件
func (this *DeployManager) LoadNodeFiles() []*DeployFile {
if len(this.nodeFiles) > 0 {
return this.nodeFiles
}
keyMap := map[string]*DeployFile{} // key => File
reg := regexp.MustCompile(`^edge-node-(\w+)-(\w+)-v([0-9.]+)\.zip$`)
@@ -52,6 +63,9 @@ func (this *DeployManager) LoadNodeFiles() []*DeployFile {
for _, v := range keyMap {
result = append(result, v)
}
this.nodeFiles = result
return result
}
@@ -67,6 +81,10 @@ func (this *DeployManager) FindNodeFile(os string, arch string) *DeployFile {
// LoadNSNodeFiles 加载所有NS节点安装文件
func (this *DeployManager) LoadNSNodeFiles() []*DeployFile {
if len(this.nsNodeFiles) > 0 {
return this.nsNodeFiles
}
keyMap := map[string]*DeployFile{} // key => File
reg := regexp.MustCompile(`^edge-dns-(\w+)-(\w+)-v([0-9.]+)\.zip$`)
@@ -97,6 +115,9 @@ func (this *DeployManager) LoadNSNodeFiles() []*DeployFile {
for _, v := range keyMap {
result = append(result, v)
}
this.nsNodeFiles = result
return result
}

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