Compare commits

..

53 Commits

Author SHA1 Message Date
刘祥超
fe4eb3928e 设置 max_prepared_stmt_count 失败时提示更详细 2021-07-21 09:01:37 +08:00
刘祥超
514d968b20 修复访问日志无法查询IP的Bug 2021-07-21 08:08:31 +08:00
刘祥超
3cce13e671 域名排行增加条数限制 2021-07-21 08:08:16 +08:00
刘祥超
6d359b09f2 更新SQL 2021-07-20 22:24:30 +08:00
刘祥超
316b3485ca API节点列表API允许认证节点调用 2021-07-20 19:03:35 +08:00
刘祥超
31c20122b4 域名服务增加停用 /启用 2021-07-20 19:03:10 +08:00
刘祥超
7343d4bfac 增加API令牌相关API 2021-07-20 17:15:44 +08:00
刘祥超
330d5a3d21 在各个地方支持IPv6 2021-07-20 10:55:34 +08:00
刘祥超
709845064f 自动清理过期指标统计数据 2021-07-19 21:01:26 +08:00
刘祥超
be5a89e513 更新SQL 2021-07-19 20:38:59 +08:00
刘祥超
80328b9b5f 增加内置的统计指标 2021-07-19 20:38:30 +08:00
刘祥超
c6a09e131b Dashboard增加指标图表 2021-07-19 17:58:16 +08:00
刘祥超
f52ea4fa4f 优化通过IP查找日志 2021-07-18 17:09:06 +08:00
刘祥超
6cbda588f7 实现WAF通知和记录IP功能 2021-07-18 15:52:34 +08:00
刘祥超
f9e7c3a2e0 WAF支持更多动作 2021-07-14 22:46:23 +08:00
刘祥超
c370dada11 访问日志增加RemoteAddr字段以加速查询 2021-07-14 22:43:31 +08:00
刘祥超
8d71afc176 搜索节点时加入所在集群状态 2021-07-14 13:59:16 +08:00
刘祥超
a39a114316 IP测试时同时也检查绑定的IP名单 2021-07-13 15:49:16 +08:00
刘祥超
3f5c3568db 更新SQL 2021-07-13 15:10:19 +08:00
刘祥超
f50ff00f0b 路径规则改成路由规则 2021-07-13 14:30:30 +08:00
刘祥超
7caa5bee75 优化看板 2021-07-13 11:04:45 +08:00
刘祥超
bf5368929b 更新SQL 2021-07-12 17:37:44 +08:00
刘祥超
ff6d8d9ba3 实现数据看板--WAF 2021-07-12 16:57:02 +08:00
刘祥超
877d42a271 管理界面可以切换风格 2021-07-12 10:21:28 +08:00
刘祥超
21c51cbdc8 实现数据看板-DNS 2021-07-11 21:44:08 +08:00
刘祥超
45e4eb72ac 实现数据看板--用户 2021-07-11 18:05:57 +08:00
刘祥超
11c344eef4 看板增加缓存目录用量 2021-07-08 19:43:09 +08:00
刘祥超
17008d6b03 健康检查失败时,会自动尝试再次连接 2021-07-08 18:37:11 +08:00
刘祥超
5512efbb70 实现服务看板(企业版) 2021-07-07 19:55:37 +08:00
刘祥超
1e0a7612df 节点列表增加下行流量,节点列表可以按CPU、内存、下行流量排序 2021-07-07 15:18:48 +08:00
刘祥超
8589e0d373 实现节点看板(仅对企业版开放) 2021-07-06 20:06:34 +08:00
刘祥超
c5edaef356 优化API节点监听逻辑,提升兼容性
如果用户填写的GPRC监听端口中的地址无法监听,则尝试只监听端口
2021-07-06 15:19:39 +08:00
刘祥超
692eb04c93 DNS节点检查是否需要升级时,加入集群是否可用的条件 2021-07-06 10:27:14 +08:00
刘祥超
83d9a17c2b 集群看板增加指标相关图表 2021-07-05 16:37:53 +08:00
刘祥超
a4422ef7bd 更新SQL 2021-07-05 11:38:45 +08:00
刘祥超
e6e9fcc3c3 实现集群看板 2021-07-05 11:37:22 +08:00
刘祥超
7484243dff 实现基本的图表管理 2021-07-03 15:45:04 +08:00
刘祥超
907676d688 指标数据增加总和数据 2021-07-01 10:39:42 +08:00
刘祥超
54dbe1f3e4 实现基础的统计指标 2021-06-30 19:59:49 +08:00
刘祥超
98a2d61fd1 SSH认证--私钥认证方式增加用户名选项 2021-06-30 14:56:36 +08:00
刘祥超
e544e088be 统计创建浏览器、系统时限制名称长度 2021-06-29 19:38:14 +08:00
刘祥超
86c33256ca 修改版本为0.2.5 2021-06-28 10:32:06 +08:00
刘祥超
91ff73e4e4 更新用户节点版本 2021-06-27 22:22:11 +08:00
刘祥超
e68473a13f 更新SQL 2021-06-27 22:14:35 +08:00
刘祥超
5774beda6f 阶段性提交 2021-06-27 21:59:37 +08:00
刘祥超
4869c11d60 ip2region增加IP格式检查 2021-06-27 17:29:16 +08:00
刘祥超
4bc6f93902 统计时创建系统、浏览器信息时加锁 2021-06-27 15:17:18 +08:00
刘祥超
ce4e079752 修复WAF用户检查的Bug 2021-06-27 08:31:10 +08:00
刘祥超
488df3d150 服务列表可以搜索端口号 2021-06-25 11:05:02 +08:00
刘祥超
109e129cc5 ACME申请证书时可以设置回调URL 2021-06-24 09:27:39 +08:00
刘祥超
447f89399f 更新SQL 2021-06-23 13:13:26 +08:00
刘祥超
206c12c746 实现公用的IP名单 2021-06-23 13:12:54 +08:00
刘祥超
3c14310e3a 变更版本 2021-06-21 14:43:51 +08:00
176 changed files with 7162 additions and 782 deletions

2
go.mod
View File

@@ -14,7 +14,7 @@ require (
github.com/go-sql-driver/mysql v1.5.0
github.com/go-yaml/yaml v2.1.0+incompatible
github.com/golang/protobuf v1.5.2
github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f
github.com/iwind/TeaGo v0.0.0-20210628135026-38575a4ab060
github.com/lionsoul2014/ip2region v2.2.0-release+incompatible
github.com/mozillazg/go-pinyin v0.18.0
github.com/pkg/sftp v1.12.0

4
go.sum
View File

@@ -184,6 +184,8 @@ github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhK
github.com/iwind/TeaGo v0.0.0-20200923021120-f5d76441fe9e/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f h1:r2O8PONj/KiuZjJHVHn7KlCePUIjNtgAmvLfgRafQ8o=
github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/TeaGo v0.0.0-20210628135026-38575a4ab060 h1:qdLtK4PDXxk2vMKkTWl5Fl9xqYuRCukzWAgJbLHdfOo=
github.com/iwind/TeaGo v0.0.0-20210628135026-38575a4ab060/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
@@ -192,6 +194,8 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=

View File

@@ -39,7 +39,7 @@ func TestRequest_Run_DNS(t *testing.T) {
req := NewRequest(&Task{
User: user,
Type: TaskTypeDNS,
AuthType: AuthTypeDNS,
DNSProvider: dnsProvider,
DNSDomain: "yun4s.cn",
Domains: []string{"yun4s.cn"},
@@ -74,9 +74,9 @@ func TestRequest_Run_HTTP(t *testing.T) {
}
req := NewRequest(&Task{
User: user,
Type: TaskTypeHTTP,
Domains: []string{"teaos.cn", "www.teaos.cn", "meloy.cn"},
User: user,
AuthType: AuthTypeHTTP,
Domains: []string{"teaos.cn", "www.teaos.cn", "meloy.cn"},
})
certData, keyData, err := req.runHTTP()
if err != nil {

View File

@@ -1,7 +1,7 @@
package teaconst
const (
Version = "0.2.3"
Version = "0.2.5"
ProductName = "Edge API"
ProcessName = "edge-api"
@@ -18,9 +18,9 @@ const (
// 其他节点版本号,用来检测是否有需要升级的节点
NodeVersion = "0.2.3"
UserNodeVersion = "0.0.8"
NodeVersion = "0.2.5"
UserNodeVersion = "0.0.10"
AuthorityNodeVersion = "0.0.2"
MonitorNodeVersion = "0.0.2"
DNSNodeVersion = "0.0.1"
DNSNodeVersion = "0.0.2"
)

View File

@@ -1,6 +1,7 @@
package acme
import (
"bytes"
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/acme"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
@@ -8,13 +9,17 @@ import (
dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils"
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"github.com/go-acme/lego/v4/registration"
_ "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/types"
"net/http"
"time"
)
@@ -44,7 +49,7 @@ func init() {
})
}
// 启用条目
// EnableACMETask 启用条目
func (this *ACMETaskDAO) EnableACMETask(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
@@ -53,7 +58,7 @@ func (this *ACMETaskDAO) EnableACMETask(tx *dbs.Tx, id int64) error {
return err
}
// 禁用条目
// DisableACMETask 禁用条目
func (this *ACMETaskDAO) DisableACMETask(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
@@ -62,7 +67,7 @@ func (this *ACMETaskDAO) DisableACMETask(tx *dbs.Tx, id int64) error {
return err
}
// 查找启用中的条目
// FindEnabledACMETask 查找启用中的条目
func (this *ACMETaskDAO) FindEnabledACMETask(tx *dbs.Tx, id int64) (*ACMETask, error) {
result, err := this.Query(tx).
Pk(id).
@@ -74,7 +79,7 @@ func (this *ACMETaskDAO) FindEnabledACMETask(tx *dbs.Tx, id int64) (*ACMETask, e
return result.(*ACMETask), err
}
// 计算某个ACME用户相关的任务数量
// CountACMETasksWithACMEUserId 计算某个ACME用户相关的任务数量
func (this *ACMETaskDAO) CountACMETasksWithACMEUserId(tx *dbs.Tx, acmeUserId int64) (int64, error) {
return this.Query(tx).
State(ACMETaskStateEnabled).
@@ -82,7 +87,7 @@ func (this *ACMETaskDAO) CountACMETasksWithACMEUserId(tx *dbs.Tx, acmeUserId int
Count()
}
// 计算某个DNS服务商相关的任务数量
// CountACMETasksWithDNSProviderId 计算某个DNS服务商相关的任务数量
func (this *ACMETaskDAO) CountACMETasksWithDNSProviderId(tx *dbs.Tx, dnsProviderId int64) (int64, error) {
return this.Query(tx).
State(ACMETaskStateEnabled).
@@ -90,7 +95,7 @@ func (this *ACMETaskDAO) CountACMETasksWithDNSProviderId(tx *dbs.Tx, dnsProvider
Count()
}
// 停止某个证书相关任务
// DisableAllTasksWithCertId 停止某个证书相关任务
func (this *ACMETaskDAO) DisableAllTasksWithCertId(tx *dbs.Tx, certId int64) error {
_, err := this.Query(tx).
Attr("certId", certId).
@@ -99,7 +104,7 @@ func (this *ACMETaskDAO) DisableAllTasksWithCertId(tx *dbs.Tx, certId int64) err
return err
}
// 计算所有任务数量
// CountAllEnabledACMETasks 计算所有任务数量
func (this *ACMETaskDAO) CountAllEnabledACMETasks(tx *dbs.Tx, adminId int64, userId int64, isAvailable bool, isExpired bool, expiringDays int64, keyword string) (int64, error) {
query := dbutils.NewQuery(tx, this, adminId, userId)
if isAvailable || isExpired || expiringDays > 0 {
@@ -130,7 +135,7 @@ func (this *ACMETaskDAO) CountAllEnabledACMETasks(tx *dbs.Tx, adminId int64, use
Count()
}
// 列出单页任务
// ListEnabledACMETasks 列出单页任务
func (this *ACMETaskDAO) ListEnabledACMETasks(tx *dbs.Tx, adminId int64, userId int64, isAvailable bool, isExpired bool, expiringDays int64, keyword string, offset int64, size int64) (result []*ACMETask, err error) {
query := dbutils.NewQuery(tx, this, adminId, userId)
if isAvailable || isExpired || expiringDays > 0 {
@@ -161,8 +166,8 @@ func (this *ACMETaskDAO) ListEnabledACMETasks(tx *dbs.Tx, adminId int64, userId
return
}
// 创建任务
func (this *ACMETaskDAO) CreateACMETask(tx *dbs.Tx, adminId int64, userId int64, authType acme.AuthType, acmeUserId int64, dnsProviderId int64, dnsDomain string, domains []string, autoRenew bool) (int64, error) {
// CreateACMETask 创建任务
func (this *ACMETaskDAO) CreateACMETask(tx *dbs.Tx, adminId int64, userId int64, authType acme.AuthType, acmeUserId int64, dnsProviderId int64, dnsDomain string, domains []string, autoRenew bool, authURL string) (int64, error) {
op := NewACMETaskOperator()
op.AdminId = adminId
op.UserId = userId
@@ -182,6 +187,7 @@ func (this *ACMETaskDAO) CreateACMETask(tx *dbs.Tx, adminId int64, userId int64,
}
op.AutoRenew = autoRenew
op.AuthURL = authURL
op.IsOn = true
op.State = ACMETaskStateEnabled
err := this.Save(tx, op)
@@ -191,8 +197,8 @@ func (this *ACMETaskDAO) CreateACMETask(tx *dbs.Tx, adminId int64, userId int64,
return types.Int64(op.Id), nil
}
// 修改任务
func (this *ACMETaskDAO) UpdateACMETask(tx *dbs.Tx, acmeTaskId int64, acmeUserId int64, dnsProviderId int64, dnsDomain string, domains []string, autoRenew bool) error {
// UpdateACMETask 修改任务
func (this *ACMETaskDAO) UpdateACMETask(tx *dbs.Tx, acmeTaskId int64, acmeUserId int64, dnsProviderId int64, dnsDomain string, domains []string, autoRenew bool, authURL string) error {
if acmeTaskId <= 0 {
return errors.New("invalid acmeTaskId")
}
@@ -214,11 +220,12 @@ func (this *ACMETaskDAO) UpdateACMETask(tx *dbs.Tx, acmeTaskId int64, acmeUserId
}
op.AutoRenew = autoRenew
op.AuthURL = authURL
err := this.Save(tx, op)
return err
}
// 检查权限
// CheckACMETask 检查权限
func (this *ACMETaskDAO) CheckACMETask(tx *dbs.Tx, adminId int64, userId int64, acmeTaskId int64) (bool, error) {
return dbutils.NewQuery(tx, this, adminId, userId).
State(ACMETaskStateEnabled).
@@ -226,7 +233,7 @@ func (this *ACMETaskDAO) CheckACMETask(tx *dbs.Tx, adminId int64, userId int64,
Exist()
}
// 设置任务关联的证书
// UpdateACMETaskCert 设置任务关联的证书
func (this *ACMETaskDAO) UpdateACMETaskCert(tx *dbs.Tx, taskId int64, certId int64) error {
if taskId <= 0 {
return errors.New("invalid taskId")
@@ -239,7 +246,7 @@ func (this *ACMETaskDAO) UpdateACMETaskCert(tx *dbs.Tx, taskId int64, certId int
return err
}
// 执行任务并记录日志
// RunTask 执行任务并记录日志
func (this *ACMETaskDAO) RunTask(tx *dbs.Tx, taskId int64) (isOk bool, errMsg string, resultCertId int64) {
isOk, errMsg, resultCertId = this.runTaskWithoutLog(tx, taskId)
@@ -350,7 +357,33 @@ func (this *ACMETaskDAO) runTaskWithoutLog(tx *dbs.Tx, taskId int64) (isOk bool,
acmeRequest.OnAuth(func(domain, token, keyAuth string) {
err := SharedACMEAuthenticationDAO.CreateAuth(tx, taskId, domain, token, keyAuth)
if err != nil {
logs.Println("[ACME]write authentication to database error: " + err.Error())
remotelogs.Error("ACME", "write authentication to database error: "+err.Error())
} else {
// 调用校验URL
if len(task.AuthURL) > 0 {
authJSON, err := json.Marshal(maps.Map{
"domain": domain,
"token": token,
"key": keyAuth,
})
if err != nil {
remotelogs.Error("ACME", "encode auth data failed: '"+task.AuthURL+"'")
} else {
client := utils.SharedHttpClient(5 * time.Second)
req, err := http.NewRequest(http.MethodPost, task.AuthURL, bytes.NewReader(authJSON))
req.Header.Set("Content-Type", "application/json")
if err != nil {
remotelogs.Error("ACME", "parse auth url failed '"+task.AuthURL+"': "+err.Error())
} else {
resp, err := client.Do(req)
if err != nil {
remotelogs.Error("ACME", "call auth url failed '"+task.AuthURL+"': "+err.Error())
} else {
_ = resp.Body.Close()
}
}
}
}
}
})
certData, keyData, err := acmeRequest.Run()

View File

@@ -1,6 +1,6 @@
package acme
// ACME任务
// ACMETask ACME任务
type ACMETask struct {
Id uint64 `field:"id"` // ID
AdminId uint32 `field:"adminId"` // 管理员ID
@@ -15,6 +15,7 @@ type ACMETask struct {
CertId uint64 `field:"certId"` // 生成的证书ID
AutoRenew uint8 `field:"autoRenew"` // 是否自动更新
AuthType string `field:"authType"` // 认证类型
AuthURL string `field:"authURL"` // 认证URL
}
type ACMETaskOperator struct {
@@ -31,6 +32,7 @@ type ACMETaskOperator struct {
CertId interface{} // 生成的证书ID
AutoRenew interface{} // 是否自动更新
AuthType interface{} // 认证类型
AuthURL interface{} // 认证URL
}
func NewACMETaskOperator() *ACMETaskOperator {

View File

@@ -35,7 +35,7 @@ func init() {
})
}
// 启用条目
// EnableAdmin 启用条目
func (this *AdminDAO) EnableAdmin(tx *dbs.Tx, id int64) (rowsAffected int64, err error) {
return this.Query(tx).
Pk(id).
@@ -43,7 +43,7 @@ func (this *AdminDAO) EnableAdmin(tx *dbs.Tx, id int64) (rowsAffected int64, err
Update()
}
// 禁用条目
// DisableAdmin 禁用条目
func (this *AdminDAO) DisableAdmin(tx *dbs.Tx, id int64) (rowsAffected int64, err error) {
return this.Query(tx).
Pk(id).
@@ -51,7 +51,7 @@ func (this *AdminDAO) DisableAdmin(tx *dbs.Tx, id int64) (rowsAffected int64, er
Update()
}
// 查找启用中的条目
// FindEnabledAdmin 查找启用中的条目
func (this *AdminDAO) FindEnabledAdmin(tx *dbs.Tx, id int64) (*Admin, error) {
result, err := this.Query(tx).
Pk(id).
@@ -63,7 +63,7 @@ func (this *AdminDAO) FindEnabledAdmin(tx *dbs.Tx, id int64) (*Admin, error) {
return result.(*Admin), err
}
// 检查管理员是否存在
// ExistEnabledAdmin 检查管理员是否存在
func (this *AdminDAO) ExistEnabledAdmin(tx *dbs.Tx, adminId int64) (bool, error) {
return this.Query(tx).
Pk(adminId).
@@ -71,7 +71,7 @@ func (this *AdminDAO) ExistEnabledAdmin(tx *dbs.Tx, adminId int64) (bool, error)
Exist()
}
// 获取管理员名称
// FindAdminFullname 获取管理员名称
func (this *AdminDAO) FindAdminFullname(tx *dbs.Tx, adminId int64) (string, error) {
return this.Query(tx).
Pk(adminId).
@@ -79,7 +79,7 @@ func (this *AdminDAO) FindAdminFullname(tx *dbs.Tx, adminId int64) (string, erro
FindStringCol("")
}
// 检查用户名、密码
// CheckAdminPassword 检查用户名、密码
func (this *AdminDAO) CheckAdminPassword(tx *dbs.Tx, username string, encryptedPassword string) (int64, error) {
if len(username) == 0 || len(encryptedPassword) == 0 {
return 0, nil
@@ -94,7 +94,7 @@ func (this *AdminDAO) CheckAdminPassword(tx *dbs.Tx, username string, encryptedP
FindInt64Col(0)
}
// 根据用户名查询管理员ID
// FindAdminIdWithUsername 根据用户名查询管理员ID
func (this *AdminDAO) FindAdminIdWithUsername(tx *dbs.Tx, username string) (int64, error) {
one, err := this.Query(tx).
Attr("username", username).
@@ -110,7 +110,7 @@ func (this *AdminDAO) FindAdminIdWithUsername(tx *dbs.Tx, username string) (int6
return int64(one.(*Admin).Id), nil
}
// 更改管理员密码
// UpdateAdminPassword 更改管理员密码
func (this *AdminDAO) UpdateAdminPassword(tx *dbs.Tx, adminId int64, password string) error {
if adminId <= 0 {
return errors.New("invalid adminId")
@@ -122,7 +122,7 @@ func (this *AdminDAO) UpdateAdminPassword(tx *dbs.Tx, adminId int64, password st
return err
}
// 创建管理员
// CreateAdmin 创建管理员
func (this *AdminDAO) CreateAdmin(tx *dbs.Tx, username string, canLogin bool, password string, fullname string, isSuper bool, modulesJSON []byte) (int64, error) {
op := NewAdminOperator()
op.IsOn = true
@@ -144,7 +144,7 @@ func (this *AdminDAO) CreateAdmin(tx *dbs.Tx, username string, canLogin bool, pa
return types.Int64(op.Id), nil
}
// 修改管理员个人资料
// UpdateAdminInfo 修改管理员个人资料
func (this *AdminDAO) UpdateAdminInfo(tx *dbs.Tx, adminId int64, fullname string) error {
if adminId <= 0 {
return errors.New("invalid adminId")
@@ -156,7 +156,7 @@ func (this *AdminDAO) UpdateAdminInfo(tx *dbs.Tx, adminId int64, fullname string
return err
}
// 修改管理员详细信息
// UpdateAdmin 修改管理员详细信息
func (this *AdminDAO) UpdateAdmin(tx *dbs.Tx, adminId int64, username string, canLogin bool, password string, fullname string, isSuper bool, modulesJSON []byte, isOn bool) error {
if adminId <= 0 {
return errors.New("invalid adminId")
@@ -180,7 +180,7 @@ func (this *AdminDAO) UpdateAdmin(tx *dbs.Tx, adminId int64, username string, ca
return err
}
// 检查用户名是否存在
// CheckAdminUsername 检查用户名是否存在
func (this *AdminDAO) CheckAdminUsername(tx *dbs.Tx, adminId int64, username string) (bool, error) {
query := this.Query(tx).
State(AdminStateEnabled).
@@ -193,7 +193,7 @@ func (this *AdminDAO) CheckAdminUsername(tx *dbs.Tx, adminId int64, username str
return query.Exist()
}
// 修改管理员登录信息
// UpdateAdminLogin 修改管理员登录信息
func (this *AdminDAO) UpdateAdminLogin(tx *dbs.Tx, adminId int64, username string, password string) error {
if adminId <= 0 {
return errors.New("invalid adminId")
@@ -208,7 +208,7 @@ func (this *AdminDAO) UpdateAdminLogin(tx *dbs.Tx, adminId int64, username strin
return err
}
// 修改管理员可以管理的模块
// UpdateAdminModules 修改管理员可以管理的模块
func (this *AdminDAO) UpdateAdminModules(tx *dbs.Tx, adminId int64, allowModulesJSON []byte) error {
if adminId <= 0 {
return errors.New("invalid adminId")
@@ -223,25 +223,25 @@ func (this *AdminDAO) UpdateAdminModules(tx *dbs.Tx, adminId int64, allowModules
return nil
}
// 查询所有管理的权限
// FindAllAdminModules 查询所有管理的权限
func (this *AdminDAO) FindAllAdminModules(tx *dbs.Tx) (result []*Admin, err error) {
_, err = this.Query(tx).
State(AdminStateEnabled).
Attr("isOn", true).
Result("id", "modules", "isSuper", "fullname").
Result("id", "modules", "isSuper", "fullname", "theme").
Slice(&result).
FindAll()
return
}
// 计算所有管理员数量
// CountAllEnabledAdmins 计算所有管理员数量
func (this *AdminDAO) CountAllEnabledAdmins(tx *dbs.Tx) (int64, error) {
return this.Query(tx).
State(AdminStateEnabled).
Count()
}
// 列出单页的管理员
// ListEnabledAdmins 列出单页的管理员
func (this *AdminDAO) ListEnabledAdmins(tx *dbs.Tx, offset int64, size int64) (result []*Admin, err error) {
_, err = this.Query(tx).
State(AdminStateEnabled).
@@ -253,3 +253,11 @@ func (this *AdminDAO) ListEnabledAdmins(tx *dbs.Tx, offset int64, size int64) (r
FindAll()
return
}
// UpdateAdminTheme 设置管理员Theme
func (this *AdminDAO) UpdateAdminTheme(tx *dbs.Tx, adminId int64, theme string) error {
return this.Query(tx).
Pk(adminId).
Set("theme", theme).
UpdateQuickly()
}

View File

@@ -1,6 +1,6 @@
package models
// 管理员
// Admin 管理员
type Admin struct {
Id uint32 `field:"id"` // ID
IsOn uint8 `field:"isOn"` // 是否启用
@@ -13,6 +13,7 @@ type Admin struct {
State uint8 `field:"state"` // 状态
Modules string `field:"modules"` // 允许的模块
CanLogin uint8 `field:"canLogin"` // 是否可以登录
Theme string `field:"theme"` // 模板设置
}
type AdminOperator struct {
@@ -27,6 +28,7 @@ type AdminOperator struct {
State interface{} // 状态
Modules interface{} // 允许的模块
CanLogin interface{} // 是否可以登录
Theme interface{} // 模板设置
}
func NewAdminOperator() *AdminOperator {

View File

@@ -120,3 +120,13 @@ func (this *ApiTokenDAO) CreateAPIToken(tx *dbs.Tx, nodeId string, secret string
err := this.Save(tx, op)
return err
}
// FindAllEnabledAPITokens 读取API令牌
func (this *ApiTokenDAO) FindAllEnabledAPITokens(tx *dbs.Tx, role string) (result []*ApiToken, err error) {
_, err = this.Query(tx).
Attr("role", role).
State(ApiTokenStateEnabled).
Slice(&result).
FindAll()
return
}

View File

@@ -35,7 +35,7 @@ func init() {
})
}
// 启用条目
// EnableClientBrowser 启用条目
func (this *ClientBrowserDAO) EnableClientBrowser(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
@@ -44,7 +44,7 @@ func (this *ClientBrowserDAO) EnableClientBrowser(tx *dbs.Tx, id uint32) error {
return err
}
// 禁用条目
// DisableClientBrowser 禁用条目
func (this *ClientBrowserDAO) DisableClientBrowser(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
@@ -53,7 +53,7 @@ func (this *ClientBrowserDAO) DisableClientBrowser(tx *dbs.Tx, id uint32) error
return err
}
// 查找启用中的条目
// FindEnabledClientBrowser 查找启用中的条目
func (this *ClientBrowserDAO) FindEnabledClientBrowser(tx *dbs.Tx, id int64) (*ClientBrowser, error) {
result, err := this.Query(tx).
Pk(id).
@@ -65,7 +65,7 @@ func (this *ClientBrowserDAO) FindEnabledClientBrowser(tx *dbs.Tx, id int64) (*C
return result.(*ClientBrowser), err
}
// 根据主键查找名称
// FindClientBrowserName 根据主键查找名称
func (this *ClientBrowserDAO) FindClientBrowserName(tx *dbs.Tx, id uint32) (string, error) {
return this.Query(tx).
Pk(id).
@@ -73,7 +73,7 @@ func (this *ClientBrowserDAO) FindClientBrowserName(tx *dbs.Tx, id uint32) (stri
FindStringCol("")
}
// 根据浏览器名称查找浏览器ID
// FindBrowserIdWithNameCacheable 根据浏览器名称查找浏览器ID
func (this *ClientBrowserDAO) FindBrowserIdWithNameCacheable(tx *dbs.Tx, browserName string) (int64, error) {
SharedCacheLocker.RLock()
browserId, ok := clientBrowserNameAndIdCacheMap[browserName]
@@ -102,8 +102,28 @@ func (this *ClientBrowserDAO) FindBrowserIdWithNameCacheable(tx *dbs.Tx, browser
return browserId, nil
}
// 创建浏览器
// CreateBrowser 创建浏览器
func (this *ClientBrowserDAO) CreateBrowser(tx *dbs.Tx, browserName string) (int64, error) {
var maxlength = 50
if len(browserName) > maxlength {
browserName = browserName[:50]
}
SharedCacheLocker.Lock()
defer SharedCacheLocker.Unlock()
// 检查是否已经创建
browserId, err := this.Query(tx).
Attr("name", browserName).
ResultPk().
FindInt64Col(0)
if err != nil {
return 0, err
}
if browserId > 0 {
return browserId, nil
}
op := NewClientBrowserOperator()
op.Name = browserName
codes := []string{browserName}

View File

@@ -35,7 +35,7 @@ func init() {
})
}
// 启用条目
// EnableClientSystem 启用条目
func (this *ClientSystemDAO) EnableClientSystem(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
@@ -44,7 +44,7 @@ func (this *ClientSystemDAO) EnableClientSystem(tx *dbs.Tx, id uint32) error {
return err
}
// 禁用条目
// DisableClientSystem 禁用条目
func (this *ClientSystemDAO) DisableClientSystem(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
@@ -53,7 +53,7 @@ func (this *ClientSystemDAO) DisableClientSystem(tx *dbs.Tx, id uint32) error {
return err
}
// 查找启用中的条目
// FindEnabledClientSystem 查找启用中的条目
func (this *ClientSystemDAO) FindEnabledClientSystem(tx *dbs.Tx, id int64) (*ClientSystem, error) {
result, err := this.Query(tx).
Pk(id).
@@ -65,7 +65,7 @@ func (this *ClientSystemDAO) FindEnabledClientSystem(tx *dbs.Tx, id int64) (*Cli
return result.(*ClientSystem), err
}
// 根据主键查找名称
// FindClientSystemName 根据主键查找名称
func (this *ClientSystemDAO) FindClientSystemName(tx *dbs.Tx, id uint32) (string, error) {
return this.Query(tx).
Pk(id).
@@ -73,7 +73,7 @@ func (this *ClientSystemDAO) FindClientSystemName(tx *dbs.Tx, id uint32) (string
FindStringCol("")
}
// 根据操作系统名称查找系统ID
// FindSystemIdWithNameCacheable 根据操作系统名称查找系统ID
func (this *ClientSystemDAO) FindSystemIdWithNameCacheable(tx *dbs.Tx, systemName string) (int64, error) {
SharedCacheLocker.RLock()
systemId, ok := clientSystemNameAndIdCacheMap[systemName]
@@ -102,8 +102,28 @@ func (this *ClientSystemDAO) FindSystemIdWithNameCacheable(tx *dbs.Tx, systemNam
return systemId, nil
}
// 创建浏览器
// CreateSystem 创建浏览器
func (this *ClientSystemDAO) CreateSystem(tx *dbs.Tx, systemName string) (int64, error) {
var maxlength = 50
if len(systemName) > maxlength {
systemName = systemName[:50]
}
SharedCacheLocker.Lock()
defer SharedCacheLocker.Unlock()
// 检查是否已经创建
systemId, err := this.Query(tx).
Attr("name", systemName).
ResultPk().
FindInt64Col(0)
if err != nil {
return 0, err
}
if systemId > 0 {
return systemId, nil
}
op := NewClientSystemOperator()
op.Name = systemName

View File

@@ -39,7 +39,7 @@ func init() {
})
}
// 启用条目
// EnableDBNode 启用条目
func (this *DBNodeDAO) EnableDBNode(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
@@ -48,7 +48,7 @@ func (this *DBNodeDAO) EnableDBNode(tx *dbs.Tx, id int64) error {
return err
}
// 禁用条目
// DisableDBNode 禁用条目
func (this *DBNodeDAO) DisableDBNode(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
@@ -57,7 +57,7 @@ func (this *DBNodeDAO) DisableDBNode(tx *dbs.Tx, id int64) error {
return err
}
// 查找启用中的条目
// FindEnabledDBNode 查找启用中的条目
func (this *DBNodeDAO) FindEnabledDBNode(tx *dbs.Tx, id int64) (*DBNode, error) {
result, err := this.Query(tx).
Pk(id).
@@ -71,7 +71,7 @@ func (this *DBNodeDAO) FindEnabledDBNode(tx *dbs.Tx, id int64) (*DBNode, error)
return node, nil
}
// 根据主键查找名称
// FindDBNodeName 根据主键查找名称
func (this *DBNodeDAO) FindDBNodeName(tx *dbs.Tx, id int64) (string, error) {
return this.Query(tx).
Pk(id).
@@ -79,14 +79,14 @@ func (this *DBNodeDAO) FindDBNodeName(tx *dbs.Tx, id int64) (string, error) {
FindStringCol("")
}
// 计算可用的节点数量
// CountAllEnabledNodes 计算可用的节点数量
func (this *DBNodeDAO) CountAllEnabledNodes(tx *dbs.Tx) (int64, error) {
return this.Query(tx).
State(DBNodeStateEnabled).
Count()
}
// 获取单页的节点
// ListEnabledNodes 获取单页的节点
func (this *DBNodeDAO) ListEnabledNodes(tx *dbs.Tx, offset int64, size int64) (result []*DBNode, err error) {
_, err = this.Query(tx).
State(DBNodeStateEnabled).
@@ -101,7 +101,7 @@ func (this *DBNodeDAO) ListEnabledNodes(tx *dbs.Tx, offset int64, size int64) (r
return
}
// 创建节点
// CreateDBNode 创建节点
func (this *DBNodeDAO) CreateDBNode(tx *dbs.Tx, isOn bool, name string, description string, host string, port int32, database string, username string, password string, charset string) (int64, error) {
op := NewDBNodeOperator()
op.State = NodeStateEnabled
@@ -121,7 +121,7 @@ func (this *DBNodeDAO) CreateDBNode(tx *dbs.Tx, isOn bool, name string, descript
return types.Int64(op.Id), nil
}
// 修改节点
// UpdateNode 修改节点
func (this *DBNodeDAO) UpdateNode(tx *dbs.Tx, nodeId int64, isOn bool, name string, description string, host string, port int32, database string, username string, password string, charset string) error {
if nodeId <= 0 {
return errors.New("invalid nodeId")
@@ -141,7 +141,7 @@ func (this *DBNodeDAO) UpdateNode(tx *dbs.Tx, nodeId int64, isOn bool, name stri
return err
}
// 查找所有可用的数据库节点
// FindAllEnabledAndOnDBNodes 查找所有可用的数据库节点
func (this *DBNodeDAO) FindAllEnabledAndOnDBNodes(tx *dbs.Tx) (result []*DBNode, err error) {
_, err = this.Query(tx).
State(DBNodeStateEnabled).
@@ -155,7 +155,7 @@ func (this *DBNodeDAO) FindAllEnabledAndOnDBNodes(tx *dbs.Tx) (result []*DBNode,
return
}
// 加密密码
// EncodePassword 加密密码
func (this *DBNodeDAO) EncodePassword(password string) string {
if strings.HasPrefix(password, DBNodePasswordEncodedPrefix) {
return password
@@ -164,7 +164,7 @@ func (this *DBNodeDAO) EncodePassword(password string) string {
return DBNodePasswordEncodedPrefix + encodedString
}
// 解密密码
// DecodePassword 解密密码
func (this *DBNodeDAO) DecodePassword(password string) string {
if !strings.HasPrefix(password, DBNodePasswordEncodedPrefix) {
return password

View File

@@ -19,9 +19,15 @@ import (
var accessLogDBMapping = map[int64]*dbs.DB{} // dbNodeId => DB
var accessLogLocker = &sync.RWMutex{}
type httpAccessLogDefinition struct {
Name string
HasRemoteAddr bool
Exists bool
}
// HTTP服务访问
var httpAccessLogDAOMapping = map[int64]*HTTPAccessLogDAOWrapper{} // dbNodeId => DAO
var httpAccessLogTableMapping = map[string]bool{} // tableName_crc(dsn) => true
var httpAccessLogDAOMapping = map[int64]*HTTPAccessLogDAOWrapper{} // dbNodeId => DAO
var httpAccessLogTableMapping = map[string]*httpAccessLogDefinition{} // tableName_crc(dsn) => true
// DNS服务访问
var nsAccessLogDAOMapping = map[int64]*NSAccessLogDAOWrapper{} // dbNodeId => DAO
@@ -76,7 +82,7 @@ func randomNSAccessLogDAO() (dao *NSAccessLogDAOWrapper) {
}
// 检查表格是否存在
func findHTTPAccessLogTableName(db *dbs.DB, day string) (tableName string, ok bool, err error) {
func findHTTPAccessLogTableName(db *dbs.DB, day string) (tableName string, hasRemoteAddr bool, ok bool, err error) {
if !regexp.MustCompile(`^\d{8}$`).MatchString(day) {
err = errors.New("invalid day '" + day + "', should be YYYYMMDD")
return
@@ -84,25 +90,25 @@ func findHTTPAccessLogTableName(db *dbs.DB, day string) (tableName string, ok bo
config, err := db.Config()
if err != nil {
return "", false, err
return "", false, false, err
}
tableName = "edgeHTTPAccessLogs_" + day
cacheKey := tableName + "_" + fmt.Sprintf("%d", crc32.ChecksumIEEE([]byte(config.Dsn)))
accessLogLocker.RLock()
_, ok = httpAccessLogTableMapping[cacheKey]
def, ok := httpAccessLogTableMapping[cacheKey]
accessLogLocker.RUnlock()
if ok {
return tableName, true, nil
return tableName, def.HasRemoteAddr, true, nil
}
tableNames, err := db.TableNames()
def, err = findHTTPAccessLogTable(db, day, false)
if err != nil {
return tableName, false, err
return tableName, false, false, err
}
return tableName, lists.ContainsString(tableNames, tableName), nil
return tableName, def.HasRemoteAddr, def.Exists, nil
}
func findNSAccessLogTableName(db *dbs.DB, day string) (tableName string, ok bool, err error) {
@@ -135,10 +141,10 @@ func findNSAccessLogTableName(db *dbs.DB, day string) (tableName string, ok bool
}
// 根据日期获取表名
func findHTTPAccessLogTable(db *dbs.DB, day string, force bool) (string, error) {
func findHTTPAccessLogTable(db *dbs.DB, day string, force bool) (*httpAccessLogDefinition, error) {
config, err := db.Config()
if err != nil {
return "", err
return nil, err
}
tableName := "edgeHTTPAccessLogs_" + day
@@ -146,36 +152,55 @@ func findHTTPAccessLogTable(db *dbs.DB, day string, force bool) (string, error)
if !force {
accessLogLocker.RLock()
_, ok := httpAccessLogTableMapping[cacheKey]
definition, ok := httpAccessLogTableMapping[cacheKey]
accessLogLocker.RUnlock()
if ok {
return tableName, nil
return definition, nil
}
}
tableNames, err := db.TableNames()
if err != nil {
return tableName, err
return nil, err
}
if lists.ContainsString(tableNames, tableName) {
table, err := db.FindTable(tableName)
if err != nil {
return nil, err
}
accessLogLocker.Lock()
httpAccessLogTableMapping[cacheKey] = true
var definition = &httpAccessLogDefinition{
Name: tableName,
HasRemoteAddr: table.FindFieldWithName("remoteAddr") != nil,
Exists: true,
}
httpAccessLogTableMapping[cacheKey] = definition
accessLogLocker.Unlock()
return tableName, nil
return definition, nil
}
if !force {
return &httpAccessLogDefinition{Name: tableName, HasRemoteAddr: true, Exists: false}, nil
}
// 创建表格
_, 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 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) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='访问日志';")
_, err = db.Exec("CREATE TABLE `" + tableName + "` (`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',`serverId` int(11) unsigned DEFAULT '0' COMMENT '服务ID',`nodeId` int(11) unsigned DEFAULT '0' COMMENT '节点ID',`status` int(3) unsigned DEFAULT '0' COMMENT '状态码',`createdAt` bigint(11) unsigned DEFAULT '0' COMMENT '创建时间',`content` json DEFAULT NULL COMMENT '日志内容',`requestId` varchar(128) DEFAULT NULL COMMENT '请求ID',`firewallPolicyId` int(11) unsigned DEFAULT '0' COMMENT 'WAF策略ID',`firewallRuleGroupId` int(11) unsigned DEFAULT '0' COMMENT 'WAF分组ID',`firewallRuleSetId` int(11) unsigned DEFAULT '0' COMMENT 'WAF集ID',`firewallRuleId` int(11) unsigned DEFAULT '0' COMMENT 'WAF规则ID',`remoteAddr` varchar(64) DEFAULT NULL COMMENT 'IP地址',PRIMARY KEY (`id`),KEY `serverId` (`serverId`),KEY `nodeId` (`nodeId`),KEY `serverId_status` (`serverId`,`status`),KEY `requestId` (`requestId`),KEY `firewallPolicyId` (`firewallPolicyId`),KEY `firewallRuleGroupId` (`firewallRuleGroupId`),KEY `firewallRuleSetId` (`firewallRuleSetId`), KEY `firewallRuleId` (`firewallRuleId`), KEY `remoteAddr` (`remoteAddr`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='访问日志';")
if err != nil {
return tableName, err
return nil, err
}
accessLogLocker.Lock()
httpAccessLogTableMapping[cacheKey] = true
var definition = &httpAccessLogDefinition{
Name: tableName,
HasRemoteAddr: true,
Exists: true,
}
httpAccessLogTableMapping[cacheKey] = definition
accessLogLocker.Unlock()
return tableName, nil
return definition, nil
}
func findNSAccessLogTable(db *dbs.DB, day string, force bool) (string, error) {
@@ -315,7 +340,7 @@ func (this *DBNodeInitializer) loop() error {
// 检查表是否存在
// httpAccessLog
{
tableName, err := findHTTPAccessLogTable(db, timeutil.Format("Ymd"), false)
tableDef, err := findHTTPAccessLogTable(db, timeutil.Format("Ymd"), true)
if err != nil {
if !strings.Contains(err.Error(), "1050") { // 非表格已存在错误
logs.Println("[DB_NODE]create first table in database node failed: " + err.Error())
@@ -335,7 +360,7 @@ func (this *DBNodeInitializer) loop() error {
daoObject := dbs.DAOObject{
Instance: db,
DB: node.Name + "(id:" + strconv.Itoa(int(node.Id)) + ")",
Table: tableName,
Table: tableDef.Name,
PkName: "id",
Model: new(HTTPAccessLog),
}
@@ -357,7 +382,6 @@ func (this *DBNodeInitializer) loop() error {
accessLogLocker.Unlock()
}
// nsAccessLog
{
tableName, err := findNSAccessLogTable(db, timeutil.Format("Ymd"), false)

View File

@@ -12,6 +12,7 @@ import (
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"net"
"net/http"
"regexp"
"sort"
@@ -69,7 +70,7 @@ func (this *HTTPAccessLogDAO) CreateHTTPAccessLogsWithDAO(tx *dbs.Tx, daoWrapper
for _, accessLog := range accessLogs {
day := timeutil.Format("Ymd", time.Unix(accessLog.Timestamp, 0))
table, err := findHTTPAccessLogTable(dao.Instance, day, false)
tableDef, err := findHTTPAccessLogTable(dao.Instance, day, false)
if err != nil {
return err
}
@@ -85,6 +86,11 @@ func (this *HTTPAccessLogDAO) CreateHTTPAccessLogsWithDAO(tx *dbs.Tx, daoWrapper
fields["firewallRuleSetId"] = accessLog.FirewallRuleSetId
fields["firewallRuleId"] = accessLog.FirewallRuleId
// TODO 根据集群、服务设置获取IP
if tableDef.HasRemoteAddr {
fields["remoteAddr"] = accessLog.RawRemoteAddr
}
content, err := json.Marshal(accessLog)
if err != nil {
return err
@@ -92,23 +98,25 @@ func (this *HTTPAccessLogDAO) CreateHTTPAccessLogsWithDAO(tx *dbs.Tx, daoWrapper
fields["content"] = content
_, err = dao.Query(tx).
Table(table).
Table(tableDef.Name).
Sets(fields).
Insert()
if err != nil {
// 是否为 Error 1146: Table 'xxx.xxx' doesn't exist 如果是,则创建表之后重试
if strings.Contains(err.Error(), "1146") {
table, err = findHTTPAccessLogTable(dao.Instance, day, true)
tableDef, err = findHTTPAccessLogTable(dao.Instance, day, true)
if err != nil {
return err
}
_, err = dao.Query(tx).
Table(table).
Table(tableDef.Name).
Sets(fields).
Insert()
if err != nil {
return err
}
} else {
logs.Println("HTTP_ACCESS_LOG", err.Error())
}
}
}
@@ -179,7 +187,7 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx, lastRequestId string, s
dao := daoWrapper.DAO
tableName, exists, err := findHTTPAccessLogTableName(dao.Instance, day)
tableName, hasRemoteAddr, exists, err := findHTTPAccessLogTableName(dao.Instance, day)
if !exists {
// 表格不存在则跳过
return
@@ -216,41 +224,60 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx, lastRequestId string, s
// keyword
if len(keyword) > 0 {
useOriginKeyword := false
// remoteAddr
if hasRemoteAddr && net.ParseIP(keyword) != nil {
query.Attr("remoteAddr", keyword)
} else if hasRemoteAddr && regexp.MustCompile(`^ip:.+`).MatchString(keyword) {
keyword = keyword[3:]
pieces := strings.SplitN(keyword, ",", 2)
if len(pieces) == 1 || len(pieces[1]) == 0 {
query.Attr("remoteAddr", pieces[0])
} else {
query.Between("remoteAddr", pieces[0], pieces[1])
}
} else {
useOriginKeyword := false
where := "JSON_EXTRACT(content, '$.remoteAddr') LIKE :keyword OR JSON_EXTRACT(content, '$.requestURI') LIKE :keyword OR JSON_EXTRACT(content, '$.host') LIKE :keyword"
where := "JSON_EXTRACT(content, '$.remoteAddr') LIKE :keyword OR JSON_EXTRACT(content, '$.requestURI') LIKE :keyword OR JSON_EXTRACT(content, '$.host') LIKE :keyword"
// 请求方法
if keyword == http.MethodGet ||
keyword == http.MethodPost ||
keyword == http.MethodHead ||
keyword == http.MethodConnect ||
keyword == http.MethodPut ||
keyword == http.MethodTrace ||
keyword == http.MethodOptions ||
keyword == http.MethodDelete ||
keyword == http.MethodPatch {
where += " OR JSON_EXTRACT(content, '$.requestMethod')=:originKeyword"
useOriginKeyword = true
}
jsonKeyword, err := json.Marshal(keyword)
if err == nil {
where += " OR JSON_CONTAINS(content, :jsonKeyword, '$.tags')"
query.Param("jsonKeyword", jsonKeyword)
}
// 响应状态码
if regexp.MustCompile(`^\d{3}$`).MatchString(keyword) {
where += " OR JSON_EXTRACT(content, '$.status')=:intKeyword"
query.Param("intKeyword", types.Int(keyword))
}
// 请求方法
if keyword == http.MethodGet ||
keyword == http.MethodPost ||
keyword == http.MethodHead ||
keyword == http.MethodConnect ||
keyword == http.MethodPut ||
keyword == http.MethodTrace ||
keyword == http.MethodOptions ||
keyword == http.MethodDelete ||
keyword == http.MethodPatch {
where += " OR JSON_EXTRACT(content, '$.requestMethod')=:originKeyword"
useOriginKeyword = true
}
if regexp.MustCompile(`^\d{3}-\d{3}$`).MatchString(keyword) {
pieces := strings.Split(keyword, "-")
where += " OR JSON_EXTRACT(content, '$.status') BETWEEN :intKeyword1 AND :intKeyword2"
query.Param("intKeyword1", types.Int(pieces[0]))
query.Param("intKeyword2", types.Int(pieces[1]))
}
// 响应状态码
if regexp.MustCompile(`^\d{3}$`).MatchString(keyword) {
where += " OR JSON_EXTRACT(content, '$.status')=:intKeyword"
query.Param("intKeyword", types.Int(keyword))
}
query.Where("("+where+")").
Param("keyword", "%"+keyword+"%")
if useOriginKeyword {
query.Param("originKeyword", keyword)
if regexp.MustCompile(`^\d{3}-\d{3}$`).MatchString(keyword) {
pieces := strings.Split(keyword, "-")
where += " OR JSON_EXTRACT(content, '$.status') BETWEEN :intKeyword1 AND :intKeyword2"
query.Param("intKeyword1", types.Int(pieces[0]))
query.Param("intKeyword2", types.Int(pieces[1]))
}
query.Where("("+where+")").
Param("keyword", "%"+keyword+"%")
if useOriginKeyword {
query.Param("originKeyword", keyword)
}
}
}
@@ -350,7 +377,7 @@ func (this *HTTPAccessLogDAO) FindAccessLogWithRequestId(tx *dbs.Tx, requestId s
dao := daoWrapper.DAO
tableName, exists, err := findHTTPAccessLogTableName(dao.Instance, day)
tableName, _, exists, err := findHTTPAccessLogTableName(dao.Instance, day)
if err != nil {
logs.Println("[DB_NODE]" + err.Error())
return

View File

@@ -1,6 +1,6 @@
package models
//
// HTTPAccessLog 访问日志
type HTTPAccessLog struct {
Id uint64 `field:"id"` // ID
ServerId uint32 `field:"serverId"` // 服务ID
@@ -13,6 +13,7 @@ type HTTPAccessLog struct {
FirewallRuleGroupId uint32 `field:"firewallRuleGroupId"` // WAF分组ID
FirewallRuleSetId uint32 `field:"firewallRuleSetId"` // WAF集ID
FirewallRuleId uint32 `field:"firewallRuleId"` // WAF规则ID
RemoteAddr string `field:"remoteAddr"` // IP地址
}
type HTTPAccessLogOperator struct {
@@ -27,6 +28,7 @@ type HTTPAccessLogOperator struct {
FirewallRuleGroupId interface{} // WAF分组ID
FirewallRuleSetId interface{} // WAF集ID
FirewallRuleId interface{} // WAF规则ID
RemoteAddr interface{} // IP地址
}
func NewHTTPAccessLogOperator() *HTTPAccessLogOperator {

View File

@@ -320,7 +320,21 @@ func (this *HTTPFirewallPolicyDAO) CheckUserFirewallPolicy(tx *dbs.Tx, userId in
return nil
}
// TODO 检查是否为用户Server所使用
// 检查是否为用户Server所使用
webIds, err := SharedHTTPWebDAO.FindAllWebIdsWithHTTPFirewallPolicyId(tx, firewallPolicyId)
if err != nil {
return err
}
for _, webId := range webIds {
err := SharedHTTPWebDAO.CheckUserWeb(tx, userId, webId)
if err != nil {
if err != ErrNotFound {
return err
}
} else {
return nil
}
}
return ErrNotFound
}
@@ -330,7 +344,7 @@ func (this *HTTPFirewallPolicyDAO) FindEnabledFirewallPolicyIdsWithIPListId(tx *
ones, err := this.Query(tx).
ResultPk().
State(HTTPFirewallPolicyStateEnabled).
Where("(JSON_CONTAINS(inbound, :listQuery, '$.whiteListRef') OR JSON_CONTAINS(inbound, :listQuery, '$.blackListRef') )").
Where("(JSON_CONTAINS(inbound, :listQuery, '$.whiteListRef') OR JSON_CONTAINS(inbound, :listQuery, '$.blackListRef') OR JSON_CONTAINS(inbound, :listQuery, '$.publicWhiteListRefs') OR JSON_CONTAINS(inbound, :listQuery, '$.publicBlackListRefs'))").
Param("listQuery", maps.Map{"isOn": true, "listId": ipListId}.AsJSON()).
FindAll()
if err != nil {

View File

@@ -37,12 +37,12 @@ func init() {
})
}
// 初始化
// Init 初始化
func (this *HTTPFirewallRuleGroupDAO) Init() {
_ = this.DAOObject.Init()
}
// 启用条目
// EnableHTTPFirewallRuleGroup 启用条目
func (this *HTTPFirewallRuleGroupDAO) EnableHTTPFirewallRuleGroup(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
@@ -51,7 +51,7 @@ func (this *HTTPFirewallRuleGroupDAO) EnableHTTPFirewallRuleGroup(tx *dbs.Tx, id
return err
}
// 禁用条目
// DisableHTTPFirewallRuleGroup 禁用条目
func (this *HTTPFirewallRuleGroupDAO) DisableHTTPFirewallRuleGroup(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
@@ -60,7 +60,7 @@ func (this *HTTPFirewallRuleGroupDAO) DisableHTTPFirewallRuleGroup(tx *dbs.Tx, i
return err
}
// 查找启用中的条目
// FindEnabledHTTPFirewallRuleGroup 查找启用中的条目
func (this *HTTPFirewallRuleGroupDAO) FindEnabledHTTPFirewallRuleGroup(tx *dbs.Tx, id int64) (*HTTPFirewallRuleGroup, error) {
result, err := this.Query(tx).
Pk(id).
@@ -72,7 +72,7 @@ func (this *HTTPFirewallRuleGroupDAO) FindEnabledHTTPFirewallRuleGroup(tx *dbs.T
return result.(*HTTPFirewallRuleGroup), err
}
// 根据主键查找名称
// FindHTTPFirewallRuleGroupName 根据主键查找名称
func (this *HTTPFirewallRuleGroupDAO) FindHTTPFirewallRuleGroupName(tx *dbs.Tx, id int64) (string, error) {
return this.Query(tx).
Pk(id).
@@ -80,7 +80,7 @@ func (this *HTTPFirewallRuleGroupDAO) FindHTTPFirewallRuleGroupName(tx *dbs.Tx,
FindStringCol("")
}
// 组合配置
// ComposeFirewallRuleGroup 组合配置
func (this *HTTPFirewallRuleGroupDAO) ComposeFirewallRuleGroup(tx *dbs.Tx, groupId int64) (*firewallconfigs.HTTPFirewallRuleGroup, error) {
group, err := this.FindEnabledHTTPFirewallRuleGroup(tx, groupId)
if err != nil {
@@ -117,7 +117,7 @@ func (this *HTTPFirewallRuleGroupDAO) ComposeFirewallRuleGroup(tx *dbs.Tx, group
return config, nil
}
// 从配置中创建分组
// CreateGroupFromConfig 从配置中创建分组
func (this *HTTPFirewallRuleGroupDAO) CreateGroupFromConfig(tx *dbs.Tx, groupConfig *firewallconfigs.HTTPFirewallRuleGroup) (int64, error) {
op := NewHTTPFirewallRuleGroupOperator()
op.IsOn = groupConfig.IsOn
@@ -150,7 +150,7 @@ func (this *HTTPFirewallRuleGroupDAO) CreateGroupFromConfig(tx *dbs.Tx, groupCon
return types.Int64(op.Id), nil
}
// 修改开启状态
// UpdateGroupIsOn 修改开启状态
func (this *HTTPFirewallRuleGroupDAO) UpdateGroupIsOn(tx *dbs.Tx, groupId int64, isOn bool) error {
_, err := this.Query(tx).
Pk(groupId).
@@ -162,7 +162,7 @@ func (this *HTTPFirewallRuleGroupDAO) UpdateGroupIsOn(tx *dbs.Tx, groupId int64,
return this.NotifyUpdate(tx, groupId)
}
// 创建分组
// CreateGroup 创建分组
func (this *HTTPFirewallRuleGroupDAO) CreateGroup(tx *dbs.Tx, isOn bool, name string, description string) (int64, error) {
op := NewHTTPFirewallRuleGroupOperator()
op.State = HTTPFirewallRuleStateEnabled
@@ -176,7 +176,7 @@ func (this *HTTPFirewallRuleGroupDAO) CreateGroup(tx *dbs.Tx, isOn bool, name st
return types.Int64(op.Id), nil
}
// 修改分组
// UpdateGroup 修改分组
func (this *HTTPFirewallRuleGroupDAO) UpdateGroup(tx *dbs.Tx, groupId int64, isOn bool, name string, description string) error {
if groupId <= 0 {
return errors.New("invalid groupId")
@@ -193,7 +193,7 @@ func (this *HTTPFirewallRuleGroupDAO) UpdateGroup(tx *dbs.Tx, groupId int64, isO
return this.NotifyUpdate(tx, groupId)
}
// 修改分组中的规则集
// UpdateGroupSets 修改分组中的规则集
func (this *HTTPFirewallRuleGroupDAO) UpdateGroupSets(tx *dbs.Tx, groupId int64, setsJSON []byte) error {
if groupId <= 0 {
return errors.New("invalid groupId")
@@ -208,7 +208,7 @@ func (this *HTTPFirewallRuleGroupDAO) UpdateGroupSets(tx *dbs.Tx, groupId int64,
return this.NotifyUpdate(tx, groupId)
}
// 根据规则集查找规则分组
// FindRuleGroupIdWithRuleSetId 根据规则集查找规则分组
func (this *HTTPFirewallRuleGroupDAO) FindRuleGroupIdWithRuleSetId(tx *dbs.Tx, setId int64) (int64, error) {
return this.Query(tx).
State(HTTPFirewallRuleStateEnabled).
@@ -218,7 +218,7 @@ func (this *HTTPFirewallRuleGroupDAO) FindRuleGroupIdWithRuleSetId(tx *dbs.Tx, s
FindInt64Col(0)
}
// 检查用户所属分组
// CheckUserRuleGroup 检查用户所属分组
func (this *HTTPFirewallRuleGroupDAO) CheckUserRuleGroup(tx *dbs.Tx, userId int64, groupId int64) error {
policyId, err := SharedHTTPFirewallPolicyDAO.FindEnabledFirewallPolicyIdWithRuleGroupId(tx, groupId)
if err != nil {
@@ -230,7 +230,7 @@ func (this *HTTPFirewallRuleGroupDAO) CheckUserRuleGroup(tx *dbs.Tx, userId int6
return SharedHTTPFirewallPolicyDAO.CheckUserFirewallPolicy(tx, userId, policyId)
}
// 通知更新
// NotifyUpdate 通知更新
func (this *HTTPFirewallRuleGroupDAO) NotifyUpdate(tx *dbs.Tx, groupId int64) error {
policyId, err := SharedHTTPFirewallPolicyDAO.FindEnabledFirewallPolicyIdWithRuleGroupId(tx, groupId)
if err != nil {

View File

@@ -37,12 +37,12 @@ func init() {
})
}
// 初始化
// Init 初始化
func (this *HTTPFirewallRuleSetDAO) Init() {
_ = this.DAOObject.Init()
}
// 启用条目
// EnableHTTPFirewallRuleSet 启用条目
func (this *HTTPFirewallRuleSetDAO) EnableHTTPFirewallRuleSet(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
@@ -51,7 +51,7 @@ func (this *HTTPFirewallRuleSetDAO) EnableHTTPFirewallRuleSet(tx *dbs.Tx, id int
return err
}
// 禁用条目
// DisableHTTPFirewallRuleSet 禁用条目
func (this *HTTPFirewallRuleSetDAO) DisableHTTPFirewallRuleSet(tx *dbs.Tx, ruleSetId int64) error {
_, err := this.Query(tx).
Pk(ruleSetId).
@@ -63,7 +63,7 @@ func (this *HTTPFirewallRuleSetDAO) DisableHTTPFirewallRuleSet(tx *dbs.Tx, ruleS
return this.NotifyUpdate(tx, ruleSetId)
}
// 查找启用中的条目
// FindEnabledHTTPFirewallRuleSet 查找启用中的条目
func (this *HTTPFirewallRuleSetDAO) FindEnabledHTTPFirewallRuleSet(tx *dbs.Tx, id int64) (*HTTPFirewallRuleSet, error) {
result, err := this.Query(tx).
Pk(id).
@@ -75,7 +75,7 @@ func (this *HTTPFirewallRuleSetDAO) FindEnabledHTTPFirewallRuleSet(tx *dbs.Tx, i
return result.(*HTTPFirewallRuleSet), err
}
// 根据主键查找名称
// FindHTTPFirewallRuleSetName 根据主键查找名称
func (this *HTTPFirewallRuleSetDAO) FindHTTPFirewallRuleSetName(tx *dbs.Tx, id int64) (string, error) {
return this.Query(tx).
Pk(id).
@@ -83,7 +83,7 @@ func (this *HTTPFirewallRuleSetDAO) FindHTTPFirewallRuleSetName(tx *dbs.Tx, id i
FindStringCol("")
}
// 组合配置
// ComposeFirewallRuleSet 组合配置
func (this *HTTPFirewallRuleSetDAO) ComposeFirewallRuleSet(tx *dbs.Tx, setId int64) (*firewallconfigs.HTTPFirewallRuleSet, error) {
set, err := this.FindEnabledHTTPFirewallRuleSet(tx, setId)
if err != nil {
@@ -118,20 +118,19 @@ func (this *HTTPFirewallRuleSetDAO) ComposeFirewallRuleSet(tx *dbs.Tx, setId int
}
}
config.Action = set.Action
if IsNotNull(set.ActionOptions) {
options := maps.Map{}
err = json.Unmarshal([]byte(set.ActionOptions), &options)
var actionConfigs = []*firewallconfigs.HTTPFirewallActionConfig{}
if len(set.Actions) > 0 {
err = json.Unmarshal([]byte(set.Actions), &actionConfigs)
if err != nil {
return nil, err
}
config.ActionOptions = options
config.Actions = actionConfigs
}
return config, nil
}
// 从配置中创建规则集
// CreateOrUpdateSetFromConfig 从配置中创建规则集
func (this *HTTPFirewallRuleSetDAO) CreateOrUpdateSetFromConfig(tx *dbs.Tx, setConfig *firewallconfigs.HTTPFirewallRuleSet) (int64, error) {
op := NewHTTPFirewallRuleSetOperator()
op.State = HTTPFirewallRuleSetStateEnabled
@@ -140,19 +139,19 @@ func (this *HTTPFirewallRuleSetDAO) CreateOrUpdateSetFromConfig(tx *dbs.Tx, setC
op.Name = setConfig.Name
op.Description = setConfig.Description
op.Connector = setConfig.Connector
op.Action = setConfig.Action
op.Code = setConfig.Code
if setConfig.ActionOptions != nil {
actionOptionsJSON, err := json.Marshal(setConfig.ActionOptions)
if len(setConfig.Actions) == 0 {
op.Actions = "[]"
} else {
actionsJSON, err := json.Marshal(setConfig.Actions)
if err != nil {
return 0, err
}
op.ActionOptions = actionOptionsJSON
} else {
op.ActionOptions = "{}"
op.Actions = actionsJSON
}
op.Code = setConfig.Code
// rules
ruleRefs := []*firewallconfigs.HTTPFirewallRuleRef{}
for _, ruleConfig := range setConfig.Rules {
@@ -186,7 +185,7 @@ func (this *HTTPFirewallRuleSetDAO) CreateOrUpdateSetFromConfig(tx *dbs.Tx, setC
return types.Int64(op.Id), nil
}
// 设置是否启用
// UpdateRuleSetIsOn 设置是否启用
func (this *HTTPFirewallRuleSetDAO) UpdateRuleSetIsOn(tx *dbs.Tx, ruleSetId int64, isOn bool) error {
if ruleSetId <= 0 {
return errors.New("invalid ruleSetId")
@@ -201,7 +200,7 @@ func (this *HTTPFirewallRuleSetDAO) UpdateRuleSetIsOn(tx *dbs.Tx, ruleSetId int6
return this.NotifyUpdate(tx, ruleSetId)
}
// 根据规则查找规则集
// FindEnabledRuleSetIdWithRuleId 根据规则查找规则集
func (this *HTTPFirewallRuleSetDAO) FindEnabledRuleSetIdWithRuleId(tx *dbs.Tx, ruleId int64) (int64, error) {
return this.Query(tx).
State(HTTPFirewallRuleStateEnabled).
@@ -211,7 +210,7 @@ func (this *HTTPFirewallRuleSetDAO) FindEnabledRuleSetIdWithRuleId(tx *dbs.Tx, r
FindInt64Col(0)
}
// 检查用户
// CheckUserRuleSet 检查用户
func (this *HTTPFirewallRuleSetDAO) CheckUserRuleSet(tx *dbs.Tx, userId int64, setId int64) error {
groupId, err := SharedHTTPFirewallRuleGroupDAO.FindRuleGroupIdWithRuleSetId(tx, setId)
if err != nil {
@@ -223,7 +222,7 @@ func (this *HTTPFirewallRuleSetDAO) CheckUserRuleSet(tx *dbs.Tx, userId int64, s
return SharedHTTPFirewallRuleGroupDAO.CheckUserRuleGroup(tx, userId, groupId)
}
// 通知更新
// NotifyUpdate 通知更新
func (this *HTTPFirewallRuleSetDAO) NotifyUpdate(tx *dbs.Tx, setId int64) error {
groupId, err := SharedHTTPFirewallRuleGroupDAO.FindRuleGroupIdWithRuleSetId(tx, setId)
if err != nil {

View File

@@ -1,6 +1,6 @@
package models
// 防火墙规则集
// HTTPFirewallRuleSet 防火墙规则集
type HTTPFirewallRuleSet struct {
Id uint32 `field:"id"` // ID
IsOn uint8 `field:"isOn"` // 是否启用
@@ -13,8 +13,9 @@ type HTTPFirewallRuleSet struct {
State uint8 `field:"state"` // 状态
AdminId uint32 `field:"adminId"` // 管理员ID
UserId uint32 `field:"userId"` // 用户ID
Action string `field:"action"` // 执行的动作
ActionOptions string `field:"actionOptions"` // 动作的选项
Action string `field:"action"` // 执行的动作(过期)
ActionOptions string `field:"actionOptions"` // 动作的选项(过期)
Actions string `field:"actions"` // 一组动作
}
type HTTPFirewallRuleSetOperator struct {
@@ -29,8 +30,9 @@ type HTTPFirewallRuleSetOperator struct {
State interface{} // 状态
AdminId interface{} // 管理员ID
UserId interface{} // 用户ID
Action interface{} // 执行的动作
ActionOptions interface{} // 动作的选项
Action interface{} // 执行的动作(过期)
ActionOptions interface{} // 动作的选项(过期)
Actions interface{} // 一组动作
}
func NewHTTPFirewallRuleSetOperator() *HTTPFirewallRuleSetOperator {

View File

@@ -83,7 +83,7 @@ func (this *HTTPLocationDAO) FindHTTPLocationName(tx *dbs.Tx, id int64) (string,
FindStringCol("")
}
// CreateLocation 创建路规则
// CreateLocation 创建路规则
func (this *HTTPLocationDAO) CreateLocation(tx *dbs.Tx, parentId int64, name string, pattern string, description string, isBreak bool, condsJSON []byte) (int64, error) {
op := NewHTTPLocationOperator()
op.IsOn = true
@@ -105,7 +105,7 @@ func (this *HTTPLocationDAO) CreateLocation(tx *dbs.Tx, parentId int64, name str
return types.Int64(op.Id), nil
}
// UpdateLocation 修改路规则
// UpdateLocation 修改路规则
func (this *HTTPLocationDAO) UpdateLocation(tx *dbs.Tx, locationId int64, name string, pattern string, description string, isOn bool, isBreak bool, condsJSON []byte) error {
if locationId <= 0 {
return errors.New("invalid locationId")

View File

@@ -1,6 +1,6 @@
package models
// 路径规则配置
// HTTPLocation 路由规则配置
type HTTPLocation struct {
Id uint32 `field:"id"` // ID
TemplateId uint32 `field:"templateId"` // 模版ID

View File

@@ -247,7 +247,7 @@ func (this *HTTPWebDAO) ComposeWebConfig(tx *dbs.Tx, webId int64) (*serverconfig
}
}
// 路规则
// 路规则
if IsNotNull(web.Locations) {
refs := []*serverconfigs.HTTPLocationRef{}
err = json.Unmarshal([]byte(web.Locations), &refs)
@@ -563,7 +563,7 @@ func (this *HTTPWebDAO) UpdateWebFirewall(tx *dbs.Tx, webId int64, firewallJSON
return this.NotifyUpdate(tx, webId)
}
// UpdateWebLocations 更改路规则配置
// UpdateWebLocations 更改路规则配置
func (this *HTTPWebDAO) UpdateWebLocations(tx *dbs.Tx, webId int64, locationsJSON []byte) error {
if webId <= 0 {
return errors.New("invalid webId")

View File

@@ -23,7 +23,7 @@ type HTTPWeb struct {
Gzip string `field:"gzip"` // Gzip配置
Cache string `field:"cache"` // 缓存配置
Firewall string `field:"firewall"` // 防火墙设置
Locations string `field:"locations"` // 路规则配置
Locations string `field:"locations"` // 路规则配置
Websocket string `field:"websocket"` // Websocket设置
RewriteRules string `field:"rewriteRules"` // 重写规则配置
HostRedirects string `field:"hostRedirects"` // 域名跳转
@@ -53,7 +53,7 @@ type HTTPWebOperator struct {
Gzip interface{} // Gzip配置
Cache interface{} // 缓存配置
Firewall interface{} // 防火墙设置
Locations interface{} // 路规则配置
Locations interface{} // 路规则配置
Websocket interface{} // Websocket设置
RewriteRules interface{} // 重写规则配置
HostRedirects interface{} // 域名跳转

View File

@@ -46,7 +46,7 @@ func init() {
})
}
// 启用条目
// EnableIPItem 启用条目
func (this *IPItemDAO) EnableIPItem(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
@@ -55,7 +55,7 @@ func (this *IPItemDAO) EnableIPItem(tx *dbs.Tx, id int64) error {
return err
}
// 禁用条目
// DisableIPItem 禁用条目
func (this *IPItemDAO) DisableIPItem(tx *dbs.Tx, id int64) error {
version, err := SharedIPListDAO.IncreaseVersion(tx)
if err != nil {
@@ -74,7 +74,7 @@ func (this *IPItemDAO) DisableIPItem(tx *dbs.Tx, id int64) error {
return this.NotifyUpdate(tx, id)
}
// 查找启用中的条目
// FindEnabledIPItem 查找启用中的条目
func (this *IPItemDAO) FindEnabledIPItem(tx *dbs.Tx, id int64) (*IPItem, error) {
result, err := this.Query(tx).
Pk(id).
@@ -86,7 +86,17 @@ func (this *IPItemDAO) FindEnabledIPItem(tx *dbs.Tx, id int64) (*IPItem, error)
return result.(*IPItem), err
}
// 创建IP
// DisableOldIPItem 根据IP删除以前的旧记录
func (this *IPItemDAO) DisableOldIPItem(tx *dbs.Tx, listId int64, ipFrom string, ipTo string) error {
return this.Query(tx).
Attr("listId", listId).
Attr("ipFrom", ipFrom).
Attr("ipTo", ipTo).
Set("state", IPItemStateDisabled).
UpdateQuickly()
}
// CreateIPItem 创建IP
func (this *IPItemDAO) CreateIPItem(tx *dbs.Tx, listId int64, ipFrom string, ipTo string, expiredAt int64, reason string, itemType IPItemType, eventLevel string) (int64, error) {
version, err := SharedIPListDAO.IncreaseVersion(tx)
if err != nil {
@@ -121,7 +131,7 @@ func (this *IPItemDAO) CreateIPItem(tx *dbs.Tx, listId int64, ipFrom string, ipT
return itemId, nil
}
// 修改IP
// UpdateIPItem 修改IP
func (this *IPItemDAO) UpdateIPItem(tx *dbs.Tx, itemId int64, ipFrom string, ipTo string, expiredAt int64, reason string, itemType IPItemType, eventLevel string) error {
if itemId <= 0 {
return errors.New("invalid itemId")
@@ -165,7 +175,7 @@ func (this *IPItemDAO) UpdateIPItem(tx *dbs.Tx, itemId int64, ipFrom string, ipT
return this.NotifyUpdate(tx, itemId)
}
// 计算IP数量
// CountIPItemsWithListId 计算IP数量
func (this *IPItemDAO) CountIPItemsWithListId(tx *dbs.Tx, listId int64) (int64, error) {
return this.Query(tx).
State(IPItemStateEnabled).
@@ -173,7 +183,7 @@ func (this *IPItemDAO) CountIPItemsWithListId(tx *dbs.Tx, listId int64) (int64,
Count()
}
// 查找IP列表
// ListIPItemsWithListId 查找IP列表
func (this *IPItemDAO) ListIPItemsWithListId(tx *dbs.Tx, listId int64, offset int64, size int64) (result []*IPItem, err error) {
_, err = this.Query(tx).
State(IPItemStateEnabled).
@@ -186,7 +196,7 @@ func (this *IPItemDAO) ListIPItemsWithListId(tx *dbs.Tx, listId int64, offset in
return
}
// 根据版本号查找IP列表
// ListIPItemsAfterVersion 根据版本号查找IP列表
func (this *IPItemDAO) ListIPItemsAfterVersion(tx *dbs.Tx, version int64, size int64) (result []*IPItem, err error) {
_, err = this.Query(tx).
// 这里不要设置状态参数,因为我们要知道哪些是删除的
@@ -200,7 +210,7 @@ func (this *IPItemDAO) ListIPItemsAfterVersion(tx *dbs.Tx, version int64, size i
return
}
// 查找IPItem对应的列表ID
// FindItemListId 查找IPItem对应的列表ID
func (this *IPItemDAO) FindItemListId(tx *dbs.Tx, itemId int64) (int64, error) {
return this.Query(tx).
Pk(itemId).
@@ -208,7 +218,7 @@ func (this *IPItemDAO) FindItemListId(tx *dbs.Tx, itemId int64) (int64, error) {
FindInt64Col(0)
}
// 查找包含某个IP的Item
// FindEnabledItemContainsIP 查找包含某个IP的Item
func (this *IPItemDAO) FindEnabledItemContainsIP(tx *dbs.Tx, listId int64, ip uint64) (*IPItem, error) {
query := this.Query(tx).
Attr("listId", listId).
@@ -229,7 +239,15 @@ func (this *IPItemDAO) FindEnabledItemContainsIP(tx *dbs.Tx, listId int64, ip ui
return one.(*IPItem), nil
}
// 通知更新
// ExistsEnabledItem 检查IP是否存在
func (this *IPItemDAO) ExistsEnabledItem(tx *dbs.Tx, itemId int64) (bool, error) {
return this.Query(tx).
Pk(itemId).
State(IPItemStateEnabled).
Exist()
}
// NotifyUpdate 通知更新
func (this *IPItemDAO) NotifyUpdate(tx *dbs.Tx, itemId int64) error {
// 获取ListId
listId, err := this.FindItemListId(tx, itemId)

View File

@@ -38,7 +38,7 @@ func init() {
})
}
// 启用条目
// EnableIPList 启用条目
func (this *IPListDAO) EnableIPList(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
@@ -47,7 +47,7 @@ func (this *IPListDAO) EnableIPList(tx *dbs.Tx, id int64) error {
return err
}
// 禁用条目
// DisableIPList 禁用条目
func (this *IPListDAO) DisableIPList(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
@@ -56,7 +56,7 @@ func (this *IPListDAO) DisableIPList(tx *dbs.Tx, id int64) error {
return err
}
// 查找启用中的条目
// FindEnabledIPList 查找启用中的条目
func (this *IPListDAO) FindEnabledIPList(tx *dbs.Tx, id int64) (*IPList, error) {
result, err := this.Query(tx).
Pk(id).
@@ -68,7 +68,7 @@ func (this *IPListDAO) FindEnabledIPList(tx *dbs.Tx, id int64) (*IPList, error)
return result.(*IPList), err
}
// 根据主键查找名称
// FindIPListName 根据主键查找名称
func (this *IPListDAO) FindIPListName(tx *dbs.Tx, id int64) (string, error) {
return this.Query(tx).
Pk(id).
@@ -76,7 +76,7 @@ func (this *IPListDAO) FindIPListName(tx *dbs.Tx, id int64) (string, error) {
FindStringCol("")
}
// 获取名单类型
// FindIPListTypeCacheable 获取名单类型
func (this *IPListDAO) FindIPListTypeCacheable(tx *dbs.Tx, listId int64) (string, error) {
// 检查缓存
SharedCacheLocker.RLock()
@@ -106,8 +106,8 @@ func (this *IPListDAO) FindIPListTypeCacheable(tx *dbs.Tx, listId int64) (string
return listType, nil
}
// 创建名单
func (this *IPListDAO) CreateIPList(tx *dbs.Tx, userId int64, listType ipconfigs.IPListType, name string, code string, timeoutJSON []byte) (int64, error) {
// CreateIPList 创建名单
func (this *IPListDAO) CreateIPList(tx *dbs.Tx, userId int64, listType ipconfigs.IPListType, name string, code string, timeoutJSON []byte, description string, isPublic bool) (int64, error) {
op := NewIPListOperator()
op.IsOn = true
op.UserId = userId
@@ -118,6 +118,8 @@ func (this *IPListDAO) CreateIPList(tx *dbs.Tx, userId int64, listType ipconfigs
if len(timeoutJSON) > 0 {
op.Timeout = timeoutJSON
}
op.Description = description
op.IsPublic = isPublic
err := this.Save(tx, op)
if err != nil {
return 0, err
@@ -125,8 +127,8 @@ func (this *IPListDAO) CreateIPList(tx *dbs.Tx, userId int64, listType ipconfigs
return types.Int64(op.Id), nil
}
// 修改名单
func (this *IPListDAO) UpdateIPList(tx *dbs.Tx, listId int64, name string, code string, timeoutJSON []byte) error {
// UpdateIPList 修改名单
func (this *IPListDAO) UpdateIPList(tx *dbs.Tx, listId int64, name string, code string, timeoutJSON []byte, description string) error {
if listId <= 0 {
return errors.New("invalid listId")
}
@@ -139,16 +141,17 @@ func (this *IPListDAO) UpdateIPList(tx *dbs.Tx, listId int64, name string, code
} else {
op.Timeout = "null"
}
op.Description = description
err := this.Save(tx, op)
return err
}
// 增加版本
// IncreaseVersion 增加版本
func (this *IPListDAO) IncreaseVersion(tx *dbs.Tx) (int64, error) {
return SharedSysLockerDAO.Increase(tx, "IP_LIST_VERSION", 1000000)
}
// 检查用户权限
// CheckUserIPList 检查用户权限
func (this *IPListDAO) CheckUserIPList(tx *dbs.Tx, userId int64, listId int64) error {
ok, err := this.Query(tx).
Pk(listId).
@@ -163,7 +166,49 @@ func (this *IPListDAO) CheckUserIPList(tx *dbs.Tx, userId int64, listId int64) e
return ErrNotFound
}
// 通知更新
// CountAllEnabledIPLists 计算名单数量
func (this *IPListDAO) CountAllEnabledIPLists(tx *dbs.Tx, listType string, isPublic bool, keyword string) (int64, error) {
var query = this.Query(tx).
State(IPListStateEnabled).
Attr("type", listType).
Attr("isPublic", isPublic)
if len(keyword) > 0 {
query.Where("(name LIKE :keyword OR description LIKE :keyword)").
Param("keyword", "%"+keyword+"%")
}
return query.Count()
}
// ListEnabledIPLists 列出单页名单
func (this *IPListDAO) ListEnabledIPLists(tx *dbs.Tx, listType string, isPublic bool, keyword string, offset int64, size int64) (result []*IPList, err error) {
var query = this.Query(tx).
State(IPListStateEnabled).
Attr("type", listType).
Attr("isPublic", isPublic)
if len(keyword) > 0 {
query.Where("(name LIKE :keyword OR description LIKE :keyword)").
Param("keyword", "%"+keyword+"%")
}
_, err = query.Offset(offset).
Limit(size).
DescPk().
Slice(&result).
FindAll()
return
}
// ExistsEnabledIPList 检查IP名单是否存在
func (this *IPListDAO) ExistsEnabledIPList(tx *dbs.Tx, listId int64) (bool, error) {
if listId <= 0 {
return false, nil
}
return this.Query(tx).
Pk(listId).
State(IPListStateEnabled).
Exist()
}
// NotifyUpdate 通知更新
func (this *IPListDAO) NotifyUpdate(tx *dbs.Tx, listId int64, taskType NodeTaskType) error {
httpFirewallPolicyIds, err := SharedHTTPFirewallPolicyDAO.FindEnabledFirewallPolicyIdsWithIPListId(tx, listId)
if err != nil {

View File

@@ -1,32 +1,36 @@
package models
// IP名单
// IPList IP名单
type IPList struct {
Id uint32 `field:"id"` // ID
IsOn uint8 `field:"isOn"` // 是否启用
Type string `field:"type"` // 类型
AdminId uint32 `field:"adminId"` // 用户ID
UserId uint32 `field:"userId"` // 用户ID
Name string `field:"name"` // 列表名
Code string `field:"code"` // 代号
State uint8 `field:"state"` // 状态
CreatedAt uint64 `field:"createdAt"` // 创建时间
Timeout string `field:"timeout"` // 默认超时时间
Actions string `field:"actions"` // IP触发的动作
Id uint32 `field:"id"` // ID
IsOn uint8 `field:"isOn"` // 是否启用
Type string `field:"type"` // 类型
AdminId uint32 `field:"adminId"` // 用户ID
UserId uint32 `field:"userId"` // 用户ID
Name string `field:"name"` // 列表名
Code string `field:"code"` // 代号
State uint8 `field:"state"` // 状态
CreatedAt uint64 `field:"createdAt"` // 创建时间
Timeout string `field:"timeout"` // 默认超时时间
Actions string `field:"actions"` // IP触发的动作
Description string `field:"description"` // 描述
IsPublic uint8 `field:"isPublic"` // 是否公用
}
type IPListOperator struct {
Id interface{} // ID
IsOn interface{} // 是否启用
Type interface{} // 类型
AdminId interface{} // 用户ID
UserId interface{} // 用户ID
Name interface{} // 列表名
Code interface{} // 代号
State interface{} // 状态
CreatedAt interface{} // 创建时间
Timeout interface{} // 默认超时时间
Actions interface{} // IP触发的动作
Id interface{} // ID
IsOn interface{} // 是否启用
Type interface{} // 类型
AdminId interface{} // 用户ID
UserId interface{} // 用户ID
Name interface{} // 列表名
Code interface{} // 代号
State interface{} // 状态
CreatedAt interface{} // 创建时间
Timeout interface{} // 默认超时时间
Actions interface{} // IP触发的动作
Description interface{} // 描述
IsPublic interface{} // 是否公用
}
func NewIPListOperator() *IPListOperator {

View File

@@ -38,6 +38,7 @@ const (
MessageTypeServerNamesAuditingSuccess MessageType = "ServerNamesAuditingSuccess" // 服务域名审核成功
MessageTypeServerNamesAuditingFailed MessageType = "ServerNamesAuditingFailed" // 服务域名审核失败
MessageTypeThresholdSatisfied MessageType = "ThresholdSatisfied" // 满足阈值
MessageTypeFirewallEvent MessageType = "FirewallEvent" // 防火墙事件
)
type MessageDAO dbs.DAO

View File

@@ -111,6 +111,37 @@ func (this *MessageReceiverDAO) FindAllEnabledReceivers(tx *dbs.Tx, target Messa
AscPk().
Slice(&result).
FindAll()
if err != nil {
return nil, err
}
if len(result) == 0 {
// 去掉类型再试试
query := this.Query(tx)
_, err = query.
Attr("clusterId", target.ClusterId).
Attr("nodeId", target.NodeId).
Attr("serverId", target.ServerId).
State(MessageReceiverStateEnabled).
AscPk().
Slice(&result).
FindAll()
if err != nil {
return nil, err
}
// 去掉服务和节点再试试
if len(result) == 0 {
query := this.Query(tx)
_, err = query.
Attr("clusterId", target.ClusterId).
State(MessageReceiverStateEnabled).
AscPk().
Slice(&result).
FindAll()
}
}
return
}

View File

@@ -2,8 +2,10 @@
package models
// MessageTaskTarget 消息接收对象
// 每个字段不一定都有值
type MessageTaskTarget struct {
ClusterId int64
NodeId int64
ServerId int64
ClusterId int64 // 集群ID
NodeId int64 // 节点ID
ServerId int64 // 服务ID
}

View File

@@ -0,0 +1,154 @@
package models
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
)
const (
MetricChartStateEnabled = 1 // 已启用
MetricChartStateDisabled = 0 // 已禁用
)
type MetricChartDAO dbs.DAO
func NewMetricChartDAO() *MetricChartDAO {
return dbs.NewDAO(&MetricChartDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeMetricCharts",
Model: new(MetricChart),
PkName: "id",
},
}).(*MetricChartDAO)
}
var SharedMetricChartDAO *MetricChartDAO
func init() {
dbs.OnReady(func() {
SharedMetricChartDAO = NewMetricChartDAO()
})
}
// EnableMetricChart 启用条目
func (this *MetricChartDAO) EnableMetricChart(tx *dbs.Tx, chartId int64) error {
_, err := this.Query(tx).
Pk(chartId).
Set("state", MetricChartStateEnabled).
Update()
return err
}
// DisableMetricChart 禁用条目
func (this *MetricChartDAO) DisableMetricChart(tx *dbs.Tx, chartId int64) error {
_, err := this.Query(tx).
Pk(chartId).
Set("state", MetricChartStateDisabled).
Update()
return err
}
// FindEnabledMetricChart 查找启用中的条目
func (this *MetricChartDAO) FindEnabledMetricChart(tx *dbs.Tx, chartId int64) (*MetricChart, error) {
result, err := this.Query(tx).
Pk(chartId).
Attr("state", MetricChartStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*MetricChart), err
}
// FindMetricChartName 根据主键查找名称
func (this *MetricChartDAO) FindMetricChartName(tx *dbs.Tx, chartId int64) (string, error) {
return this.Query(tx).
Pk(chartId).
Result("name").
FindStringCol("")
}
// CreateChart 创建图表
func (this *MetricChartDAO) CreateChart(tx *dbs.Tx, itemId int64, name string, chartType string, widthDiv int32, maxItems int32, params maps.Map) (int64, error) {
op := NewMetricChartOperator()
op.ItemId = itemId
op.Name = name
op.Type = chartType
op.WidthDiv = widthDiv
op.MaxItems = maxItems
if params == nil {
params = maps.Map{}
}
paramsJSON, err := json.Marshal(params)
if err != nil {
return 0, err
}
op.Params = paramsJSON
op.IsOn = true
op.State = MetricChartStateEnabled
return this.SaveInt64(tx, op)
}
// UpdateChart 修改图表
func (this *MetricChartDAO) UpdateChart(tx *dbs.Tx, chartId int64, name string, chartType string, widthDiv int32, maxItems int32, params maps.Map, isOn bool) error {
if chartId <= 0 {
return errors.New("invalid chartId")
}
op := NewMetricChartOperator()
op.Id = chartId
op.Name = name
op.Type = chartType
op.WidthDiv = widthDiv
op.MaxItems = maxItems
if params == nil {
params = maps.Map{}
}
paramsJSON, err := json.Marshal(params)
if err != nil {
return err
}
op.Params = paramsJSON
op.IsOn = isOn
return this.Save(tx, op)
}
// CountEnabledCharts 计算图表数量
func (this *MetricChartDAO) CountEnabledCharts(tx *dbs.Tx, itemId int64) (int64, error) {
return this.Query(tx).
Attr("itemId", itemId).
State(MetricChartStateEnabled).
Count()
}
// ListEnabledCharts 列出单页图表
func (this *MetricChartDAO) ListEnabledCharts(tx *dbs.Tx, itemId int64, offset int64, size int64) (result []*MetricChart, err error) {
_, err = this.Query(tx).
Attr("itemId", itemId).
State(MetricChartStateEnabled).
Offset(offset).
Limit(size).
DescPk().
Slice(&result).
FindAll()
return
}
// FindAllEnabledCharts 查找所有图表
func (this *MetricChartDAO) FindAllEnabledCharts(tx *dbs.Tx, itemId int64) (result []*MetricChart, err error) {
_, err = this.Query(tx).
Attr("itemId", itemId).
State(MetricChartStateEnabled).
DescPk().
Slice(&result).
FindAll()
return
}

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,34 @@
package models
// MetricChart 指标图表
type MetricChart struct {
Id uint32 `field:"id"` // ID
ItemId uint32 `field:"itemId"` // 指标ID
Name string `field:"name"` // 名称
Code string `field:"code"` // 代号
Type string `field:"type"` // 图形类型
WidthDiv int32 `field:"widthDiv"` // 宽度划分
Params string `field:"params"` // 图形参数
Order uint32 `field:"order"` // 排序
IsOn uint8 `field:"isOn"` // 是否启用
State uint8 `field:"state"` // 状态
MaxItems uint32 `field:"maxItems"` // 最多条目
}
type MetricChartOperator struct {
Id interface{} // ID
ItemId interface{} // 指标ID
Name interface{} // 名称
Code interface{} // 代号
Type interface{} // 图形类型
WidthDiv interface{} // 宽度划分
Params interface{} // 图形参数
Order interface{} // 排序
IsOn interface{} // 是否启用
State interface{} // 状态
MaxItems interface{} // 最多条目
}
func NewMetricChartOperator() *MetricChartOperator {
return &MetricChartOperator{}
}

View File

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

View File

@@ -0,0 +1,329 @@
package models
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"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/types"
"sort"
"strings"
)
const (
MetricItemStateEnabled = 1 // 已启用
MetricItemStateDisabled = 0 // 已禁用
)
type MetricItemDAO dbs.DAO
func NewMetricItemDAO() *MetricItemDAO {
return dbs.NewDAO(&MetricItemDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeMetricItems",
Model: new(MetricItem),
PkName: "id",
},
}).(*MetricItemDAO)
}
var SharedMetricItemDAO *MetricItemDAO
func init() {
dbs.OnReady(func() {
SharedMetricItemDAO = NewMetricItemDAO()
})
}
// EnableMetricItem 启用条目
func (this *MetricItemDAO) EnableMetricItem(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
Set("state", MetricItemStateEnabled).
Update()
return err
}
// DisableMetricItem 禁用条目
func (this *MetricItemDAO) DisableMetricItem(tx *dbs.Tx, itemId int64) error {
isPublic, err := this.Query(tx).
Pk(itemId).
Result("isPublic").
FindIntCol(0)
if err != nil {
return err
}
_, err = this.Query(tx).
Pk(itemId).
Set("state", MetricItemStateDisabled).
Update()
if err != nil {
return err
}
// 通知更新
err = this.NotifyUpdate(tx, itemId, isPublic == 1)
if err != nil {
return err
}
// 删除统计数据
err = SharedMetricStatDAO.DeleteItemStats(tx, itemId)
if err != nil {
return err
}
return nil
}
// FindEnabledMetricItem 查找启用中的条目
func (this *MetricItemDAO) FindEnabledMetricItem(tx *dbs.Tx, id int64) (*MetricItem, error) {
result, err := this.Query(tx).
Pk(id).
Attr("state", MetricItemStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*MetricItem), err
}
// FindMetricItemName 根据主键查找名称
func (this *MetricItemDAO) FindMetricItemName(tx *dbs.Tx, id int64) (string, error) {
return this.Query(tx).
Pk(id).
Result("name").
FindStringCol("")
}
// CreateItem 创建指标
func (this *MetricItemDAO) CreateItem(tx *dbs.Tx, code string, category string, name string, keys []string, period int32, periodUnit string, value string, isPublic bool) (int64, error) {
sort.Strings(keys)
op := NewMetricItemOperator()
op.Code = code
op.Category = category
op.Name = name
if len(keys) > 0 {
keysJSON, err := json.Marshal(keys)
if err != nil {
return 0, err
}
op.Keys = keysJSON
} else {
op.Keys = "[]"
}
op.Period = period
op.PeriodUnit = periodUnit
op.Value = value
op.IsPublic = isPublic
op.IsOn = true
op.State = MetricItemStateEnabled
itemId, err := this.SaveInt64(tx, op)
if err != nil {
return 0, err
}
if isPublic {
err = this.NotifyUpdate(tx, itemId, isPublic)
if err != nil {
return 0, err
}
}
return itemId, nil
}
// UpdateItem 修改\指标
func (this *MetricItemDAO) UpdateItem(tx *dbs.Tx, itemId int64, name string, keys []string, period int32, periodUnit string, value string, isOn bool, isPublic bool) error {
if itemId <= 0 {
return errors.New("invalid itemId")
}
sort.Strings(keys)
// 是否有变化
oldItem, err := this.FindEnabledMetricItem(tx, itemId)
if err != nil {
return err
}
if oldItem == nil {
return nil
}
oldIsPublic := oldItem.IsPublic == 1
var versionChanged = false
if strings.Join(oldItem.DecodeKeys(), "&") != strings.Join(keys, "&") || types.Int32(oldItem.Period) != period || oldItem.PeriodUnit != periodUnit || oldItem.Value != value {
versionChanged = true
}
// 保存
op := NewMetricItemOperator()
op.Id = itemId
op.Name = name
if len(keys) > 0 {
keysJSON, err := json.Marshal(keys)
if err != nil {
return err
}
op.Keys = keysJSON
} else {
op.Keys = "[]"
}
op.Period = period
op.PeriodUnit = periodUnit
op.Value = value
op.IsOn = isOn
if versionChanged {
op.Version = dbs.SQL("version+1")
}
op.IsPublic = isPublic
err = this.Save(tx, op)
if err != nil {
return err
}
// 通知更新
if versionChanged || (oldItem.IsOn == 0 && isOn) || (oldItem.IsOn == 1 && !isOn) || oldIsPublic != isPublic {
err := this.NotifyUpdate(tx, itemId, isPublic || oldIsPublic)
if err != nil {
return err
}
}
// 删除旧数据
if versionChanged {
err := SharedMetricStatDAO.DeleteOldItemStats(tx, itemId, types.Int32(oldItem.Version+1))
if err != nil {
return err
}
}
return nil
}
// CountEnabledItems 计算指标的数量
func (this *MetricItemDAO) CountEnabledItems(tx *dbs.Tx, category serverconfigs.MetricItemCategory) (int64, error) {
return this.Query(tx).
State(MetricItemStateEnabled).
Attr("userId", 0).
Attr("category", category).
Count()
}
// ListEnabledItems 列出单页指标
func (this *MetricItemDAO) ListEnabledItems(tx *dbs.Tx, category serverconfigs.MetricItemCategory, offset int64, size int64) (result []*MetricItem, err error) {
_, err = this.Query(tx).
State(MetricItemStateEnabled).
Attr("userId", 0).
Attr("category", category).
Offset(offset).
Limit(size).
DescPk().
Slice(&result).
FindAll()
return
}
// FindAllPublicItems 取得公用的指标
func (this *MetricItemDAO) FindAllPublicItems(tx *dbs.Tx) (result []*MetricItem, err error) {
_, err = this.Query(tx).
State(MetricItemStateEnabled).
Attr("userId", 0).
Attr("isPublic", true).
DescPk().
Slice(&result).
FindAll()
return
}
// ComposeItemConfig 组合指标配置
func (this *MetricItemDAO) ComposeItemConfig(tx *dbs.Tx, itemId int64) (*serverconfigs.MetricItemConfig, error) {
if itemId <= 0 {
return nil, nil
}
one, err := this.Query(tx).
Pk(itemId).
State(MetricItemStateEnabled).
Find()
if err != nil {
return nil, err
}
if one == nil {
return nil, nil
}
var item = one.(*MetricItem)
var config = &serverconfigs.MetricItemConfig{
Id: int64(item.Id),
IsOn: item.IsOn == 1,
Period: types.Int(item.Period),
PeriodUnit: item.PeriodUnit,
Category: item.Category,
Value: item.Value,
Keys: item.DecodeKeys(),
Version: types.Int32(item.Version),
}
return config, nil
}
// ComposeItemConfigWithItem 根据Item信息组合指标
func (this *MetricItemDAO) ComposeItemConfigWithItem(item *MetricItem) *serverconfigs.MetricItemConfig {
if item == nil {
return nil
}
var config = &serverconfigs.MetricItemConfig{
Id: int64(item.Id),
IsOn: item.IsOn == 1,
Period: types.Int(item.Period),
PeriodUnit: item.PeriodUnit,
Category: item.Category,
Value: item.Value,
Keys: item.DecodeKeys(),
Version: types.Int32(item.Version),
}
return config
}
// FindItemVersion 获取指标的版本号
func (this *MetricItemDAO) FindItemVersion(tx *dbs.Tx, itemId int64) (int32, error) {
version, err := this.Query(tx).
Pk(itemId).
Result("version").
FindIntCol(0)
if err != nil {
return 0, err
}
return types.Int32(version), nil
}
// NotifyUpdate 通知更新
func (this *MetricItemDAO) NotifyUpdate(tx *dbs.Tx, itemId int64, isPublic bool) error {
if isPublic {
clusterIds, err := SharedNodeClusterDAO.FindAllEnableClusterIds(tx)
if err != nil {
return err
}
for _, clusterId := range clusterIds {
err = SharedNodeTaskDAO.CreateClusterTask(tx, clusterId, NodeTaskTypeConfigChanged)
if err != nil {
return err
}
}
return nil
}
clusterIds, err := SharedNodeClusterMetricItemDAO.FindAllClusterIdsWithItemId(tx, itemId)
if err != nil {
return err
}
for _, clusterId := range clusterIds {
err = SharedNodeTaskDAO.CreateClusterTask(tx, clusterId, NodeTaskTypeConfigChanged)
if err != nil {
return err
}
}
return nil
}

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,40 @@
package models
// MetricItem 指标定义
type MetricItem struct {
Id uint64 `field:"id"` // ID
IsOn uint8 `field:"isOn"` // 是否启用
Code string `field:"code"` // 代号(用来区分是否内置)
Category string `field:"category"` // 类型比如http, tcp等
AdminId uint32 `field:"adminId"` // 管理员ID
UserId uint32 `field:"userId"` // 用户ID
Name string `field:"name"` // 指标名称
Keys string `field:"keys"` // 统计的Key
Period uint32 `field:"period"` // 周期
PeriodUnit string `field:"periodUnit"` // 周期单位
Value string `field:"value"` // 值运算
State uint8 `field:"state"` // 状态
Version uint32 `field:"version"` // 版本号
IsPublic uint8 `field:"isPublic"` // 是否为公用
}
type MetricItemOperator struct {
Id interface{} // ID
IsOn interface{} // 是否启用
Code interface{} // 代号(用来区分是否内置)
Category interface{} // 类型比如http, tcp等
AdminId interface{} // 管理员ID
UserId interface{} // 用户ID
Name interface{} // 指标名称
Keys interface{} // 统计的Key
Period interface{} // 周期
PeriodUnit interface{} // 周期单位
Value interface{} // 值运算
State interface{} // 状态
Version interface{} // 版本号
IsPublic interface{} // 是否为公用
}
func NewMetricItemOperator() *MetricItemOperator {
return &MetricItemOperator{}
}

View File

@@ -0,0 +1,14 @@
package models
import (
"encoding/json"
)
// DecodeKeys 解析Key
func (this *MetricItem) DecodeKeys() []string {
var result []string
if len(this.Keys) > 0 {
_ = json.Unmarshal([]byte(this.Keys), &result)
}
return result
}

View File

@@ -0,0 +1,355 @@
package models
import (
"encoding/json"
_ "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/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"strconv"
"time"
)
type MetricStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
for range ticker.C {
err := SharedMetricStatDAO.Clean(nil, 120) // 只保留120天
if err != nil {
logs.Println("SharedMetricStatDAO: clean expired data failed: " + err.Error())
}
}
}()
})
}
func NewMetricStatDAO() *MetricStatDAO {
return dbs.NewDAO(&MetricStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeMetricStats",
Model: new(MetricStat),
PkName: "id",
},
}).(*MetricStatDAO)
}
var SharedMetricStatDAO *MetricStatDAO
func init() {
dbs.OnReady(func() {
SharedMetricStatDAO = NewMetricStatDAO()
})
}
// CreateStat 创建统计数据
func (this *MetricStatDAO) CreateStat(tx *dbs.Tx, hash string, clusterId int64, nodeId int64, serverId int64, itemId int64, keys []string, value float64, time string, version int32) error {
hash += "@" + strconv.FormatInt(nodeId, 10)
var keysString string
if len(keys) > 0 {
keysJSON, err := json.Marshal(keys)
if err != nil {
return err
}
keysString = string(keysJSON)
} else {
keysString = "[]"
}
return this.Query(tx).
Param("value", value).
InsertOrUpdateQuickly(maps.Map{
"hash": hash,
"clusterId": clusterId,
"nodeId": nodeId,
"serverId": serverId,
"itemId": itemId,
"value": value,
"time": time,
"version": version,
"keys": keysString,
"createdDay": timeutil.Format("Ymd"),
}, maps.Map{
"value": value,
})
}
// DeleteOldItemStats 删除以前版本的统计数据
func (this *MetricStatDAO) DeleteOldItemStats(tx *dbs.Tx, itemId int64, version int32) error {
_, err := this.Query(tx).
Attr("itemId", itemId).
Where("version<:version").
Delete()
return err
}
// DeleteItemStats 删除某个指标相关的统计数据
func (this *MetricStatDAO) DeleteItemStats(tx *dbs.Tx, itemId int64) error {
_, err := this.Query(tx).
Attr("itemId", itemId).
Delete()
return err
}
// CountItemStats 计算统计数据数量
func (this *MetricStatDAO) CountItemStats(tx *dbs.Tx, itemId int64, version int32) (int64, error) {
return this.Query(tx).
Attr("itemId", itemId).
Attr("version", version).
Count()
}
// ListItemStats 列出单页统计数据
func (this *MetricStatDAO) ListItemStats(tx *dbs.Tx, itemId int64, version int32, offset int64, size int64) (result []*MetricStat, err error) {
_, err = this.Query(tx).
Attr("itemId", itemId).
Attr("version", version).
Offset(offset).
Limit(size).
Desc("time").
Desc("serverId").
Desc("value").
Slice(&result).
FindAll()
return
}
// FindItemStatsAtLastTime 取得所有集群最近一次计时前 N 个数据
// 适合每条数据中包含不同的Key的场景
func (this *MetricStatDAO) FindItemStatsAtLastTime(tx *dbs.Tx, itemId int64, version int32, size int64) (result []*MetricStat, err error) {
// 最近一次时间
statOne, err := this.Query(tx).
Attr("itemId", itemId).
Attr("version", version).
DescPk().
Find()
if err != nil {
return nil, err
}
if statOne == nil {
return nil, nil
}
var lastStat = statOne.(*MetricStat)
var lastTime = lastStat.Time
_, err = this.Query(tx).
Attr("itemId", itemId).
Attr("version", version).
Attr("time", lastTime).
// TODO 增加更多聚合算法,比如 AVG、MEDIAN、MIN、MAX 等
// TODO 这里的 MIN(`keys`) 在MySQL8中可以换成FIRST_VALUE
Result("MIN(time) AS time", "SUM(value) AS value", "keys").
Desc("value").
Group("keys").
Limit(size).
Slice(&result).
FindAll()
return
}
// FindItemStatsWithClusterIdAndLastTime 取得集群最近一次计时前 N 个数据
// 适合每条数据中包含不同的Key的场景
func (this *MetricStatDAO) FindItemStatsWithClusterIdAndLastTime(tx *dbs.Tx, clusterId int64, itemId int64, version int32, size int64) (result []*MetricStat, err error) {
// 最近一次时间
statOne, err := this.Query(tx).
Attr("itemId", itemId).
Attr("version", version).
DescPk().
Find()
if err != nil {
return nil, err
}
if statOne == nil {
return nil, nil
}
var lastStat = statOne.(*MetricStat)
var lastTime = lastStat.Time
_, err = this.Query(tx).
Attr("clusterId", clusterId).
Attr("itemId", itemId).
Attr("version", version).
Attr("time", lastTime).
// TODO 增加更多聚合算法,比如 AVG、MEDIAN、MIN、MAX 等
// TODO 这里的 MIN(`keys`) 在MySQL8中可以换成FIRST_VALUE
Result("MIN(time) AS time", "SUM(value) AS value", "keys").
Desc("value").
Group("keys").
Limit(size).
Slice(&result).
FindAll()
return
}
// FindItemStatsWithNodeIdAndLastTime 取得节点最近一次计时前 N 个数据
// 适合每条数据中包含不同的Key的场景
func (this *MetricStatDAO) FindItemStatsWithNodeIdAndLastTime(tx *dbs.Tx, nodeId int64, itemId int64, version int32, size int64) (result []*MetricStat, err error) {
// 最近一次时间
statOne, err := this.Query(tx).
Attr("itemId", itemId).
Attr("version", version).
DescPk().
Find()
if err != nil {
return nil, err
}
if statOne == nil {
return nil, nil
}
var lastStat = statOne.(*MetricStat)
var lastTime = lastStat.Time
_, err = this.Query(tx).
Attr("nodeId", nodeId).
Attr("itemId", itemId).
Attr("version", version).
Attr("time", lastTime).
// TODO 增加更多聚合算法,比如 AVG、MEDIAN、MIN、MAX 等
// TODO 这里的 MIN(`keys`) 在MySQL8中可以换成FIRST_VALUE
Result("MIN(time) AS time", "SUM(value) AS value", "keys").
Desc("value").
Group("keys").
Limit(size).
Slice(&result).
FindAll()
return
}
// FindItemStatsWithServerIdAndLastTime 取得节点最近一次计时前 N 个数据
// 适合每条数据中包含不同的Key的场景
func (this *MetricStatDAO) FindItemStatsWithServerIdAndLastTime(tx *dbs.Tx, serverId int64, itemId int64, version int32, size int64) (result []*MetricStat, err error) {
// 最近一次时间
statOne, err := this.Query(tx).
Attr("itemId", itemId).
Attr("version", version).
DescPk().
Find()
if err != nil {
return nil, err
}
if statOne == nil {
return nil, nil
}
var lastStat = statOne.(*MetricStat)
var lastTime = lastStat.Time
_, err = this.Query(tx).
Attr("serverId", serverId).
Attr("itemId", itemId).
Attr("version", version).
Attr("time", lastTime).
// TODO 增加更多聚合算法,比如 AVG、MEDIAN、MIN、MAX 等
// TODO 这里的 MIN(`keys`) 在MySQL8中可以换成FIRST_VALUE
Result("MIN(time) AS time", "SUM(value) AS value", "keys").
Desc("value").
Group("keys").
Limit(size).
Slice(&result).
FindAll()
return
}
// FindLatestItemStats 取得所有集群上最近 N 个时间的数据
// 适合同个Key在不同时间段的变化场景
func (this *MetricStatDAO) FindLatestItemStats(tx *dbs.Tx, itemId int64, version int32, size int64) (result []*MetricStat, err error) {
_, err = this.Query(tx).
Attr("itemId", itemId).
Attr("version", version).
// TODO 增加更多聚合算法,比如 AVG、MEDIAN、MIN、MAX 等
// TODO 这里的 MIN(`keys`) 在MySQL8中可以换成FIRST_VALUE
Result("time", "SUM(value) AS value", "MIN(`keys`) AS `keys`").
Desc("time").
Group("time").
Limit(size).
Slice(&result).
FindAll()
if err != nil {
return nil, err
}
lists.Reverse(result)
return
}
// FindLatestItemStatsWithClusterId 取得集群最近 N 个时间的数据
// 适合同个Key在不同时间段的变化场景
func (this *MetricStatDAO) FindLatestItemStatsWithClusterId(tx *dbs.Tx, clusterId int64, itemId int64, version int32, size int64) (result []*MetricStat, err error) {
_, err = this.Query(tx).
Attr("clusterId", clusterId).
Attr("itemId", itemId).
Attr("version", version).
// TODO 增加更多聚合算法,比如 AVG、MEDIAN、MIN、MAX 等
// TODO 这里的 MIN(`keys`) 在MySQL8中可以换成FIRST_VALUE
Result("time", "SUM(value) AS value", "MIN(`keys`) AS `keys`").
Desc("time").
Group("time").
Limit(size).
Slice(&result).
FindAll()
if err != nil {
return nil, err
}
lists.Reverse(result)
return
}
// FindLatestItemStatsWithNodeId 取得节点最近 N 个时间的数据
// 适合同个Key在不同时间段的变化场景
func (this *MetricStatDAO) FindLatestItemStatsWithNodeId(tx *dbs.Tx, nodeId int64, itemId int64, version int32, size int64) (result []*MetricStat, err error) {
_, err = this.Query(tx).
Attr("nodeId", nodeId).
Attr("itemId", itemId).
Attr("version", version).
// TODO 增加更多聚合算法,比如 AVG、MEDIAN、MIN、MAX 等
// TODO 这里的 MIN(`keys`) 在MySQL8中可以换成FIRST_VALUE
Result("time", "SUM(value) AS value", "MIN(`keys`) AS `keys`").
Desc("time").
Group("time").
Limit(size).
Slice(&result).
FindAll()
if err != nil {
return nil, err
}
lists.Reverse(result)
return
}
// FindLatestItemStatsWithServerId 取得服务最近 N 个时间的数据
// 适合同个Key在不同时间段的变化场景
func (this *MetricStatDAO) FindLatestItemStatsWithServerId(tx *dbs.Tx, serverId int64, itemId int64, version int32, size int64) (result []*MetricStat, err error) {
_, err = this.Query(tx).
Attr("serverId", serverId).
Attr("itemId", itemId).
Attr("version", version).
// TODO 增加更多聚合算法,比如 AVG、MEDIAN、MIN、MAX 等
// TODO 这里的 MIN(`keys`) 在MySQL8中可以换成FIRST_VALUE
Result("time", "SUM(value) AS value", "MIN(`keys`) AS `keys`").
Desc("time").
Group("time").
Limit(size).
Slice(&result).
FindAll()
if err != nil {
return nil, err
}
lists.Reverse(result)
return
}
// 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
}
return nil
}

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,34 @@
package models
// MetricStat 指标统计数据
type MetricStat struct {
Id uint64 `field:"id"` // ID
Hash string `field:"hash"` // Hash值
ClusterId uint32 `field:"clusterId"` // 集群ID
NodeId uint32 `field:"nodeId"` // 节点ID
ServerId uint32 `field:"serverId"` // 服务ID
ItemId uint64 `field:"itemId"` // 指标
Keys string `field:"keys"` // 键值
Value float64 `field:"value"` // 数值
Time string `field:"time"` // 分钟值YYYYMMDDHHII
Version uint32 `field:"version"` // 版本号
CreatedDay string `field:"createdDay"` // YYYYMMDD
}
type MetricStatOperator struct {
Id interface{} // ID
Hash interface{} // Hash值
ClusterId interface{} // 集群ID
NodeId interface{} // 节点ID
ServerId interface{} // 服务ID
ItemId interface{} // 指标
Keys interface{} // 键值
Value interface{} // 数值
Time interface{} // 分钟值YYYYMMDDHHII
Version interface{} // 版本号
CreatedDay interface{} // YYYYMMDD
}
func NewMetricStatOperator() *MetricStatOperator {
return &MetricStatOperator{}
}

View File

@@ -0,0 +1,12 @@
package models
import "encoding/json"
// DecodeKeys 解析Key
func (this *MetricStat) DecodeKeys() []string {
var result []string
if len(this.Keys) > 0 {
_ = json.Unmarshal([]byte(this.Keys), &result)
}
return result
}

View File

@@ -0,0 +1,136 @@
package models
import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
)
type MetricSumStatDAO dbs.DAO
func NewMetricSumStatDAO() *MetricSumStatDAO {
return dbs.NewDAO(&MetricSumStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeMetricSumStats",
Model: new(MetricSumStat),
PkName: "id",
},
}).(*MetricSumStatDAO)
}
var SharedMetricSumStatDAO *MetricSumStatDAO
func init() {
dbs.OnReady(func() {
SharedMetricSumStatDAO = NewMetricSumStatDAO()
})
}
// UpdateSum 更新统计数据
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,
}, maps.Map{
"count": count,
"total": total,
})
}
// FindNodeServerSum 查找某个服务在某个节点上的统计数据
func (this *MetricSumStatDAO) FindNodeServerSum(tx *dbs.Tx, nodeId int64, serverId int64, time string, itemId int64, version int32) (count int64, total float32, err error) {
one, err := this.Query(tx).
Attr("nodeId", nodeId).
Attr("serverId", serverId).
Attr("time", time).
Attr("itemId", itemId).
Attr("version", version).
Find()
if err != nil {
return 0, 0, err
}
if one == nil {
return
}
return int64(one.(*MetricSumStat).Count), float32(one.(*MetricSumStat).Total), nil
}
// FindSumAtTime 查找某个时间的统计数据
func (this *MetricSumStatDAO) FindSumAtTime(tx *dbs.Tx, time string, itemId int64, version int32) (count int64, total float32, err error) {
one, err := this.Query(tx).
Attr("time", time).
Attr("itemId", itemId).
Attr("version", version).
Result("SUM(count) AS `count`, SUM(total) AS total").
Find()
if err != nil {
return 0, 0, err
}
if one == nil {
return
}
return int64(one.(*MetricSumStat).Count), float32(one.(*MetricSumStat).Total), nil
}
// 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).
Attr("serverId", serverId).
Attr("time", time).
Attr("itemId", itemId).
Attr("version", version).
Result("SUM(count) AS `count`, SUM(total) AS total").
Find()
if err != nil {
return 0, 0, err
}
if one == nil {
return
}
return int64(one.(*MetricSumStat).Count), float32(one.(*MetricSumStat).Total), nil
}
// 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).
Attr("clusterId", clusterId).
Attr("time", time).
Attr("itemId", itemId).
Attr("version", version).
Result("SUM(count) AS `count`, SUM(total) AS total").
Find()
if err != nil {
return 0, 0, err
}
if one == nil {
return
}
return int64(one.(*MetricSumStat).Count), float32(one.(*MetricSumStat).Total), nil
}
// 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).
Attr("nodeId", nodeId).
Attr("time", time).
Attr("itemId", itemId).
Attr("version", version).
Result("SUM(count) AS `count`, SUM(total) AS total").
Find()
if err != nil {
return 0, 0, err
}
if one == nil {
return
}
return int64(one.(*MetricSumStat).Count), float32(one.(*MetricSumStat).Total), nil
}

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,30 @@
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"` // 版本号
}
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{} // 版本号
}
func NewMetricSumStatOperator() *MetricSumStatOperator {
return &MetricSumStatOperator{}
}

View File

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

View File

@@ -75,6 +75,15 @@ func (this *NSNodeDAO) FindEnabledNSNode(tx *dbs.Tx, id int64) (*NSNode, error)
return result.(*NSNode), err
}
// FindEnabledNSNodeName 查找节点名称
func (this *NSNodeDAO) FindEnabledNSNodeName(tx *dbs.Tx, nodeId int64) (string, error) {
return this.Query(tx).
Pk(nodeId).
State(NSNodeStateEnabled).
Result("name").
FindStringCol("")
}
// FindAllEnabledNodesWithClusterId 查找一个集群下的所有节点
func (this *NSNodeDAO) FindAllEnabledNodesWithClusterId(tx *dbs.Tx, clusterId int64) (result []*NSNode, err error) {
_, err = this.Query(tx).
@@ -94,6 +103,15 @@ func (this *NSNodeDAO) CountAllEnabledNodes(tx *dbs.Tx) (int64, error) {
Count()
}
// CountAllOfflineNodes 计算离线节点数量
func (this *NSNodeDAO) CountAllOfflineNodes(tx *dbs.Tx) (int64, error) {
return this.Query(tx).
State(NSNodeStateEnabled).
Where("(status IS NULL OR JSON_EXTRACT(status, '$.updatedAt')<UNIX_TIMESTAMP()-120)").
Where("clusterId IN (SELECT id FROM " + SharedNSClusterDAO.Table + " WHERE state=1)").
Count()
}
// CountAllEnabledNodesMatch 计算满足条件的节点数量
func (this *NSNodeDAO) CountAllEnabledNodesMatch(tx *dbs.Tx, clusterId int64, installState configutils.BoolState, activeState configutils.BoolState, keyword string) (int64, error) {
query := this.Query(tx)
@@ -335,6 +353,7 @@ func (this NSNodeDAO) UpdateNodeStatus(tx *dbs.Tx, nodeId int64, statusJSON []by
func (this *NSNodeDAO) CountAllLowerVersionNodes(tx *dbs.Tx, version string) (int64, error) {
return this.Query(tx).
State(NSNodeStateEnabled).
Where("clusterId IN (SELECT id FROM "+SharedNSClusterDAO.Table+" WHERE state=1)").
Where("status IS NOT NULL").
Where("(JSON_EXTRACT(status, '$.buildVersionCode') IS NULL OR JSON_EXTRACT(status, '$.buildVersionCode')<:version)").
Param("version", utils.VersionToLong(version)).
@@ -379,6 +398,14 @@ func (this *NSNodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64) (*dnsconfigs.
return config, nil
}
// FindNodeClusterId 获取节点的集群ID
func (this *NSNodeDAO) FindNodeClusterId(tx *dbs.Tx, nodeId int64) (int64, error) {
return this.Query(tx).
Pk(nodeId).
Result("clusterId").
FindInt64Col(0)
}
// NotifyUpdate 通知更新
func (this *NSNodeDAO) NotifyUpdate(tx *dbs.Tx, nodeId int64) error {
// TODO 先什么都不做

View File

@@ -112,7 +112,8 @@ func (this *NSRecordDAO) CreateRecord(tx *dbs.Tx, domainId int64, description st
return this.SaveInt64(tx, op)
}
func (this *NSRecordDAO) UpdateRecord(tx *dbs.Tx, recordId int64, description string, name string, dnsType dnsconfigs.RecordType, value string, ttl int32, routeIds []int64) error {
// UpdateRecord 修改记录
func (this *NSRecordDAO) UpdateRecord(tx *dbs.Tx, recordId int64, description string, name string, dnsType dnsconfigs.RecordType, value string, ttl int32, routeIds []int64, isOn bool) error {
if recordId <= 0 {
return errors.New("invalid recordId")
}
@@ -129,6 +130,7 @@ func (this *NSRecordDAO) UpdateRecord(tx *dbs.Tx, recordId int64, description st
op.Type = dnsType
op.Value = value
op.Ttl = ttl
op.IsOn = isOn
if len(routeIds) == 0 {
op.RouteIds = "[]"
@@ -145,7 +147,8 @@ func (this *NSRecordDAO) UpdateRecord(tx *dbs.Tx, recordId int64, description st
return this.Save(tx, op)
}
func (this *NSRecordDAO) CountAllEnabledRecords(tx *dbs.Tx, domainId int64, dnsType dnsconfigs.RecordType, keyword string, routeId int64) (int64, error) {
// CountAllEnabledDomainRecords 计算域名中记录数量
func (this *NSRecordDAO) CountAllEnabledDomainRecords(tx *dbs.Tx, domainId int64, dnsType dnsconfigs.RecordType, keyword string, routeId int64) (int64, error) {
query := this.Query(tx).
Attr("domainId", domainId).
State(NSRecordStateEnabled)
@@ -162,6 +165,15 @@ func (this *NSRecordDAO) CountAllEnabledRecords(tx *dbs.Tx, domainId int64, dnsT
return query.Count()
}
// CountAllEnabledRecords 计算所有记录数量
func (this *NSRecordDAO) CountAllEnabledRecords(tx *dbs.Tx) (int64, error) {
return this.Query(tx).
Where("domainId IN (SELECT id FROM " + SharedNSDomainDAO.Table + " WHERE state=1)").
State(NSRecordStateEnabled).
Count()
}
// ListEnabledRecords 列出单页记录
func (this *NSRecordDAO) ListEnabledRecords(tx *dbs.Tx, domainId int64, dnsType dnsconfigs.RecordType, keyword string, routeId int64, offset int64, size int64) (result []*NSRecord, err error) {
query := this.Query(tx).
Attr("domainId", domainId).

View File

@@ -0,0 +1,168 @@
package nameservers
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
_ "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"
)
type NSRecordHourlyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
for range ticker.C {
err := SharedNSRecordHourlyStatDAO.Clean(nil, 60) // 只保留60天
if err != nil {
remotelogs.Error("NodeClusterTrafficDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
}
func NewNSRecordHourlyStatDAO() *NSRecordHourlyStatDAO {
return dbs.NewDAO(&NSRecordHourlyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeNSRecordHourlyStats",
Model: new(NSRecordHourlyStat),
PkName: "id",
},
}).(*NSRecordHourlyStatDAO)
}
var SharedNSRecordHourlyStatDAO *NSRecordHourlyStatDAO
func init() {
dbs.OnReady(func() {
SharedNSRecordHourlyStatDAO = NewNSRecordHourlyStatDAO()
})
}
// IncreaseHourlyStat 增加统计数据
func (this *NSRecordHourlyStatDAO) IncreaseHourlyStat(tx *dbs.Tx, clusterId int64, nodeId int64, hour string, domainId int64, recordId int64, countRequests int64, bytes int64) error {
if len(hour) != 10 {
return errors.New("invalid hour '" + hour + "'")
}
return this.Query(tx).
Param("countRequests", countRequests).
Param("bytes", bytes).
InsertOrUpdateQuickly(maps.Map{
"clusterId": clusterId,
"nodeId": nodeId,
"domainId": domainId,
"recordId": recordId,
"day": hour[:8],
"hour": hour,
"countRequests": countRequests,
"bytes": bytes,
}, maps.Map{
"countRequests": dbs.SQL("countRequests+:countRequests"),
"bytes": dbs.SQL("bytes+:bytes"),
})
}
// FindHourlyStats 按小时统计
func (this *NSRecordHourlyStatDAO) FindHourlyStats(tx *dbs.Tx, hourFrom string, hourTo string) (result []*NSRecordHourlyStat, err error) {
ones, err := this.Query(tx).
Result("hour", "SUM(countRequests) AS countRequests", "SUM(bytes) AS bytes").
Between("hour", hourFrom, hourTo).
Group("hour").
FindAll()
if err != nil {
return nil, err
}
var m = map[string]*NSRecordHourlyStat{} // hour => *NSRecordHourlyStat
for _, one := range ones {
m[one.(*NSRecordHourlyStat).Hour] = one.(*NSRecordHourlyStat)
}
hours, err := utils.RangeHours(hourFrom, hourTo)
if err != nil {
return nil, err
}
for _, hour := range hours {
stat, ok := m[hour]
if ok {
result = append(result, stat)
} else {
result = append(result, &NSRecordHourlyStat{
Hour: hour,
})
}
}
return
}
// FindDailyStats 按天统计
func (this *NSRecordHourlyStatDAO) FindDailyStats(tx *dbs.Tx, dayFrom string, dayTo string) (result []*NSRecordHourlyStat, err error) {
ones, err := this.Query(tx).
Result("day", "SUM(countRequests) AS countRequests", "SUM(bytes) AS bytes").
Between("day", dayFrom, dayTo).
Group("day").
FindAll()
if err != nil {
return nil, err
}
var m = map[string]*NSRecordHourlyStat{} // day => *NSRecordHourlyStat
for _, one := range ones {
m[one.(*NSRecordHourlyStat).Day] = one.(*NSRecordHourlyStat)
}
days, err := utils.RangeDays(dayFrom, dayTo)
if err != nil {
return nil, err
}
for _, day := range days {
stat, ok := m[day]
if ok {
result = append(result, stat)
} else {
result = append(result, &NSRecordHourlyStat{
Day: day,
})
}
}
return
}
// ListTopNodes 节点排行
func (this *NSRecordHourlyStatDAO) ListTopNodes(tx *dbs.Tx, hourFrom string, hourTo string, size int64) (result []*NSRecordHourlyStat, err error) {
_, err = this.Query(tx).
Result("MIN(clusterId) AS clusterId", "nodeId", "SUM(countRequests) AS countRequests", "SUM(bytes) AS bytes").
Between("hour", hourFrom, hourTo).
Group("nodeId").
Limit(size).
Slice(&result).
FindAll()
return
}
// ListTopDomains 域名排行
func (this *NSRecordHourlyStatDAO) ListTopDomains(tx *dbs.Tx, hourFrom string, hourTo string, size int64) (result []*NSRecordHourlyStat, err error) {
_, err = this.Query(tx).
Result("domainId", "SUM(countRequests) AS countRequests", "SUM(bytes) AS bytes").
Between("hour", hourFrom, hourTo).
Group("domainId").
Limit(size).
Slice(&result).
FindAll()
return
}
// Clean 清理历史数据
func (this *NSRecordHourlyStatDAO) Clean(tx *dbs.Tx, days int) error {
var hour = timeutil.Format("Ymd00", time.Now().AddDate(0, 0, -days))
_, err := this.Query(tx).
Lt("hour", hour).
Delete()
return err
}

View File

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

View File

@@ -0,0 +1,30 @@
package nameservers
// NSRecordHourlyStat NS记录统计
type NSRecordHourlyStat struct {
Id uint64 `field:"id"` // ID
ClusterId uint32 `field:"clusterId"` // 集群ID
NodeId uint32 `field:"nodeId"` // 节点ID
DomainId uint32 `field:"domainId"` // 域名ID
RecordId uint64 `field:"recordId"` // 记录ID
Day string `field:"day"` // YYYYMMDD
Hour string `field:"hour"` // YYYYMMDDHH
CountRequests uint32 `field:"countRequests"` // 请求数
Bytes uint64 `field:"bytes"` // 流量
}
type NSRecordHourlyStatOperator struct {
Id interface{} // ID
ClusterId interface{} // 集群ID
NodeId interface{} // 节点ID
DomainId interface{} // 域名ID
RecordId interface{} // 记录ID
Day interface{} // YYYYMMDD
Hour interface{} // YYYYMMDDHH
CountRequests interface{} // 请求数
Bytes interface{} // 流量
}
func NewNSRecordHourlyStatOperator() *NSRecordHourlyStatOperator {
return &NSRecordHourlyStatOperator{}
}

View File

@@ -0,0 +1 @@
package nameservers

View File

@@ -85,9 +85,9 @@ func (this *NodeClusterDAO) FindEnabledClusterIdWithUniqueId(tx *dbs.Tx, uniqueI
}
// FindNodeClusterName 根据主键查找名称
func (this *NodeClusterDAO) FindNodeClusterName(tx *dbs.Tx, id int64) (string, error) {
func (this *NodeClusterDAO) FindNodeClusterName(tx *dbs.Tx, clusterId int64) (string, error) {
return this.Query(tx).
Pk(id).
Pk(clusterId).
Result("name").
FindStringCol("")
}

View File

@@ -0,0 +1,162 @@
package models
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
)
const (
NodeClusterMetricItemStateEnabled = 1 // 已启用
NodeClusterMetricItemStateDisabled = 0 // 已禁用
)
type NodeClusterMetricItemDAO dbs.DAO
func NewNodeClusterMetricItemDAO() *NodeClusterMetricItemDAO {
return dbs.NewDAO(&NodeClusterMetricItemDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeNodeClusterMetricItems",
Model: new(NodeClusterMetricItem),
PkName: "id",
},
}).(*NodeClusterMetricItemDAO)
}
var SharedNodeClusterMetricItemDAO *NodeClusterMetricItemDAO
func init() {
dbs.OnReady(func() {
SharedNodeClusterMetricItemDAO = NewNodeClusterMetricItemDAO()
})
}
// EnableNodeClusterMetricItem 启用条目
func (this *NodeClusterMetricItemDAO) EnableNodeClusterMetricItem(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
Set("state", NodeClusterMetricItemStateEnabled).
Update()
return err
}
// DisableNodeClusterMetricItem 禁用条目
func (this *NodeClusterMetricItemDAO) DisableNodeClusterMetricItem(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
Set("state", NodeClusterMetricItemStateDisabled).
Update()
return err
}
// FindEnabledNodeClusterMetricItem 查找启用中的条目
func (this *NodeClusterMetricItemDAO) FindEnabledNodeClusterMetricItem(tx *dbs.Tx, id uint32) (*NodeClusterMetricItem, error) {
result, err := this.Query(tx).
Pk(id).
Attr("state", NodeClusterMetricItemStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*NodeClusterMetricItem), err
}
// DisableClusterItem 禁用某个集群的指标
func (this *NodeClusterMetricItemDAO) DisableClusterItem(tx *dbs.Tx, clusterId int64, itemId int64) error {
err := this.Query(tx).
Attr("clusterId", clusterId).
Attr("itemId", itemId).
State(NodeClusterMetricItemStateEnabled).
Set("state", NodeClusterMetricItemStateDisabled).
UpdateQuickly()
if err != nil {
return err
}
return this.NotifyUpdate(tx, clusterId)
}
// EnableClusterItem 启用某个集群的指标
func (this *NodeClusterMetricItemDAO) EnableClusterItem(tx *dbs.Tx, clusterId int64, itemId int64) error {
if clusterId <= 0 || itemId <= 0 {
return errors.New("clusterId or itemId should not be 0")
}
var op = NewNodeClusterMetricItemOperator()
op.ClusterId = clusterId
op.ItemId = itemId
op.IsOn = true
err := this.Save(tx, op)
if err != nil {
return err
}
return this.NotifyUpdate(tx, clusterId)
}
// FindAllClusterItems 查找某个集群的指标
// category 不填写即表示获取所有指标
func (this *NodeClusterMetricItemDAO) FindAllClusterItems(tx *dbs.Tx, clusterId int64, category string) (result []*NodeClusterMetricItem, err error) {
var query = this.Query(tx).
Attr("clusterId", clusterId).
State(NodeClusterMetricItemStateEnabled)
if len(category) > 0 {
query.Where("itemId IN (SELECT id FROM "+SharedMetricItemDAO.Table+" WHERE category=:category)").
Param("category", category)
}
_, err = query.
DescPk().
Slice(&result).
FindAll()
return
}
// FindAllClusterItemIds 查找某个集群的指标Ids
func (this *NodeClusterMetricItemDAO) FindAllClusterItemIds(tx *dbs.Tx, clusterId int64) (result []int64, err error) {
ones, err := this.Query(tx).
Attr("clusterId", clusterId).
State(NodeClusterMetricItemStateEnabled).
Result("itemId").
DescPk().
FindAll()
for _, one := range ones {
result = append(result, int64(one.(*NodeClusterMetricItem).ItemId))
}
return
}
// FindAllClusterIdsWithItemId 查找使用某个指标的所有集群IDs
func (this *NodeClusterMetricItemDAO) FindAllClusterIdsWithItemId(tx *dbs.Tx, itemId int64) (clusterIds []int64, err error) {
ones, err := this.Query(tx).
Attr("itemId", itemId).
Result("clusterId").
FindAll()
if err != nil {
return nil, err
}
for _, one := range ones {
clusterIds = append(clusterIds, int64(one.(*NodeClusterMetricItem).ClusterId))
}
return
}
// CountAllClusterItems 计算集群中指标数量
func (this *NodeClusterMetricItemDAO) CountAllClusterItems(tx *dbs.Tx, clusterId int64) (int64, error) {
return this.Query(tx).
Attr("clusterId", clusterId).
State(NodeClusterMetricItemStateEnabled).
Count()
}
// ExistsClusterItem 是否存在
func (this *NodeClusterMetricItemDAO) ExistsClusterItem(tx *dbs.Tx, clusterId int64, itemId int64) (bool, error) {
return this.Query(tx).
Attr("clusterId", clusterId).
Attr("itemId", itemId).
State(NodeClusterMetricItemStateEnabled).
Exist()
}
// NotifyUpdate 通知更新
func (this *NodeClusterMetricItemDAO) NotifyUpdate(tx *dbs.Tx, clusterId int64) error {
return SharedNodeTaskDAO.CreateClusterTask(tx, clusterId, NodeTaskTypeConfigChanged)
}

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,22 @@
package models
// NodeClusterMetricItem 集群使用的指标
type NodeClusterMetricItem struct {
Id uint32 `field:"id"` // ID
IsOn uint8 `field:"isOn"` // 是否启用
ClusterId uint32 `field:"clusterId"` // 集群ID
ItemId uint64 `field:"itemId"` // 指标ID
State uint8 `field:"state"` // 是否启用
}
type NodeClusterMetricItemOperator struct {
Id interface{} // ID
IsOn interface{} // 是否启用
ClusterId interface{} // 集群ID
ItemId interface{} // 指标ID
State interface{} // 是否启用
}
func NewNodeClusterMetricItemOperator() *NodeClusterMetricItemOperator {
return &NodeClusterMetricItemOperator{}
}

View File

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

View File

@@ -14,6 +14,7 @@ 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"
@@ -204,17 +205,27 @@ func (this *NodeDAO) CountAllEnabledNodes(tx *dbs.Tx) (int64, error) {
}
// ListEnabledNodesMatch 列出单页节点
func (this *NodeDAO) ListEnabledNodesMatch(tx *dbs.Tx, offset int64, size int64, clusterId int64, installState configutils.BoolState, activeState configutils.BoolState, keyword string, groupId int64, regionId int64) (result []*Node, err error) {
func (this *NodeDAO) ListEnabledNodesMatch(tx *dbs.Tx,
clusterId int64,
installState configutils.BoolState,
activeState configutils.BoolState,
keyword string,
groupId int64,
regionId int64,
order string,
offset int64,
size int64) (result []*Node, err error) {
query := this.Query(tx).
State(NodeStateEnabled).
Offset(offset).
Limit(size).
DescPk().
Slice(&result)
// 集群
if clusterId > 0 {
query.Attr("clusterId", clusterId)
} else {
query.Where("clusterId IN (SELECT id FROM " + SharedNodeClusterDAO.Table + " WHERE state=1)")
}
// 安装状态
@@ -253,6 +264,27 @@ func (this *NodeDAO) ListEnabledNodesMatch(tx *dbs.Tx, offset int64, size int64,
query.Attr("regionId", regionId)
}
// 排序
switch order {
case "cpuAsc":
query.Asc("IF(JSON_EXTRACT(status, '$.updatedAt')>UNIX_TIMESTAMP()-120, IFNULL(JSON_EXTRACT(status, '$.cpuUsage'), 0), 0)")
case "cpuDesc":
query.Desc("IF(JSON_EXTRACT(status, '$.updatedAt')>UNIX_TIMESTAMP()-120, IFNULL(JSON_EXTRACT(status, '$.cpuUsage'), 0), 0)")
case "memoryAsc":
query.Asc("IF(JSON_EXTRACT(status, '$.updatedAt')>UNIX_TIMESTAMP()-120, IFNULL(JSON_EXTRACT(status, '$.memoryUsage'), 0), 0)")
case "memoryDesc":
query.Desc("IF(JSON_EXTRACT(status, '$.updatedAt')>UNIX_TIMESTAMP()-120, IFNULL(JSON_EXTRACT(status, '$.memoryUsage'), 0), 0)")
case "trafficInAsc":
query.Asc("IF(JSON_EXTRACT(status, '$.updatedAt')>UNIX_TIMESTAMP()-120, IFNULL(JSON_EXTRACT(status, '$.trafficInBytes'), 0), 0)")
case "trafficInDesc":
query.Desc("IF(JSON_EXTRACT(status, '$.updatedAt')>UNIX_TIMESTAMP()-120, IFNULL(JSON_EXTRACT(status, '$.trafficInBytes'), 0), 0)")
case "trafficOutAsc":
query.Asc("IF(JSON_EXTRACT(status, '$.updatedAt')>UNIX_TIMESTAMP()-120, IFNULL(JSON_EXTRACT(status, '$.trafficOutBytes'), 0), 0)")
case "trafficOutDesc":
query.Desc("IF(JSON_EXTRACT(status, '$.updatedAt')>UNIX_TIMESTAMP()-120, IFNULL(JSON_EXTRACT(status, '$.trafficOutBytes'), 0), 0)")
}
query.DescPk()
_, err = query.FindAll()
return
}
@@ -301,6 +333,8 @@ func (this *NodeDAO) FindAllNodeIdsMatch(tx *dbs.Tx, clusterId int64, isOn confi
query.State(NodeStateEnabled)
if clusterId > 0 {
query.Attr("clusterId", clusterId)
} else {
query.Where("clusterId IN (SELECT id FROM " + SharedNodeClusterDAO.Table + " WHERE state=1)")
}
if isOn == configutils.BoolStateYes {
query.Attr("isOn", true)
@@ -345,13 +379,21 @@ func (this *NodeDAO) FindAllInactiveNodesWithClusterId(tx *dbs.Tx, clusterId int
}
// CountAllEnabledNodesMatch 计算节点数量
func (this *NodeDAO) CountAllEnabledNodesMatch(tx *dbs.Tx, clusterId int64, installState configutils.BoolState, activeState configutils.BoolState, keyword string, groupId int64, regionId int64) (int64, error) {
func (this *NodeDAO) CountAllEnabledNodesMatch(tx *dbs.Tx,
clusterId int64,
installState configutils.BoolState,
activeState configutils.BoolState,
keyword string,
groupId int64,
regionId int64) (int64, error) {
query := this.Query(tx)
query.State(NodeStateEnabled)
// 集群
if clusterId > 0 {
query.Attr("clusterId", clusterId)
} else {
query.Where("clusterId IN (SELECT id FROM " + SharedNodeClusterDAO.Table + " WHERE state=1)")
}
// 安装状态
@@ -513,6 +555,7 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64) (*nodeconfigs.N
config := &nodeconfigs.NodeConfig{
Id: int64(node.Id),
NodeId: node.UniqueId,
Secret: node.Secret,
IsOn: node.IsOn == 1,
Servers: nil,
Version: int64(node.Version),
@@ -640,6 +683,36 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64) (*nodeconfigs.N
}
}
// 集群指标
metricItemIds, err := SharedNodeClusterMetricItemDAO.FindAllClusterItemIds(tx, int64(node.ClusterId))
if err != nil {
return nil, err
}
var metricItems = []*serverconfigs.MetricItemConfig{}
for _, itemId := range metricItemIds {
itemConfig, err := SharedMetricItemDAO.ComposeItemConfig(tx, itemId)
if err != nil {
return nil, err
}
if itemConfig != nil {
metricItems = append(metricItems, itemConfig)
}
}
// 公用指标
publicMetricItems, err := SharedMetricItemDAO.FindAllPublicItems(tx)
if err != nil {
return nil, err
}
for _, item := range publicMetricItems {
itemConfig := SharedMetricItemDAO.ComposeItemConfigWithItem(item)
if itemConfig != nil && !lists.ContainsInt64(metricItemIds, itemConfig.Id) {
metricItems = append(metricItems, itemConfig)
}
}
config.MetricItems = metricItems
return config, nil
}

View File

@@ -85,6 +85,7 @@ func (this *NodeGrantDAO) CreateGrant(tx *dbs.Tx, adminId int64, name string, me
op.Password = password
op.Su = false // TODO 需要做到前端可以配置
case "privateKey":
op.Username = username
op.PrivateKey = privateKey
}
op.Description = description
@@ -111,6 +112,7 @@ func (this *NodeGrantDAO) UpdateGrant(tx *dbs.Tx, grantId int64, name string, me
op.Password = password
op.Su = false // TODO 需要做到前端可以配置
case "privateKey":
op.Username = username
op.PrivateKey = privateKey
}
op.Description = description

View File

@@ -214,6 +214,7 @@ func (this *NodeIPAddressDAO) FindNodeAccessIPAddresses(tx *dbs.Tx, nodeId int64
role = nodeconfigs.NodeRoleNode
}
_, err = this.Query(tx).
Attr("role", role).
Attr("nodeId", nodeId).
State(NodeIPAddressStateEnabled).
Attr("canAccess", true).

View File

@@ -171,11 +171,12 @@ func (this *NodeThresholdDAO) FindAllEnabledAndOnNodeThresholds(tx *dbs.Tx, role
}
// CountAllEnabledThresholds 计算阈值的数量
func (this *NodeThresholdDAO) CountAllEnabledThresholds(tx *dbs.Tx, clusterId int64, nodeId int64) (int64, error) {
func (this *NodeThresholdDAO) CountAllEnabledThresholds(tx *dbs.Tx, role string, clusterId int64, nodeId int64) (int64, error) {
if clusterId <= 0 && nodeId <= 0 {
return 0, nil
}
query := this.Query(tx)
query.Attr("role", role)
if clusterId > 0 {
query.Attr("clusterId", clusterId)
}

View File

@@ -33,13 +33,14 @@ func init() {
}
// CreateValue 创建值
func (this *NodeValueDAO) CreateValue(tx *dbs.Tx, role nodeconfigs.NodeRole, nodeId int64, item string, valueJSON []byte, createdAt int64) error {
func (this *NodeValueDAO) CreateValue(tx *dbs.Tx, clusterId int64, role nodeconfigs.NodeRole, nodeId int64, item string, valueJSON []byte, createdAt int64) error {
day := timeutil.FormatTime("Ymd", createdAt)
hour := timeutil.FormatTime("YmdH", createdAt)
minute := timeutil.FormatTime("YmdHi", createdAt)
return this.Query(tx).
InsertOrUpdateQuickly(maps.Map{
"clusterId": clusterId,
"role": role,
"nodeId": nodeId,
"item": item,
@@ -90,6 +91,80 @@ func (this *NodeValueDAO) ListValues(tx *dbs.Tx, role string, nodeId int64, item
return
}
// ListValuesWithClusterId 列出集群最近的的平均数据
func (this *NodeValueDAO) ListValuesWithClusterId(tx *dbs.Tx, role string, clusterId int64, item string, key string, timeRange nodeconfigs.NodeValueRange) (result []*NodeValue, err error) {
query := this.Query(tx).
Attr("role", role).
Attr("clusterId", clusterId).
Attr("item", item).
Result("AVG(JSON_EXTRACT(value, '$." + key + "')) AS value, MIN(createdAt) AS createdAt")
switch timeRange {
// TODO 支持更多的时间范围
case nodeconfigs.NodeValueRangeMinute:
fromMinute := timeutil.FormatTime("YmdHi", time.Now().Unix()-3600) // 一个小时之前的
query.Gte("minute", fromMinute)
query.Result("minute")
query.Group("minute")
default:
err = errors.New("invalid 'range' value: '" + timeRange + "'")
return
}
_, err = query.Slice(&result).
FindAll()
return
}
// ListValuesForUserNodes 列出用户节点相关的平均数据
func (this *NodeValueDAO) ListValuesForUserNodes(tx *dbs.Tx, item string, key string, timeRange nodeconfigs.NodeValueRange) (result []*NodeValue, err error) {
query := this.Query(tx).
Attr("role", "user").
Attr("item", item).
Result("AVG(JSON_EXTRACT(value, '$." + key + "')) AS value, MIN(createdAt) AS createdAt")
switch timeRange {
// TODO 支持更多的时间范围
case nodeconfigs.NodeValueRangeMinute:
fromMinute := timeutil.FormatTime("YmdHi", time.Now().Unix()-3600) // 一个小时之前的
query.Gte("minute", fromMinute)
query.Result("minute")
query.Group("minute")
default:
err = errors.New("invalid 'range' value: '" + timeRange + "'")
return
}
_, err = query.Slice(&result).
FindAll()
return
}
// ListValuesForNSNodes 列出用户节点相关的平均数据
func (this *NodeValueDAO) ListValuesForNSNodes(tx *dbs.Tx, item string, key string, timeRange nodeconfigs.NodeValueRange) (result []*NodeValue, err error) {
query := this.Query(tx).
Attr("role", "dns").
Attr("item", item).
Result("AVG(JSON_EXTRACT(value, '$." + key + "')) AS value, MIN(createdAt) AS createdAt")
switch timeRange {
// TODO 支持更多的时间范围
case nodeconfigs.NodeValueRangeMinute:
fromMinute := timeutil.FormatTime("YmdHi", time.Now().Unix()-3600) // 一个小时之前的
query.Gte("minute", fromMinute)
query.Result("minute")
query.Group("minute")
default:
err = errors.New("invalid 'range' value: '" + timeRange + "'")
return
}
_, err = query.Slice(&result).
FindAll()
return
}
// SumValues 计算某项参数值
func (this *NodeValueDAO) SumValues(tx *dbs.Tx, role string, nodeId int64, item string, param string, method nodeconfigs.NodeValueSumMethod, duration int32, durationUnit nodeconfigs.NodeValueDurationUnit) (float64, error) {
if duration <= 0 {
@@ -110,11 +185,28 @@ func (this *NodeValueDAO) SumValues(tx *dbs.Tx, role string, nodeId int64, item
}
switch durationUnit {
case nodeconfigs.NodeValueDurationUnitMinute:
fromMinute := timeutil.FormatTime("YmdHi", time.Now().Unix()-int64(duration * 60))
fromMinute := timeutil.FormatTime("YmdHi", time.Now().Unix()-int64(duration*60))
query.Gte("minute", fromMinute)
default:
fromMinute := timeutil.FormatTime("YmdHi", time.Now().Unix()-int64(duration * 60))
fromMinute := timeutil.FormatTime("YmdHi", time.Now().Unix()-int64(duration*60))
query.Gte("minute", fromMinute)
}
return query.FindFloat64Col(0)
}
// FindLatestNodeValue 获取最近一条数据
func (this *NodeValueDAO) FindLatestNodeValue(tx *dbs.Tx, role string, nodeId int64, item string) (*NodeValue, error) {
one, err := this.Query(tx).
Attr("role", role).
Attr("nodeId", nodeId).
Attr("item", item).
DescPk().
Find()
if err != nil {
return nil, err
}
if one == nil {
return nil, nil
}
return one.(*NodeValue), nil
}

View File

@@ -3,6 +3,7 @@ package models
// NodeValue 节点监控数据
type NodeValue struct {
Id uint64 `field:"id"` // ID
ClusterId uint32 `field:"clusterId"` // 集群ID
NodeId uint32 `field:"nodeId"` // 节点ID
Role string `field:"role"` // 节点角色
Item string `field:"item"` // 监控项
@@ -15,6 +16,7 @@ type NodeValue struct {
type NodeValueOperator struct {
Id interface{} // ID
ClusterId interface{} // 集群ID
NodeId interface{} // 节点ID
Role interface{} // 节点角色
Item interface{} // 监控项

View File

@@ -1 +1,19 @@
package models
import (
"encoding/json"
"github.com/iwind/TeaGo/maps"
)
func (this *NodeValue) DecodeMapValue() maps.Map {
if len(this.Value) == 0 {
return maps.Map{}
}
var m = maps.Map{}
err := json.Unmarshal([]byte(this.Value), &m)
if err != nil {
// 忽略错误
return m
}
return m
}

View File

@@ -1,17 +1,36 @@
package models
import (
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
_ "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"
"regexp"
"time"
)
type ServerDailyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
for range ticker.C {
err := SharedServerDailyStatDAO.Clean(nil, 60) // 只保留60天
if err != nil {
logs.Println("ServerDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
}
func NewServerDailyStatDAO() *ServerDailyStatDAO {
return dbs.NewDAO(&ServerDailyStatDAO{
DAOObject: dbs.DAOObject{
@@ -33,24 +52,41 @@ func init() {
// SaveStats 提交数据
func (this *ServerDailyStatDAO) SaveStats(tx *dbs.Tx, stats []*pb.ServerDailyStat) error {
var serverUserMap = map[int64]int64{} // serverId => userId
for _, stat := range stats {
day := timeutil.FormatTime("Ymd", stat.CreatedAt)
hour := timeutil.FormatTime("YmdH", stat.CreatedAt)
timeFrom := timeutil.FormatTime("His", stat.CreatedAt)
timeTo := timeutil.FormatTime("His", stat.CreatedAt+5*60-1) // 5分钟
serverUserId, ok := serverUserMap[stat.ServerId]
if !ok {
userId, err := SharedServerDAO.FindServerUserId(tx, stat.ServerId)
if err != nil {
return err
}
serverUserId = userId
}
_, _, err := this.Query(tx).
Param("bytes", stat.Bytes).
Param("cachedBytes", stat.CachedBytes).
Param("countRequests", stat.CountRequests).
Param("countCachedRequests", stat.CountCachedRequests).
Param("countAttackRequests", stat.CountAttackRequests).
Param("attackBytes", stat.AttackBytes).
InsertOrUpdate(maps.Map{
"userId": serverUserId,
"serverId": stat.ServerId,
"regionId": stat.RegionId,
"bytes": dbs.SQL("bytes+:bytes"),
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
"bytes": stat.Bytes,
"cachedBytes": stat.CachedBytes,
"countRequests": stat.CountRequests,
"countCachedRequests": stat.CountCachedRequests,
"countAttackRequests": stat.CountAttackRequests,
"attackBytes": stat.AttackBytes,
"day": day,
"hour": hour,
"timeFrom": timeFrom,
"timeTo": timeTo,
}, maps.Map{
@@ -58,6 +94,8 @@ func (this *ServerDailyStatDAO) SaveStats(tx *dbs.Tx, stats []*pb.ServerDailySta
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
})
if err != nil {
return err
@@ -74,8 +112,7 @@ func (this *ServerDailyStatDAO) SumUserMonthly(tx *dbs.Tx, userId int64, regionI
query.Attr("regionId", regionId)
}
return query.Between("day", month+"01", month+"32").
Where("serverId IN (SELECT id FROM "+SharedServerDAO.Table+" WHERE userId=:userId)").
Param("userId", userId).
Attr("userId", userId).
SumInt64("bytes", 0)
}
@@ -87,8 +124,7 @@ func (this *ServerDailyStatDAO) SumUserMonthlyPeek(tx *dbs.Tx, userId int64, reg
query.Attr("regionId", regionId)
}
max, err := query.Between("day", month+"01", month+"32").
Where("serverId IN (SELECT id FROM "+SharedServerDAO.Table+" WHERE userId=:userId)").
Param("userId", userId).
Attr("userId", userId).
Max("bytes", 0)
if err != nil {
return 0, err
@@ -105,8 +141,7 @@ func (this *ServerDailyStatDAO) SumUserDaily(tx *dbs.Tx, userId int64, regionId
}
return query.
Attr("day", day).
Where("serverId IN (SELECT id FROM "+SharedServerDAO.Table+" WHERE userId=:userId)").
Param("userId", userId).
Attr("userId", userId).
SumInt64("bytes", 0)
}
@@ -119,8 +154,7 @@ func (this *ServerDailyStatDAO) SumUserDailyPeek(tx *dbs.Tx, userId int64, regio
}
max, err := query.
Attr("day", day).
Where("serverId IN (SELECT id FROM "+SharedServerDAO.Table+" WHERE userId=:userId)").
Param("userId", userId).
Attr("userId", userId).
Max("bytes", 0)
if err != nil {
return 0, err
@@ -138,7 +172,7 @@ func (this *ServerDailyStatDAO) SumMinutelyStat(tx *dbs.Tx, serverId int64, minu
}
one, _, err := this.Query(tx).
Result("SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests").
Result("SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Attr("serverId", serverId).
Attr("day", minute[:8]).
Attr("timeFrom", minute[8:]+"00").
@@ -155,6 +189,8 @@ func (this *ServerDailyStatDAO) SumMinutelyStat(tx *dbs.Tx, serverId int64, minu
stat.CachedBytes = one.GetInt64("cachedBytes")
stat.CountRequests = one.GetInt64("countRequests")
stat.CountCachedRequests = one.GetInt64("countCachedRequests")
stat.CountAttackRequests = one.GetInt64("countAttackRequests")
stat.AttackBytes = one.GetInt64("attackBytes")
return
}
@@ -168,7 +204,7 @@ func (this *ServerDailyStatDAO) SumHourlyStat(tx *dbs.Tx, serverId int64, hour s
}
one, _, err := this.Query(tx).
Result("SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests").
Result("SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Attr("serverId", serverId).
Attr("day", hour[:8]).
Gte("timeFrom", hour[8:]+"0000").
@@ -186,6 +222,8 @@ func (this *ServerDailyStatDAO) SumHourlyStat(tx *dbs.Tx, serverId int64, hour s
stat.CachedBytes = one.GetInt64("cachedBytes")
stat.CountRequests = one.GetInt64("countRequests")
stat.CountCachedRequests = one.GetInt64("countCachedRequests")
stat.CountAttackRequests = one.GetInt64("countAttackRequests")
stat.AttackBytes = one.GetInt64("attackBytes")
return
}
@@ -199,7 +237,7 @@ func (this *ServerDailyStatDAO) SumDailyStat(tx *dbs.Tx, serverId int64, day str
}
one, _, err := this.Query(tx).
Result("SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests").
Result("SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Attr("serverId", serverId).
Attr("day", day).
FindOne()
@@ -215,5 +253,94 @@ func (this *ServerDailyStatDAO) SumDailyStat(tx *dbs.Tx, serverId int64, day str
stat.CachedBytes = one.GetInt64("cachedBytes")
stat.CountRequests = one.GetInt64("countRequests")
stat.CountCachedRequests = one.GetInt64("countCachedRequests")
stat.CountAttackRequests = one.GetInt64("countAttackRequests")
stat.AttackBytes = one.GetInt64("attackBytes")
return
}
// FindDailyStats 按天统计
func (this *ServerDailyStatDAO) FindDailyStats(tx *dbs.Tx, serverId int64, dayFrom string, dayTo string) (result []*ServerDailyStat, err error) {
ones, err := this.Query(tx).
Result("SUM(bytes) AS bytes", "SUM(cachedBytes) AS cachedBytes", "SUM(countRequests) AS countRequests", "SUM(countCachedRequests) AS countCachedRequests", "SUM(countAttackRequests) AS countAttackRequests", "SUM(attackBytes) AS attackBytes", "day").
Attr("serverId", serverId).
Between("day", dayFrom, dayTo).
Group("day").
FindAll()
if err != nil {
return nil, err
}
dayMap := map[string]*ServerDailyStat{} // day => Stat
for _, one := range ones {
stat := one.(*ServerDailyStat)
dayMap[stat.Day] = stat
}
days, err := utils.RangeDays(dayFrom, dayTo)
if err != nil {
return nil, err
}
for _, day := range days {
stat, ok := dayMap[day]
if ok {
result = append(result, stat)
} else {
result = append(result, &ServerDailyStat{Day: day})
}
}
return
}
// FindHourlyStats 按小时统计
func (this *ServerDailyStatDAO) FindHourlyStats(tx *dbs.Tx, serverId int64, hourFrom string, hourTo string) (result []*ServerDailyStat, err error) {
ones, err := this.Query(tx).
Result("SUM(bytes) AS bytes", "SUM(cachedBytes) AS cachedBytes", "SUM(countRequests) AS countRequests", "SUM(countCachedRequests) AS countCachedRequests", "SUM(countAttackRequests) AS countAttackRequests", "SUM(attackBytes) AS attackBytes", "hour").
Attr("serverId", serverId).
Between("hour", hourFrom, hourTo).
Group("hour").
FindAll()
if err != nil {
return nil, err
}
hourMap := map[string]*ServerDailyStat{} // hour => Stat
for _, one := range ones {
stat := one.(*ServerDailyStat)
hourMap[stat.Hour] = stat
}
hours, err := utils.RangeHours(hourFrom, hourTo)
if err != nil {
return nil, err
}
for _, hour := range hours {
stat, ok := hourMap[hour]
if ok {
result = append(result, stat)
} else {
result = append(result, &ServerDailyStat{Hour: hour})
}
}
return
}
// FindTopUserStats 流量排行
func (this *ServerDailyStatDAO) FindTopUserStats(tx *dbs.Tx, hourFrom string, hourTo string) (result []*ServerDailyStat, err error) {
_, err = this.Query(tx).
Result("userId", "SUM(bytes) AS bytes", "SUM(countRequests) AS countRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Between("hour", hourFrom, hourTo).
Where("userId>0").
Group("userId").
Slice(&result).
FindAll()
return
}
// Clean 清理历史数据
func (this *ServerDailyStatDAO) Clean(tx *dbs.Tx, days int) error {
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days))
_, err := this.Query(tx).
Lt("day", day).
Delete()
return err
}

View File

@@ -3,13 +3,17 @@ package models
// ServerDailyStat 计费流量统计
type ServerDailyStat struct {
Id uint64 `field:"id"` // ID
UserId uint32 `field:"userId"` // 用户ID
ServerId uint32 `field:"serverId"` // 服务ID
RegionId uint32 `field:"regionId"` // 区域ID
Bytes uint64 `field:"bytes"` // 流量
CachedBytes uint64 `field:"cachedBytes"` // 缓存的流量
CountRequests uint64 `field:"countRequests"` // 请求数
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存的请求数
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击请求数
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
Day string `field:"day"` // 日期YYYYMMDD
Hour string `field:"hour"` // YYYYMMDDHH
TimeFrom string `field:"timeFrom"` // 开始时间HHMMSS
TimeTo string `field:"timeTo"` // 结束时间
IsCharged uint8 `field:"isCharged"` // 是否已计算费用
@@ -17,13 +21,17 @@ type ServerDailyStat struct {
type ServerDailyStatOperator struct {
Id interface{} // ID
UserId interface{} // 用户ID
ServerId interface{} // 服务ID
RegionId interface{} // 区域ID
Bytes interface{} // 流量
CachedBytes interface{} // 缓存的流量
CountRequests interface{} // 请求数
CountCachedRequests interface{} // 缓存的请求数
CountAttackRequests interface{} // 攻击请求数
AttackBytes interface{} // 攻击流量
Day interface{} // 日期YYYYMMDD
Hour interface{} // YYYYMMDDHH
TimeFrom interface{} // 开始时间HHMMSS
TimeTo interface{} // 结束时间
IsCharged interface{} // 是否已计算费用

View File

@@ -18,6 +18,7 @@ import (
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
"regexp"
"strconv"
"strings"
"time"
@@ -640,8 +641,14 @@ func (this *ServerDAO) CountAllEnabledServersMatch(tx *dbs.Tx, groupId int64, ke
Param("groupId", numberutils.FormatInt64(groupId))
}
if len(keyword) > 0 {
query.Where("(name LIKE :keyword OR serverNames LIKE :keyword)").
Param("keyword", "%"+keyword+"%")
if regexp.MustCompile(`^\d+$`).MatchString(keyword) {
query.Where("(name LIKE :keyword OR serverNames LIKE :keyword OR JSON_CONTAINS(http, :portRange, '$.listen') OR JSON_CONTAINS(https, :portRange, '$.listen') OR JSON_CONTAINS(tcp, :portRange, '$.listen') OR JSON_CONTAINS(tls, :portRange, '$.listen'))").
Param("portRange", maps.Map{"portRange": keyword}.AsJSON()).
Param("keyword", "%"+keyword+"%")
} else {
query.Where("(name LIKE :keyword OR serverNames LIKE :keyword)").
Param("keyword", "%"+keyword+"%")
}
}
if userId > 0 {
query.Attr("userId", userId)
@@ -674,8 +681,13 @@ func (this *ServerDAO) ListEnabledServersMatch(tx *dbs.Tx, offset int64, size in
Param("groupId", numberutils.FormatInt64(groupId))
}
if len(keyword) > 0 {
query.Where("(name LIKE :keyword OR serverNames LIKE :keyword)").
Param("keyword", "%"+keyword+"%")
if regexp.MustCompile(`^\d+$`).MatchString(keyword) {
query.Where("(name LIKE :keyword OR serverNames LIKE :keyword OR JSON_CONTAINS(http, :portRange, '$.listen') OR JSON_CONTAINS(https, :portRange, '$.listen') OR JSON_CONTAINS(tcp, :portRange, '$.listen') OR JSON_CONTAINS(tls, :portRange, '$.listen'))").
Param("portRange", string(maps.Map{"portRange": keyword}.AsJSON()))
} else {
query.Where("(name LIKE :keyword OR serverNames LIKE :keyword)").
Param("keyword", "%"+keyword+"%")
}
}
if userId > 0 {
query.Attr("userId", userId)
@@ -1186,6 +1198,18 @@ func (this *ServerDAO) FindServerAdminIdAndUserId(tx *dbs.Tx, serverId int64) (a
return int64(one.(*Server).AdminId), int64(one.(*Server).UserId), nil
}
// FindServerUserId 查找服务的用户ID
func (this *ServerDAO) FindServerUserId(tx *dbs.Tx, serverId int64) (userId int64, err error) {
one, _, err := this.Query(tx).
Pk(serverId).
Result("userId").
FindOne()
if err != nil || one == nil {
return 0, err
}
return one.GetInt64("userId"), nil
}
// CheckUserServer 检查用户服务
func (this *ServerDAO) CheckUserServer(tx *dbs.Tx, userId int64, serverId int64) error {
if serverId <= 0 || userId <= 0 {

View File

@@ -0,0 +1,93 @@
package models
import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
)
const (
ServerStatBoardChartStateEnabled = 1 // 已启用
ServerStatBoardChartStateDisabled = 0 // 已禁用
)
type ServerStatBoardChartDAO dbs.DAO
func NewServerStatBoardChartDAO() *ServerStatBoardChartDAO {
return dbs.NewDAO(&ServerStatBoardChartDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeServerStatBoardCharts",
Model: new(ServerStatBoardChart),
PkName: "id",
},
}).(*ServerStatBoardChartDAO)
}
var SharedServerStatBoardChartDAO *ServerStatBoardChartDAO
func init() {
dbs.OnReady(func() {
SharedServerStatBoardChartDAO = NewServerStatBoardChartDAO()
})
}
// EnableServerStatBoardChart 启用条目
func (this *ServerStatBoardChartDAO) EnableServerStatBoardChart(tx *dbs.Tx, id uint64) error {
_, err := this.Query(tx).
Pk(id).
Set("state", ServerStatBoardChartStateEnabled).
Update()
return err
}
// DisableServerStatBoardChart 禁用条目
func (this *ServerStatBoardChartDAO) DisableServerStatBoardChart(tx *dbs.Tx, id uint64) error {
_, err := this.Query(tx).
Pk(id).
Set("state", ServerStatBoardChartStateDisabled).
Update()
return err
}
// FindEnabledServerStatBoardChart 查找启用中的条目
func (this *ServerStatBoardChartDAO) FindEnabledServerStatBoardChart(tx *dbs.Tx, id uint64) (*ServerStatBoardChart, error) {
result, err := this.Query(tx).
Pk(id).
Attr("state", ServerStatBoardChartStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*ServerStatBoardChart), err
}
// EnableChart 启用图表
func (this *ServerStatBoardChartDAO) EnableChart(tx *dbs.Tx, boardId int64, chartId int64) error {
op := NewServerStatBoardChartOperator()
op.BoardId = boardId
op.ChartId = chartId
op.State = ServerStatBoardChartStateEnabled
return this.Save(tx, op)
}
// DisableChart 禁用图表
func (this *ServerStatBoardChartDAO) DisableChart(tx *dbs.Tx, boardId int64, chartId int64) error {
return this.Query(tx).
Attr("borderId", boardId).
Attr("chartId", chartId).
Set("state", ServerStatBoardChartStateDisabled).
UpdateQuickly()
}
// FindAllEnabledCharts 查找看板中所有图表
func (this *ServerStatBoardChartDAO) FindAllEnabledCharts(tx *dbs.Tx, boardId int64) (result []*ServerStatBoardChart, err error) {
_, err = this.Query(tx).
Attr("boardId", boardId).
Desc("order").
AscPk().
State(ServerStatBoardChartStateEnabled).
Slice(&result).
FindAll()
return
}

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,26 @@
package models
// ServerStatBoardChart 服务看板中的图表
type ServerStatBoardChart struct {
Id uint64 `field:"id"` // ID
BoardId uint64 `field:"boardId"` // 看板ID
Code string `field:"code"` // 内置图表代码
ItemId uint32 `field:"itemId"` // 指标ID
ChartId uint32 `field:"chartId"` // 图表ID
Order uint32 `field:"order"` // 排序
State uint8 `field:"state"` // 状态
}
type ServerStatBoardChartOperator struct {
Id interface{} // ID
BoardId interface{} // 看板ID
Code interface{} // 内置图表代码
ItemId interface{} // 指标ID
ChartId interface{} // 图表ID
Order interface{} // 排序
State interface{} // 状态
}
func NewServerStatBoardChartOperator() *ServerStatBoardChartOperator {
return &ServerStatBoardChartOperator{}
}

View File

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

View File

@@ -0,0 +1,83 @@
package models
import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
)
const (
ServerStatBoardStateEnabled = 1 // 已启用
ServerStatBoardStateDisabled = 0 // 已禁用
)
type ServerStatBoardDAO dbs.DAO
func NewServerStatBoardDAO() *ServerStatBoardDAO {
return dbs.NewDAO(&ServerStatBoardDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeServerStatBoards",
Model: new(ServerStatBoard),
PkName: "id",
},
}).(*ServerStatBoardDAO)
}
var SharedServerStatBoardDAO *ServerStatBoardDAO
func init() {
dbs.OnReady(func() {
SharedServerStatBoardDAO = NewServerStatBoardDAO()
})
}
// EnableServerStatBoard 启用条目
func (this *ServerStatBoardDAO) EnableServerStatBoard(tx *dbs.Tx, id uint64) error {
_, err := this.Query(tx).
Pk(id).
Set("state", ServerStatBoardStateEnabled).
Update()
return err
}
// DisableServerStatBoard 禁用条目
func (this *ServerStatBoardDAO) DisableServerStatBoard(tx *dbs.Tx, id uint64) error {
_, err := this.Query(tx).
Pk(id).
Set("state", ServerStatBoardStateDisabled).
Update()
return err
}
// FindEnabledServerStatBoard 查找启用中的条目
func (this *ServerStatBoardDAO) FindEnabledServerStatBoard(tx *dbs.Tx, id uint64) (*ServerStatBoard, error) {
result, err := this.Query(tx).
Pk(id).
Attr("state", ServerStatBoardStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*ServerStatBoard), err
}
// FindServerStatBoardName 根据主键查找名称
func (this *ServerStatBoardDAO) FindServerStatBoardName(tx *dbs.Tx, id uint64) (string, error) {
return this.Query(tx).
Pk(id).
Result("name").
FindStringCol("")
}
// FindAllEnabledBoards 查找看板
func (this *ServerStatBoardDAO) FindAllEnabledBoards(tx *dbs.Tx, clusterId int64) (result []*ServerStatBoard, err error) {
_, err = this.Query(tx).
Attr("clusterId", clusterId).
State(ServerStatBoardStateEnabled).
Slice(&result).
Desc("order").
AscPk().
FindAll()
return
}

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,24 @@
package models
// ServerStatBoard 服务统计看板
type ServerStatBoard struct {
Id uint64 `field:"id"` // ID
Name string `field:"name"` // 名称
ClusterId uint32 `field:"clusterId"` // 集群ID
IsOn uint8 `field:"isOn"` // 是否启用
Order uint32 `field:"order"` // 排序
State uint8 `field:"state"` // 状态
}
type ServerStatBoardOperator struct {
Id interface{} // ID
Name interface{} // 名称
ClusterId interface{} // 集群ID
IsOn interface{} // 是否启用
Order interface{} // 排序
State interface{} // 状态
}
func NewServerStatBoardOperator() *ServerStatBoardOperator {
return &ServerStatBoardOperator{}
}

View File

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

View File

@@ -2,14 +2,34 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
_ "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"
)
type NodeClusterTrafficDailyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
for range ticker.C {
err := SharedNodeClusterTrafficDailyStatDAO.Clean(nil, 60) // 只保留60天
if err != nil {
remotelogs.Error("NodeClusterTrafficDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
}
func NewNodeClusterTrafficDailyStatDAO() *NodeClusterTrafficDailyStatDAO {
return dbs.NewDAO(&NodeClusterTrafficDailyStatDAO{
DAOObject: dbs.DAOObject{
@@ -29,22 +49,75 @@ func init() {
})
}
// 增加流量
func (this *NodeClusterTrafficDailyStatDAO) IncreaseDailyBytes(tx *dbs.Tx, clusterId int64, day string, bytes int64) error {
// IncreaseDailyStat 增加统计数据
func (this *NodeClusterTrafficDailyStatDAO) IncreaseDailyStat(tx *dbs.Tx, clusterId int64, day string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests int64, countAttackRequests int64, attackBytes int64) error {
if len(day) != 8 {
return errors.New("invalid day '" + day + "'")
}
err := this.Query(tx).
Param("bytes", bytes).
Param("cachedBytes", cachedBytes).
Param("countRequests", countRequests).
Param("countCachedRequests", countCachedRequests).
Param("countAttackRequests", countAttackRequests).
Param("attackBytes", attackBytes).
InsertOrUpdateQuickly(maps.Map{
"clusterId": clusterId,
"day": day,
"bytes": bytes,
"clusterId": clusterId,
"day": day,
"bytes": bytes,
"cachedBytes": cachedBytes,
"countRequests": countRequests,
"countCachedRequests": countCachedRequests,
"countAttackRequests": countAttackRequests,
"attackBytes": attackBytes,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"bytes": dbs.SQL("bytes+:bytes"),
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
})
if err != nil {
return err
}
return nil
}
// FindDailyStats 获取日期之间统计
func (this *NodeClusterTrafficDailyStatDAO) FindDailyStats(tx *dbs.Tx, clusterId int64, dayFrom string, dayTo string) (result []*NodeClusterTrafficDailyStat, err error) {
ones, err := this.Query(tx).
Attr("clusterId", clusterId).
Between("day", dayFrom, dayTo).
FindAll()
if err != nil {
return nil, err
}
dayMap := map[string]*NodeClusterTrafficDailyStat{} // day => Stat
for _, one := range ones {
stat := one.(*NodeClusterTrafficDailyStat)
dayMap[stat.Day] = stat
}
days, err := utils.RangeDays(dayFrom, dayTo)
if err != nil {
return nil, err
}
for _, day := range days {
stat, ok := dayMap[day]
if ok {
result = append(result, stat)
} else {
result = append(result, &NodeClusterTrafficDailyStat{Day: day})
}
}
return result, nil
}
// Clean 清理历史数据
func (this *NodeClusterTrafficDailyStatDAO) Clean(tx *dbs.Tx, days int) error {
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days))
_, err := this.Query(tx).
Lt("day", day).
Delete()
return err
}

View File

@@ -1,18 +1,28 @@
package stats
// 总的流量统计(按天)
// NodeClusterTrafficDailyStat 总的流量统计(按天)
type NodeClusterTrafficDailyStat struct {
Id uint64 `field:"id"` // ID
ClusterId uint32 `field:"clusterId"` // 集群ID
Day string `field:"day"` // YYYYMMDD
Bytes uint64 `field:"bytes"` // 流量字节
Id uint64 `field:"id"` // ID
ClusterId uint32 `field:"clusterId"` // 集群ID
Day string `field:"day"` // YYYYMMDD
Bytes uint64 `field:"bytes"` // 流量字节
CachedBytes uint64 `field:"cachedBytes"` // 缓存流量
CountRequests uint64 `field:"countRequests"` // 请求数
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存的请求数
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击请求数
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
}
type NodeClusterTrafficDailyStatOperator struct {
Id interface{} // ID
ClusterId interface{} // 集群ID
Day interface{} // YYYYMMDD
Bytes interface{} // 流量字节
Id interface{} // ID
ClusterId interface{} // 集群ID
Day interface{} // YYYYMMDD
Bytes interface{} // 流量字节
CachedBytes interface{} // 缓存流量
CountRequests interface{} // 请求数
CountCachedRequests interface{} // 缓存的请求数
CountAttackRequests interface{} // 攻击请求数
AttackBytes interface{} // 攻击流量
}
func NewNodeClusterTrafficDailyStatOperator() *NodeClusterTrafficDailyStatOperator {

View File

@@ -2,14 +2,34 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
_ "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"
)
type NodeTrafficDailyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
for range ticker.C {
err := SharedNodeTrafficDailyStatDAO.Clean(nil, 60) // 只保留60天
if err != nil {
remotelogs.Error("NodeTrafficDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
}
func NewNodeTrafficDailyStatDAO() *NodeTrafficDailyStatDAO {
return dbs.NewDAO(&NodeTrafficDailyStatDAO{
DAOObject: dbs.DAOObject{
@@ -29,22 +49,78 @@ func init() {
})
}
// 增加流量
func (this *NodeTrafficDailyStatDAO) IncreaseDailyBytes(tx *dbs.Tx, nodeId int64, day string, bytes int64) error {
// IncreaseDailyStat 增加统计数据
func (this *NodeTrafficDailyStatDAO) IncreaseDailyStat(tx *dbs.Tx, clusterId int64, role string, nodeId int64, day string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests int64, countAttackRequests int64, attackBytes int64) error {
if len(day) != 8 {
return errors.New("invalid day '" + day + "'")
}
err := this.Query(tx).
Param("bytes", bytes).
Param("cachedBytes", cachedBytes).
Param("countRequests", countRequests).
Param("countCachedRequests", countCachedRequests).
Param("countAttackRequests", countAttackRequests).
Param("attackBytes", attackBytes).
InsertOrUpdateQuickly(maps.Map{
"nodeId": nodeId,
"day": day,
"bytes": bytes,
"clusterId": clusterId,
"role": role,
"nodeId": nodeId,
"day": day,
"bytes": bytes,
"cachedBytes": cachedBytes,
"countRequests": countRequests,
"countCachedRequests": countCachedRequests,
"countAttackRequests": countAttackRequests,
"attackBytes": attackBytes,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"bytes": dbs.SQL("bytes+:bytes"),
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
})
if err != nil {
return err
}
return nil
}
// FindDailyStats 获取日期之间统计
func (this *NodeTrafficDailyStatDAO) FindDailyStats(tx *dbs.Tx, role string, nodeId int64, dayFrom string, dayTo string) (result []*NodeTrafficDailyStat, err error) {
ones, err := this.Query(tx).
Attr("nodeId", nodeId).
Attr("role", role).
Between("day", dayFrom, dayTo).
FindAll()
if err != nil {
return nil, err
}
dayMap := map[string]*NodeTrafficDailyStat{} // day => Stat
for _, one := range ones {
stat := one.(*NodeTrafficDailyStat)
dayMap[stat.Day] = stat
}
days, err := utils.RangeDays(dayFrom, dayTo)
if err != nil {
return nil, err
}
for _, day := range days {
stat, ok := dayMap[day]
if ok {
result = append(result, stat)
} else {
result = append(result, &NodeTrafficDailyStat{Day: day})
}
}
return result, nil
}
// Clean 清理历史数据
func (this *NodeTrafficDailyStatDAO) Clean(tx *dbs.Tx, days int) error {
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days))
_, err := this.Query(tx).
Lt("day", day).
Delete()
return err
}

View File

@@ -1,18 +1,32 @@
package stats
// 总的流量统计(按天)
// NodeTrafficDailyStat 总的流量统计(按天)
type NodeTrafficDailyStat struct {
Id uint64 `field:"id"` // ID
NodeId uint32 `field:"nodeId"` // 集群ID
Day string `field:"day"` // YYYYMMDD
Bytes uint64 `field:"bytes"` // 流量字节
Id uint64 `field:"id"` // ID
Role string `field:"role"` // 节点角色
ClusterId uint32 `field:"clusterId"` // 集群ID
NodeId uint32 `field:"nodeId"` // 集群ID
Day string `field:"day"` // YYYYMMDD
Bytes uint64 `field:"bytes"` // 流量字节
CachedBytes uint64 `field:"cachedBytes"` // 缓存流量
CountRequests uint64 `field:"countRequests"` // 请求数
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存的请求数
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击数
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
}
type NodeTrafficDailyStatOperator struct {
Id interface{} // ID
NodeId interface{} // 集群ID
Day interface{} // YYYYMMDD
Bytes interface{} // 流量字节
Id interface{} // ID
Role interface{} // 节点角色
ClusterId interface{} // 集群ID
NodeId interface{} // 集群ID
Day interface{} // YYYYMMDD
Bytes interface{} // 流量字节
CachedBytes interface{} // 缓存流量
CountRequests interface{} // 请求数
CountCachedRequests interface{} // 缓存的请求数
CountAttackRequests interface{} // 攻击数
AttackBytes interface{} // 攻击流量
}
func NewNodeTrafficDailyStatOperator() *NodeTrafficDailyStatOperator {

View File

@@ -0,0 +1,188 @@
package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
_ "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"
)
type NodeTrafficHourlyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
for range ticker.C {
err := SharedNodeTrafficHourlyStatDAO.Clean(nil, 60) // 只保留60天
if err != nil {
remotelogs.Error("NodeTrafficHourlyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
}
func NewNodeTrafficHourlyStatDAO() *NodeTrafficHourlyStatDAO {
return dbs.NewDAO(&NodeTrafficHourlyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeNodeTrafficHourlyStats",
Model: new(NodeTrafficHourlyStat),
PkName: "id",
},
}).(*NodeTrafficHourlyStatDAO)
}
var SharedNodeTrafficHourlyStatDAO *NodeTrafficHourlyStatDAO
func init() {
dbs.OnReady(func() {
SharedNodeTrafficHourlyStatDAO = NewNodeTrafficHourlyStatDAO()
})
}
// IncreaseHourlyStat 增加统计数据
func (this *NodeTrafficHourlyStatDAO) IncreaseHourlyStat(tx *dbs.Tx, clusterId int64, role string, nodeId int64, hour string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests int64, countAttackRequests int64, attackBytes int64) error {
if len(hour) != 10 {
return errors.New("invalid hour '" + hour + "'")
}
err := this.Query(tx).
Param("bytes", bytes).
Param("cachedBytes", cachedBytes).
Param("countRequests", countRequests).
Param("countCachedRequests", countCachedRequests).
Param("countAttackRequests", countAttackRequests).
Param("attackBytes", attackBytes).
InsertOrUpdateQuickly(maps.Map{
"clusterId": clusterId,
"role": role,
"nodeId": nodeId,
"hour": hour,
"bytes": bytes,
"cachedBytes": cachedBytes,
"countRequests": countRequests,
"countCachedRequests": countCachedRequests,
"countAttackRequests": countAttackRequests,
"attackBytes": attackBytes,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
})
if err != nil {
return err
}
return nil
}
// FindHourlyStatsWithClusterId 获取小时之间统计
func (this *NodeTrafficHourlyStatDAO) FindHourlyStatsWithClusterId(tx *dbs.Tx, clusterId int64, hourFrom string, hourTo string) (result []*NodeTrafficHourlyStat, err error) {
ones, err := this.Query(tx).
Attr("clusterId", clusterId).
Between("hour", hourFrom, hourTo).
Result("hour, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Group("hour").
FindAll()
if err != nil {
return nil, err
}
hourMap := map[string]*NodeTrafficHourlyStat{} // hour => Stat
for _, one := range ones {
stat := one.(*NodeTrafficHourlyStat)
hourMap[stat.Hour] = stat
}
hours, err := utils.RangeHours(hourFrom, hourTo)
if err != nil {
return nil, err
}
for _, hour := range hours {
stat, ok := hourMap[hour]
if ok {
result = append(result, stat)
} else {
result = append(result, &NodeTrafficHourlyStat{Hour: hour})
}
}
return result, nil
}
// FindTopNodeStats 取得一定时间内的节点排行数据
func (this *NodeTrafficHourlyStatDAO) FindTopNodeStats(tx *dbs.Tx, role string, hourFrom string, hourTo string) (result []*NodeTrafficHourlyStat, err error) {
// TODO 节点如果已经被删除,则忽略
_, err = this.Query(tx).
Attr("role", role).
Between("hour", hourFrom, hourTo).
Result("nodeId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Group("nodeId").
Desc("countRequests").
Slice(&result).
FindAll()
return
}
// FindTopNodeStatsWithClusterId 取得集群一定时间内的节点排行数据
func (this *NodeTrafficHourlyStatDAO) FindTopNodeStatsWithClusterId(tx *dbs.Tx, role string, clusterId int64, hourFrom string, hourTo string) (result []*NodeTrafficHourlyStat, err error) {
// TODO 节点如果已经被删除,则忽略
_, err = this.Query(tx).
Attr("role", role).
Attr("clusterId", clusterId).
Between("hour", hourFrom, hourTo).
Result("nodeId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Group("nodeId").
Desc("countRequests").
Slice(&result).
FindAll()
return
}
// FindHourlyStatsWithNodeId 获取节点小时之间统计
func (this *NodeTrafficHourlyStatDAO) FindHourlyStatsWithNodeId(tx *dbs.Tx, role string, nodeId int64, hourFrom string, hourTo string) (result []*NodeTrafficHourlyStat, err error) {
ones, err := this.Query(tx).
Attr("role", role).
Attr("nodeId", nodeId).
Between("hour", hourFrom, hourTo).
Result("hour, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Group("hour").
FindAll()
if err != nil {
return nil, err
}
hourMap := map[string]*NodeTrafficHourlyStat{} // hour => Stat
for _, one := range ones {
stat := one.(*NodeTrafficHourlyStat)
hourMap[stat.Hour] = stat
}
hours, err := utils.RangeHours(hourFrom, hourTo)
if err != nil {
return nil, err
}
for _, hour := range hours {
stat, ok := hourMap[hour]
if ok {
result = append(result, stat)
} else {
result = append(result, &NodeTrafficHourlyStat{Hour: hour})
}
}
return result, nil
}
// Clean 清理历史数据
func (this *NodeTrafficHourlyStatDAO) Clean(tx *dbs.Tx, days int) error {
var hour = timeutil.Format("Ymd00", time.Now().AddDate(0, 0, -days))
_, err := this.Query(tx).
Lt("hour", hour).
Delete()
return err
}

View File

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

View File

@@ -0,0 +1,34 @@
package stats
// NodeTrafficHourlyStat 总的流量统计(按天)
type NodeTrafficHourlyStat struct {
Id uint64 `field:"id"` // ID
Role string `field:"role"` // 节点角色
ClusterId uint32 `field:"clusterId"` // 集群ID
NodeId uint32 `field:"nodeId"` // 集群ID
Hour string `field:"hour"` // YYYYMMDDHH
Bytes uint64 `field:"bytes"` // 流量字节
CachedBytes uint64 `field:"cachedBytes"` // 缓存流量
CountRequests uint64 `field:"countRequests"` // 请求数
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存的请求数
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击请求数
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
}
type NodeTrafficHourlyStatOperator struct {
Id interface{} // ID
Role interface{} // 节点角色
ClusterId interface{} // 集群ID
NodeId interface{} // 集群ID
Hour interface{} // YYYYMMDDHH
Bytes interface{} // 流量字节
CachedBytes interface{} // 缓存流量
CountRequests interface{} // 请求数
CountCachedRequests interface{} // 缓存的请求数
CountAttackRequests interface{} // 攻击请求数
AttackBytes interface{} // 攻击流量
}
func NewNodeTrafficHourlyStatOperator() *NodeTrafficHourlyStatOperator {
return &NodeTrafficHourlyStatOperator{}
}

View File

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

View File

@@ -0,0 +1,155 @@
package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"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"
)
type ServerDomainHourlyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
for range ticker.C {
err := SharedServerDomainHourlyStatDAO.Clean(nil, 60) // 只保留60天
if err != nil {
remotelogs.Error("ServerDomainHourlyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
}
func NewServerDomainHourlyStatDAO() *ServerDomainHourlyStatDAO {
return dbs.NewDAO(&ServerDomainHourlyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeServerDomainHourlyStats",
Model: new(ServerDomainHourlyStat),
PkName: "id",
},
}).(*ServerDomainHourlyStatDAO)
}
var SharedServerDomainHourlyStatDAO *ServerDomainHourlyStatDAO
func init() {
dbs.OnReady(func() {
SharedServerDomainHourlyStatDAO = NewServerDomainHourlyStatDAO()
})
}
// IncreaseHourlyStat 增加统计数据
func (this *ServerDomainHourlyStatDAO) IncreaseHourlyStat(tx *dbs.Tx, clusterId int64, nodeId int64, serverId int64, domain string, hour string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests int64, countAttackRequests int64, attackBytes int64) error {
if len(hour) != 10 {
return errors.New("invalid hour '" + hour + "'")
}
err := this.Query(tx).
Param("bytes", bytes).
Param("cachedBytes", cachedBytes).
Param("countRequests", countRequests).
Param("countCachedRequests", countCachedRequests).
Param("countAttackRequests", countAttackRequests).
Param("attackBytes", attackBytes).
InsertOrUpdateQuickly(maps.Map{
"clusterId": clusterId,
"nodeId": nodeId,
"serverId": serverId,
"hour": hour,
"domain": domain,
"bytes": bytes,
"cachedBytes": cachedBytes,
"countRequests": countRequests,
"countCachedRequests": countCachedRequests,
"countAttackRequests": countAttackRequests,
"attackBytes": attackBytes,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
})
if err != nil {
return err
}
return nil
}
// FindTopDomainStats 取得一定时间内的域名排行数据
func (this *ServerDomainHourlyStatDAO) FindTopDomainStats(tx *dbs.Tx, hourFrom string, hourTo string, size int64) (result []*ServerDomainHourlyStat, err error) {
// TODO 节点如果已经被删除,则忽略
_, err = this.Query(tx).
Between("hour", hourFrom, hourTo).
Result("domain, MIN(serverId) AS serverId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Group("domain").
Desc("countRequests").
Limit(size).
Slice(&result).
FindAll()
return
}
// FindTopDomainStatsWithClusterId 取得集群上的一定时间内的域名排行数据
func (this *ServerDomainHourlyStatDAO) FindTopDomainStatsWithClusterId(tx *dbs.Tx, clusterId int64, hourFrom string, hourTo string, size int64) (result []*ServerDomainHourlyStat, err error) {
// TODO 节点如果已经被删除,则忽略
_, err = this.Query(tx).
Attr("clusterId", clusterId).
Between("hour", hourFrom, hourTo).
Result("domain, MIN(serverId) AS serverId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Group("domain").
Desc("countRequests").
Limit(size).
Slice(&result).
FindAll()
return
}
// FindTopDomainStatsWithNodeId 取得节点上的一定时间内的域名排行数据
func (this *ServerDomainHourlyStatDAO) FindTopDomainStatsWithNodeId(tx *dbs.Tx, nodeId int64, hourFrom string, hourTo string, size int64) (result []*ServerDomainHourlyStat, err error) {
// TODO 节点如果已经被删除,则忽略
_, err = this.Query(tx).
Attr("nodeId", nodeId).
Between("hour", hourFrom, hourTo).
Result("domain, MIN(serverId) AS serverId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Group("domain").
Desc("countRequests").
Limit(size).
Slice(&result).
FindAll()
return
}
// FindTopDomainStatsWithServerId 取得某个服务的一定时间内的域名排行数据
func (this *ServerDomainHourlyStatDAO) FindTopDomainStatsWithServerId(tx *dbs.Tx, serverId int64, hourFrom string, hourTo string, size int64) (result []*ServerDomainHourlyStat, err error) {
// TODO 节点如果已经被删除,则忽略
_, err = this.Query(tx).
Attr("serverId", serverId).
Between("hour", hourFrom, hourTo).
Result("domain, MIN(serverId) AS serverId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Group("domain").
Desc("countRequests").
Limit(size).
Slice(&result).
FindAll()
return
}
// Clean 清理历史数据
func (this *ServerDomainHourlyStatDAO) Clean(tx *dbs.Tx, days int) error {
var hour = timeutil.Format("Ymd00", time.Now().AddDate(0, 0, -days))
_, err := this.Query(tx).
Lt("hour", hour).
Delete()
return err
}

View File

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

View File

@@ -0,0 +1,36 @@
package stats
// ServerDomainHourlyStat 服务域名统计
type ServerDomainHourlyStat struct {
Id uint64 `field:"id"` // ID
ClusterId uint32 `field:"clusterId"` // 集群ID
NodeId uint32 `field:"nodeId"` // 节点ID
ServerId uint32 `field:"serverId"` // 服务ID
Domain string `field:"domain"` // 域名
Hour string `field:"hour"` // YYYYMMDDHH
Bytes uint64 `field:"bytes"` // 流量
CachedBytes uint64 `field:"cachedBytes"` // 缓存流量
CountRequests uint64 `field:"countRequests"` // 请求数
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存请求
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击请求数
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
}
type ServerDomainHourlyStatOperator struct {
Id interface{} // ID
ClusterId interface{} // 集群ID
NodeId interface{} // 节点ID
ServerId interface{} // 服务ID
Domain interface{} // 域名
Hour interface{} // YYYYMMDDHH
Bytes interface{} // 流量
CachedBytes interface{} // 缓存流量
CountRequests interface{} // 请求数
CountCachedRequests interface{} // 缓存请求
CountAttackRequests interface{} // 攻击请求数
AttackBytes interface{} // 攻击流量
}
func NewServerDomainHourlyStatOperator() *ServerDomainHourlyStatOperator {
return &ServerDomainHourlyStatOperator{}
}

View File

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

View File

@@ -3,14 +3,33 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"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"
)
type ServerHTTPFirewallDailyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
for range ticker.C {
err := SharedServerHTTPFirewallDailyStatDAO.Clean(nil, 60) // 只保留60天
if err != nil {
remotelogs.Error("ServerHTTPFirewallDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
}
func NewServerHTTPFirewallDailyStatDAO() *ServerHTTPFirewallDailyStatDAO {
return dbs.NewDAO(&ServerHTTPFirewallDailyStatDAO{
DAOObject: dbs.DAOObject{
@@ -30,7 +49,7 @@ func init() {
})
}
// 增加数量
// IncreaseDailyCount 增加数量
func (this *ServerHTTPFirewallDailyStatDAO) IncreaseDailyCount(tx *dbs.Tx, serverId int64, firewallRuleGroupId int64, action string, day string, count int64) error {
if len(day) != 8 {
return errors.New("invalid day '" + day + "'")
@@ -52,7 +71,7 @@ func (this *ServerHTTPFirewallDailyStatDAO) IncreaseDailyCount(tx *dbs.Tx, serve
return nil
}
// 计算某天的数据
// SumDailyCount 计算某天的数据
func (this *ServerHTTPFirewallDailyStatDAO) SumDailyCount(tx *dbs.Tx, userId int64, serverId int64, action string, dayFrom string, dayTo string) (int64, error) {
query := this.Query(tx).
Between("day", dayFrom, dayTo)
@@ -68,7 +87,7 @@ func (this *ServerHTTPFirewallDailyStatDAO) SumDailyCount(tx *dbs.Tx, userId int
return query.SumInt64("count", 0)
}
// 查询规则分组数量
// GroupDailyCount 查询规则分组数量
func (this *ServerHTTPFirewallDailyStatDAO) GroupDailyCount(tx *dbs.Tx, userId int64, serverId int64, dayFrom string, dayTo string, offset int64, size int64) (result []*ServerHTTPFirewallDailyStat, err error) {
query := this.Query(tx).
Between("day", dayFrom, dayTo)
@@ -88,7 +107,7 @@ func (this *ServerHTTPFirewallDailyStatDAO) GroupDailyCount(tx *dbs.Tx, userId i
return
}
// 查询某个日期段内的记录
// FindDailyStats 查询某个日期段内的记录
func (this *ServerHTTPFirewallDailyStatDAO) FindDailyStats(tx *dbs.Tx, userId int64, serverId int64, action string, dayFrom string, dayTo string) (result []*ServerHTTPFirewallDailyStat, err error) {
query := this.Query(tx).
Between("day", dayFrom, dayTo).
@@ -105,3 +124,13 @@ func (this *ServerHTTPFirewallDailyStatDAO) FindDailyStats(tx *dbs.Tx, userId in
FindAll()
return
}
// Clean 清理历史数据
func (this *ServerHTTPFirewallDailyStatDAO) Clean(tx *dbs.Tx, days int) error {
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days))
_, err := this.Query(tx).
Lt("day", day).
Delete()
return err
}

View File

@@ -0,0 +1,136 @@
package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"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"
)
type ServerHTTPFirewallHourlyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
for range ticker.C {
err := SharedServerHTTPFirewallHourlyStatDAO.Clean(nil, 60) // 只保留60天
if err != nil {
remotelogs.Error("ServerHTTPFirewallHourlyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
}
func NewServerHTTPFirewallHourlyStatDAO() *ServerHTTPFirewallHourlyStatDAO {
return dbs.NewDAO(&ServerHTTPFirewallHourlyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeServerHTTPFirewallHourlyStats",
Model: new(ServerHTTPFirewallHourlyStat),
PkName: "id",
},
}).(*ServerHTTPFirewallHourlyStatDAO)
}
var SharedServerHTTPFirewallHourlyStatDAO *ServerHTTPFirewallHourlyStatDAO
func init() {
dbs.OnReady(func() {
SharedServerHTTPFirewallHourlyStatDAO = NewServerHTTPFirewallHourlyStatDAO()
})
}
// IncreaseHourlyCount 增加数量
func (this *ServerHTTPFirewallHourlyStatDAO) IncreaseHourlyCount(tx *dbs.Tx, serverId int64, firewallRuleGroupId int64, action string, hour string, count int64) error {
if len(hour) != 10 {
return errors.New("invalid hour '" + hour + "'")
}
err := this.Query(tx).
Param("count", count).
InsertOrUpdateQuickly(maps.Map{
"serverId": serverId,
"day": hour[:8],
"hour": hour,
"httpFirewallRuleGroupId": firewallRuleGroupId,
"action": action,
"count": count,
}, maps.Map{
"count": dbs.SQL("count+:count"),
})
if err != nil {
return err
}
return nil
}
// SumHourlyCount 计算某天的数据
func (this *ServerHTTPFirewallHourlyStatDAO) SumHourlyCount(tx *dbs.Tx, userId int64, serverId int64, action string, dayFrom string, dayTo string) (int64, error) {
query := this.Query(tx).
Between("day", dayFrom, dayTo)
if serverId > 0 {
query.Attr("serverId", serverId)
} else if userId > 0 {
query.Where("serverId IN (SELECT id FROM "+models.SharedServerDAO.Table+" WHERE userId=:userId AND state=1)").
Param("userId", userId)
}
if len(action) > 0 {
query.Attr("action", action)
}
return query.SumInt64("count", 0)
}
// GroupHourlyCount 查询规则分组数量
func (this *ServerHTTPFirewallHourlyStatDAO) GroupHourlyCount(tx *dbs.Tx, userId int64, serverId int64, dayFrom string, dayTo string, offset int64, size int64) (result []*ServerHTTPFirewallHourlyStat, err error) {
query := this.Query(tx).
Between("day", dayFrom, dayTo)
if serverId > 0 {
query.Attr("serverId", serverId)
} else if userId > 0 {
query.Where("serverId IN (SELECT id FROM "+models.SharedServerDAO.Table+" WHERE userId=:userId AND state=1)").
Param("userId", userId)
}
_, err = query.Group("httpFirewallRuleGroupId").
Result("httpFirewallRuleGroupId, SUM(count) AS count").
Desc("count").
Offset(offset).
Limit(size).
Slice(&result).
FindAll()
return
}
// FindHourlyStats 查询某个日期段内的记录
func (this *ServerHTTPFirewallHourlyStatDAO) FindHourlyStats(tx *dbs.Tx, userId int64, serverId int64, action string, hourFrom string, hourTo string) (result []*ServerHTTPFirewallHourlyStat, err error) {
query := this.Query(tx).
Between("hour", hourFrom, hourTo).
Attr("action", action)
if serverId > 0 {
query.Attr("serverId", serverId)
} else if userId > 0 {
query.Where("serverId IN (SELECT id FROM "+models.SharedServerDAO.Table+" WHERE userId=:userId AND state=1)").
Param("userId", userId)
}
_, err = query.Group("hour").
Result("hour, SUM(count) AS count").
Slice(&result).
FindAll()
return
}
// Clean 清理历史数据
func (this *ServerHTTPFirewallHourlyStatDAO) Clean(tx *dbs.Tx, days int) error {
var hour = timeutil.Format("Ymd00", time.Now().AddDate(0, 0, -days))
_, err := this.Query(tx).
Lt("hour", hour).
Delete()
return err
}

View File

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

View File

@@ -0,0 +1,26 @@
package stats
// ServerHTTPFirewallHourlyStat WAF统计
type ServerHTTPFirewallHourlyStat struct {
Id uint64 `field:"id"` // ID
ServerId uint32 `field:"serverId"` // 服务ID
Day string `field:"day"` // YYYYMMDD
Hour string `field:"hour"` // YYYYMMDDHH
HttpFirewallRuleGroupId uint32 `field:"httpFirewallRuleGroupId"` // WAF分组ID
Action string `field:"action"` // 采取的动作
Count uint64 `field:"count"` // 数量
}
type ServerHTTPFirewallHourlyStatOperator struct {
Id interface{} // ID
ServerId interface{} // 服务ID
Day interface{} // YYYYMMDD
Hour interface{} // YYYYMMDDHH
HttpFirewallRuleGroupId interface{} // WAF分组ID
Action interface{} // 采取的动作
Count interface{} // 数量
}
func NewServerHTTPFirewallHourlyStatOperator() *ServerHTTPFirewallHourlyStatOperator {
return &ServerHTTPFirewallHourlyStatOperator{}
}

View File

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

View File

@@ -2,15 +2,34 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
_ "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"
)
type TrafficDailyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
for range ticker.C {
err := SharedTrafficDailyStatDAO.Clean(nil, 60) // 只保留60天
if err != nil {
remotelogs.Error("TrafficDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
}
func NewTrafficDailyStatDAO() *TrafficDailyStatDAO {
return dbs.NewDAO(&TrafficDailyStatDAO{
DAOObject: dbs.DAOObject{
@@ -30,18 +49,33 @@ func init() {
})
}
// 增加流量
func (this *TrafficDailyStatDAO) IncreaseDailyBytes(tx *dbs.Tx, day string, bytes int64) error {
// IncreaseDailyStat 增加统计数据
func (this *TrafficDailyStatDAO) IncreaseDailyStat(tx *dbs.Tx, day string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests int64, countAttackRequests int64, attackBytes int64) error {
if len(day) != 8 {
return errors.New("invalid day '" + day + "'")
}
err := this.Query(tx).
Param("bytes", bytes).
Param("cachedBytes", cachedBytes).
Param("countRequests", countRequests).
Param("countCachedRequests", countCachedRequests).
Param("countAttackRequests", countAttackRequests).
Param("attackBytes", attackBytes).
InsertOrUpdateQuickly(maps.Map{
"day": day,
"bytes": bytes,
"day": day,
"bytes": bytes,
"cachedBytes": cachedBytes,
"countRequests": countRequests,
"countCachedRequests": countCachedRequests,
"countAttackRequests": countAttackRequests,
"attackBytes": attackBytes,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"bytes": dbs.SQL("bytes+:bytes"),
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
})
if err != nil {
return err
@@ -49,11 +83,14 @@ func (this *TrafficDailyStatDAO) IncreaseDailyBytes(tx *dbs.Tx, day string, byte
return nil
}
// 获取日期之间统计
// FindDailyStats 获取日期之间统计
func (this *TrafficDailyStatDAO) FindDailyStats(tx *dbs.Tx, dayFrom string, dayTo string) (result []*TrafficDailyStat, err error) {
ones, err := this.Query(tx).
Between("day", dayFrom, dayTo).
FindAll()
if err != nil {
return nil, err
}
dayMap := map[string]*TrafficDailyStat{} // day => Stat
for _, one := range ones {
stat := one.(*TrafficDailyStat)
@@ -73,3 +110,12 @@ func (this *TrafficDailyStatDAO) FindDailyStats(tx *dbs.Tx, dayFrom string, dayT
}
return result, nil
}
// Clean 清理历史数据
func (this *TrafficDailyStatDAO) Clean(tx *dbs.Tx, days int) error {
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days))
_, err := this.Query(tx).
Lt("day", day).
Delete()
return err
}

View File

@@ -1,16 +1,26 @@
package stats
// 总的流量统计
// TrafficDailyStat 总的流量统计(按天)
type TrafficDailyStat struct {
Id uint64 `field:"id"` // ID
Day string `field:"day"` // YYYYMMDD
Bytes uint64 `field:"bytes"` // 流量字节
Id uint64 `field:"id"` // ID
Day string `field:"day"` // YYYYMMDD
CachedBytes uint64 `field:"cachedBytes"` // 缓存流量
Bytes uint64 `field:"bytes"` // 流量字节
CountRequests uint64 `field:"countRequests"` // 请求数
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存请求数
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击量
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
}
type TrafficDailyStatOperator struct {
Id interface{} // ID
Day interface{} // YYYYMMDD
Bytes interface{} // 流量字节
Id interface{} // ID
Day interface{} // YYYYMMDD
CachedBytes interface{} // 缓存流量
Bytes interface{} // 流量字节
CountRequests interface{} // 请求数
CountCachedRequests interface{} // 缓存请求数
CountAttackRequests interface{} // 攻击量
AttackBytes interface{} // 攻击流量
}
func NewTrafficDailyStatOperator() *TrafficDailyStatOperator {

View File

@@ -2,15 +2,34 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
_ "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"
)
type TrafficHourlyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
go func() {
for range ticker.C {
err := SharedTrafficHourlyStatDAO.Clean(nil, 60) // 只保留60天
if err != nil {
remotelogs.Error("TrafficHourlyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
}
func NewTrafficHourlyStatDAO() *TrafficHourlyStatDAO {
return dbs.NewDAO(&TrafficHourlyStatDAO{
DAOObject: dbs.DAOObject{
@@ -30,18 +49,33 @@ func init() {
})
}
// 增加流量
func (this *TrafficHourlyStatDAO) IncreaseHourlyBytes(tx *dbs.Tx, hour string, bytes int64) error {
// IncreaseHourlyStat 增加流量
func (this *TrafficHourlyStatDAO) IncreaseHourlyStat(tx *dbs.Tx, hour string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests int64, countAttackRequests int64, attackBytes int64) error {
if len(hour) != 10 {
return errors.New("invalid hour '" + hour + "'")
}
err := this.Query(tx).
Param("bytes", bytes).
Param("cachedBytes", cachedBytes).
Param("countRequests", countRequests).
Param("countCachedRequests", countCachedRequests).
Param("countAttackRequests", countAttackRequests).
Param("attackBytes", attackBytes).
InsertOrUpdateQuickly(maps.Map{
"hour": hour,
"bytes": bytes,
"hour": hour,
"bytes": bytes,
"cachedBytes": cachedBytes,
"countRequests": countRequests,
"countCachedRequests": countCachedRequests,
"countAttackRequests": countAttackRequests,
"attackBytes": attackBytes,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"bytes": dbs.SQL("bytes+:bytes"),
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
})
if err != nil {
return err
@@ -49,11 +83,14 @@ func (this *TrafficHourlyStatDAO) IncreaseHourlyBytes(tx *dbs.Tx, hour string, b
return nil
}
// 获取日期之间统计
// FindHourlyStats 获取小时之间统计
func (this *TrafficHourlyStatDAO) FindHourlyStats(tx *dbs.Tx, hourFrom string, hourTo string) (result []*TrafficHourlyStat, err error) {
ones, err := this.Query(tx).
Between("hour", hourFrom, hourTo).
FindAll()
if err != nil {
return nil, err
}
hourMap := map[string]*TrafficHourlyStat{} // hour => Stat
for _, one := range ones {
stat := one.(*TrafficHourlyStat)
@@ -73,3 +110,12 @@ func (this *TrafficHourlyStatDAO) FindHourlyStats(tx *dbs.Tx, hourFrom string, h
}
return result, nil
}
// Clean 清理历史数据
func (this *TrafficHourlyStatDAO) Clean(tx *dbs.Tx, days int) error {
var hour = timeutil.Format("Ymd00", time.Now().AddDate(0, 0, -days))
_, err := this.Query(tx).
Lt("hour", hour).
Delete()
return err
}

View File

@@ -1,16 +1,26 @@
package stats
// 总的流量统计(按小时)
// TrafficHourlyStat 总的流量统计(按小时)
type TrafficHourlyStat struct {
Id uint64 `field:"id"` // ID
Hour string `field:"hour"` // YYYYMMDDHH
Bytes uint64 `field:"bytes"` // 流量字节
Id uint64 `field:"id"` // ID
Hour string `field:"hour"` // YYYYMMDDHH
Bytes uint64 `field:"bytes"` // 流量字节
CachedBytes uint64 `field:"cachedBytes"` // 缓存流量
CountRequests uint64 `field:"countRequests"` // 请求数
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存请求数
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击数
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
}
type TrafficHourlyStatOperator struct {
Id interface{} // ID
Hour interface{} // YYYYMMDDHH
Bytes interface{} // 流量字节
Id interface{} // ID
Hour interface{} // YYYYMMDDHH
Bytes interface{} // 流量字节
CachedBytes interface{} // 缓存流量
CountRequests interface{} // 请求数
CountCachedRequests interface{} // 缓存请求数
CountAttackRequests interface{} // 攻击数
AttackBytes interface{} // 攻击流量
}
func NewTrafficHourlyStatOperator() *TrafficHourlyStatOperator {

View File

@@ -3,11 +3,14 @@ package models
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/types"
stringutil "github.com/iwind/TeaGo/utils/string"
timeutil "github.com/iwind/TeaGo/utils/time"
)
const (
@@ -97,6 +100,7 @@ func (this *UserDAO) CreateUser(tx *dbs.Tx, username string, password string, fu
op.Remark = remark
op.Source = source
op.ClusterId = clusterId
op.Day = timeutil.Format("Ymd")
op.IsOn = true
op.State = UserStateEnabled
@@ -156,9 +160,12 @@ func (this *UserDAO) UpdateUserLogin(tx *dbs.Tx, userId int64, username string,
}
// CountAllEnabledUsers 计算用户数量
func (this *UserDAO) CountAllEnabledUsers(tx *dbs.Tx, keyword string) (int64, error) {
func (this *UserDAO) CountAllEnabledUsers(tx *dbs.Tx, clusterId int64, keyword string) (int64, error) {
query := this.Query(tx)
query.State(UserStateEnabled)
if clusterId > 0 {
query.Attr("clusterId", clusterId)
}
if len(keyword) > 0 {
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+"%")
@@ -167,9 +174,12 @@ func (this *UserDAO) CountAllEnabledUsers(tx *dbs.Tx, keyword string) (int64, er
}
// ListEnabledUsers 列出单页用户
func (this *UserDAO) ListEnabledUsers(tx *dbs.Tx, keyword string, offset int64, size int64) (result []*User, err error) {
func (this *UserDAO) ListEnabledUsers(tx *dbs.Tx, clusterId int64, keyword string, offset int64, size int64) (result []*User, err error) {
query := this.Query(tx)
query.State(UserStateEnabled)
if clusterId > 0 {
query.Attr("clusterId", clusterId)
}
if len(keyword) > 0 {
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+"%")
@@ -283,3 +293,50 @@ func (this *UserDAO) FindUserFeatures(tx *dbs.Tx, userId int64) ([]*UserFeature,
return result, nil
}
// SumDailyUsers 获取当天用户数量
func (this *UserDAO) SumDailyUsers(tx *dbs.Tx, dayFrom string, dayTo string) (int64, error) {
return this.Query(tx).
Between("day", dayFrom, dayTo).
State(UserStateEnabled).
Count()
}
// CountDailyUsers 计算每天用户数
func (this *UserDAO) CountDailyUsers(tx *dbs.Tx, dayFrom string, dayTo string) ([]*pb.ComposeUserGlobalBoardResponse_DailyStat, error) {
ones, _, err := this.Query(tx).
Result("COUNT(*) AS count", "day").
Between("day", dayFrom, dayTo).
State(UserStateEnabled).
Group("day").
FindOnes()
if err != nil {
return nil, err
}
var m = map[string]*pb.ComposeUserGlobalBoardResponse_DailyStat{} // day => Stat
for _, one := range ones {
m[one.GetString("day")] = &pb.ComposeUserGlobalBoardResponse_DailyStat{
Day: one.GetString("day"),
Count: one.GetInt64("count"),
}
}
var result = []*pb.ComposeUserGlobalBoardResponse_DailyStat{}
days, err := utils.RangeDays(dayFrom, dayTo)
if err != nil {
return nil, err
}
for _, day := range days {
stat, ok := m[day]
if ok {
result = append(result, stat)
} else {
result = append(result, &pb.ComposeUserGlobalBoardResponse_DailyStat{
Day: day,
Count: 0,
})
}
}
return result, nil
}

View File

@@ -1,6 +1,6 @@
package models
// 用户
// User 用户
type User struct {
Id uint32 `field:"id"` // ID
IsOn uint8 `field:"isOn"` // 是否启用
@@ -13,6 +13,7 @@ type User struct {
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"` // 来源
@@ -32,6 +33,7 @@ type UserOperator struct {
Email interface{} // 邮箱地址
AvatarFileId interface{} // 头像文件ID
CreatedAt interface{} // 创建时间
Day interface{} // YYYYMMDD
UpdatedAt interface{} // 修改时间
State interface{} // 状态
Source interface{} // 来源

View File

@@ -265,3 +265,11 @@ func (this *UserNodeDAO) CountAllLowerVersionNodes(tx *dbs.Tx, version string) (
Param("version", utils.VersionToLong(version)).
Count()
}
// CountOfflineNodes 计算离线节点数量
func (this *UserNodeDAO) CountOfflineNodes(tx *dbs.Tx) (int64, error) {
return this.Query(tx).
State(UserNodeStateEnabled).
Where("(status IS NULL OR JSON_EXTRACT(status, '$.updatedAt')<UNIX_TIMESTAMP()-120)").
Count()
}

View File

@@ -188,7 +188,7 @@ func (this *LocalEdgeDNSProvider) UpdateRecord(domain string, record *dnstypes.R
}
if len(record.Id) > 0 {
err = nameservers.SharedNSRecordDAO.UpdateRecord(tx, types.Int64(record.Id), "", newRecord.Name, newRecord.Type, newRecord.Value, this.ttl, routeIds)
err = nameservers.SharedNSRecordDAO.UpdateRecord(tx, types.Int64(record.Id), "", newRecord.Name, newRecord.Type, newRecord.Value, this.ttl, routeIds, true)
if err != nil {
return err
}
@@ -198,7 +198,7 @@ func (this *LocalEdgeDNSProvider) UpdateRecord(domain string, record *dnstypes.R
return err
}
if realRecord != nil {
err = nameservers.SharedNSRecordDAO.UpdateRecord(tx, types.Int64(realRecord.Id), "", newRecord.Name, newRecord.Type, newRecord.Value, this.ttl, routeIds)
err = nameservers.SharedNSRecordDAO.UpdateRecord(tx, types.Int64(realRecord.Id), "", newRecord.Name, newRecord.Type, newRecord.Value, this.ttl, routeIds, true)
if err != nil {
return err
}

View File

@@ -6,4 +6,5 @@ type Credentials struct {
Username string
Password string
PrivateKey string
Method string
}

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