Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe4eb3928e | ||
|
|
514d968b20 | ||
|
|
3cce13e671 | ||
|
|
6d359b09f2 | ||
|
|
316b3485ca | ||
|
|
31c20122b4 | ||
|
|
7343d4bfac | ||
|
|
330d5a3d21 | ||
|
|
709845064f | ||
|
|
be5a89e513 | ||
|
|
80328b9b5f | ||
|
|
c6a09e131b | ||
|
|
f52ea4fa4f | ||
|
|
6cbda588f7 | ||
|
|
f9e7c3a2e0 | ||
|
|
c370dada11 | ||
|
|
8d71afc176 | ||
|
|
a39a114316 | ||
|
|
3f5c3568db | ||
|
|
f50ff00f0b | ||
|
|
7caa5bee75 | ||
|
|
bf5368929b | ||
|
|
ff6d8d9ba3 | ||
|
|
877d42a271 | ||
|
|
21c51cbdc8 | ||
|
|
45e4eb72ac | ||
|
|
11c344eef4 | ||
|
|
17008d6b03 | ||
|
|
5512efbb70 | ||
|
|
1e0a7612df | ||
|
|
8589e0d373 | ||
|
|
c5edaef356 | ||
|
|
692eb04c93 | ||
|
|
83d9a17c2b | ||
|
|
a4422ef7bd | ||
|
|
e6e9fcc3c3 | ||
|
|
7484243dff | ||
|
|
907676d688 | ||
|
|
54dbe1f3e4 | ||
|
|
98a2d61fd1 | ||
|
|
e544e088be | ||
|
|
86c33256ca |
2
go.mod
2
go.mod
@@ -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
4
go.sum
@@ -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=
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package teaconst
|
||||
|
||||
const (
|
||||
Version = "0.2.4"
|
||||
Version = "0.2.5"
|
||||
|
||||
ProductName = "Edge API"
|
||||
ProcessName = "edge-api"
|
||||
@@ -18,9 +18,9 @@ const (
|
||||
|
||||
// 其他节点版本号,用来检测是否有需要升级的节点
|
||||
|
||||
NodeVersion = "0.2.4"
|
||||
UserNodeVersion = "0.0.9"
|
||||
NodeVersion = "0.2.5"
|
||||
UserNodeVersion = "0.0.10"
|
||||
AuthorityNodeVersion = "0.0.2"
|
||||
MonitorNodeVersion = "0.0.2"
|
||||
DNSNodeVersion = "0.0.1"
|
||||
DNSNodeVersion = "0.0.2"
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -104,6 +104,11 @@ func (this *ClientBrowserDAO) FindBrowserIdWithNameCacheable(tx *dbs.Tx, browser
|
||||
|
||||
// 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()
|
||||
|
||||
|
||||
@@ -104,6 +104,11 @@ func (this *ClientSystemDAO) FindSystemIdWithNameCacheable(tx *dbs.Tx, systemNam
|
||||
|
||||
// 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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package models
|
||||
|
||||
// 路径规则配置
|
||||
// HTTPLocation 路由规则配置
|
||||
type HTTPLocation struct {
|
||||
Id uint32 `field:"id"` // ID
|
||||
TemplateId uint32 `field:"templateId"` // 模版ID
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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{} // 域名跳转
|
||||
|
||||
@@ -86,6 +86,16 @@ func (this *IPItemDAO) FindEnabledIPItem(tx *dbs.Tx, id int64) (*IPItem, error)
|
||||
return result.(*IPItem), err
|
||||
}
|
||||
|
||||
// DisableOldIPItem 根据IP删除以前的旧记录
|
||||
func (this *IPItemDAO) DisableOldIPItem(tx *dbs.Tx, listId int64, ipFrom string, ipTo string) error {
|
||||
return this.Query(tx).
|
||||
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)
|
||||
|
||||
@@ -38,6 +38,7 @@ const (
|
||||
MessageTypeServerNamesAuditingSuccess MessageType = "ServerNamesAuditingSuccess" // 服务域名审核成功
|
||||
MessageTypeServerNamesAuditingFailed MessageType = "ServerNamesAuditingFailed" // 服务域名审核失败
|
||||
MessageTypeThresholdSatisfied MessageType = "ThresholdSatisfied" // 满足阈值
|
||||
MessageTypeFirewallEvent MessageType = "FirewallEvent" // 防火墙事件
|
||||
)
|
||||
|
||||
type MessageDAO dbs.DAO
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
154
internal/db/models/metric_chart_dao.go
Normal file
154
internal/db/models/metric_chart_dao.go
Normal 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
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package metrics
|
||||
package models
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
34
internal/db/models/metric_chart_model.go
Normal file
34
internal/db/models/metric_chart_model.go
Normal 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{}
|
||||
}
|
||||
1
internal/db/models/metric_chart_model_ext.go
Normal file
1
internal/db/models/metric_chart_model_ext.go
Normal file
@@ -0,0 +1 @@
|
||||
package models
|
||||
@@ -1,4 +1,4 @@
|
||||
package metrics
|
||||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -46,12 +48,35 @@ func (this *MetricItemDAO) EnableMetricItem(tx *dbs.Tx, id int64) error {
|
||||
}
|
||||
|
||||
// DisableMetricItem 禁用条目
|
||||
func (this *MetricItemDAO) DisableMetricItem(tx *dbs.Tx, id int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
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()
|
||||
return err
|
||||
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 查找启用中的条目
|
||||
@@ -75,7 +100,9 @@ func (this *MetricItemDAO) FindMetricItemName(tx *dbs.Tx, id int64) (string, err
|
||||
}
|
||||
|
||||
// CreateItem 创建指标
|
||||
func (this *MetricItemDAO) CreateItem(tx *dbs.Tx, code string, category string, name string, keys []string, period int32, periodUnit string, value string) (int64, error) {
|
||||
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
|
||||
@@ -92,16 +119,47 @@ func (this *MetricItemDAO) CreateItem(tx *dbs.Tx, code string, category string,
|
||||
op.Period = period
|
||||
op.PeriodUnit = periodUnit
|
||||
op.Value = value
|
||||
op.IsPublic = isPublic
|
||||
op.IsOn = true
|
||||
op.State = MetricItemStateEnabled
|
||||
return this.SaveInt64(tx, op)
|
||||
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) error {
|
||||
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
|
||||
@@ -118,7 +176,32 @@ func (this *MetricItemDAO) UpdateItem(tx *dbs.Tx, itemId int64, name string, key
|
||||
op.PeriodUnit = periodUnit
|
||||
op.Value = value
|
||||
op.IsOn = isOn
|
||||
return this.Save(tx, op)
|
||||
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 计算指标的数量
|
||||
@@ -144,6 +227,18 @@ func (this *MetricItemDAO) ListEnabledItems(tx *dbs.Tx, category serverconfigs.M
|
||||
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 {
|
||||
@@ -168,7 +263,67 @@ func (this *MetricItemDAO) ComposeItemConfig(tx *dbs.Tx, itemId int64) (*serverc
|
||||
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
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package metrics
|
||||
package models
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
@@ -1,4 +1,4 @@
|
||||
package metrics
|
||||
package models
|
||||
|
||||
// MetricItem 指标定义
|
||||
type MetricItem struct {
|
||||
@@ -14,6 +14,8 @@ type MetricItem struct {
|
||||
PeriodUnit string `field:"periodUnit"` // 周期单位
|
||||
Value string `field:"value"` // 值运算
|
||||
State uint8 `field:"state"` // 状态
|
||||
Version uint32 `field:"version"` // 版本号
|
||||
IsPublic uint8 `field:"isPublic"` // 是否为公用
|
||||
}
|
||||
|
||||
type MetricItemOperator struct {
|
||||
@@ -29,6 +31,8 @@ type MetricItemOperator struct {
|
||||
PeriodUnit interface{} // 周期单位
|
||||
Value interface{} // 值运算
|
||||
State interface{} // 状态
|
||||
Version interface{} // 版本号
|
||||
IsPublic interface{} // 是否为公用
|
||||
}
|
||||
|
||||
func NewMetricItemOperator() *MetricItemOperator {
|
||||
@@ -1,7 +1,10 @@
|
||||
package metrics
|
||||
package models
|
||||
|
||||
import "encoding/json"
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// DecodeKeys 解析Key
|
||||
func (this *MetricItem) DecodeKeys() []string {
|
||||
var result []string
|
||||
if len(this.Keys) > 0 {
|
||||
355
internal/db/models/metric_stat_dao.go
Normal file
355
internal/db/models/metric_stat_dao.go
Normal 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
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package metrics
|
||||
package models
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
34
internal/db/models/metric_stat_model.go
Normal file
34
internal/db/models/metric_stat_model.go
Normal 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{}
|
||||
}
|
||||
12
internal/db/models/metric_stat_model_ext.go
Normal file
12
internal/db/models/metric_stat_model_ext.go
Normal 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
|
||||
}
|
||||
136
internal/db/models/metric_sum_stat_dao.go
Normal file
136
internal/db/models/metric_sum_stat_dao.go
Normal 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
|
||||
}
|
||||
6
internal/db/models/metric_sum_stat_dao_test.go
Normal file
6
internal/db/models/metric_sum_stat_dao_test.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
)
|
||||
30
internal/db/models/metric_sum_stat_model.go
Normal file
30
internal/db/models/metric_sum_stat_model.go
Normal 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{}
|
||||
}
|
||||
1
internal/db/models/metric_sum_stat_model_ext.go
Normal file
1
internal/db/models/metric_sum_stat_model_ext.go
Normal file
@@ -0,0 +1 @@
|
||||
package models
|
||||
@@ -1,28 +0,0 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
)
|
||||
|
||||
type MetricKeyDAO dbs.DAO
|
||||
|
||||
func NewMetricKeyDAO() *MetricKeyDAO {
|
||||
return dbs.NewDAO(&MetricKeyDAO{
|
||||
DAOObject: dbs.DAOObject{
|
||||
DB: Tea.Env,
|
||||
Table: "edgeMetricKeys",
|
||||
Model: new(MetricKey),
|
||||
PkName: "id",
|
||||
},
|
||||
}).(*MetricKeyDAO)
|
||||
}
|
||||
|
||||
var SharedMetricKeyDAO *MetricKeyDAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedMetricKeyDAO = NewMetricKeyDAO()
|
||||
})
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package metrics
|
||||
|
||||
// MetricKey 指标键值
|
||||
type MetricKey struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
ItemId uint64 `field:"itemId"` // 指标ID
|
||||
Value string `field:"value"` // 值
|
||||
Hash string `field:"hash"` // 对值进行Hash
|
||||
}
|
||||
|
||||
type MetricKeyOperator struct {
|
||||
Id interface{} // ID
|
||||
ItemId interface{} // 指标ID
|
||||
Value interface{} // 值
|
||||
Hash interface{} // 对值进行Hash
|
||||
}
|
||||
|
||||
func NewMetricKeyOperator() *MetricKeyOperator {
|
||||
return &MetricKeyOperator{}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package metrics
|
||||
@@ -1,28 +0,0 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
)
|
||||
|
||||
type MetricStatDAO dbs.DAO
|
||||
|
||||
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()
|
||||
})
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package metrics
|
||||
|
||||
// MetricStat 指标统计数据
|
||||
type MetricStat 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"` // 指标
|
||||
KeyId uint64 `field:"keyId"` // 指标键ID
|
||||
Value float64 `field:"value"` // 数值
|
||||
Minute string `field:"minute"` // 分钟值YYYYMMDDHHII
|
||||
}
|
||||
|
||||
type MetricStatOperator struct {
|
||||
Id interface{} // ID
|
||||
ClusterId interface{} // 集群ID
|
||||
NodeId interface{} // 节点ID
|
||||
ServerId interface{} // 服务ID
|
||||
ItemId interface{} // 指标
|
||||
KeyId interface{} // 指标键ID
|
||||
Value interface{} // 数值
|
||||
Minute interface{} // 分钟值YYYYMMDDHHII
|
||||
}
|
||||
|
||||
func NewMetricStatOperator() *MetricStatOperator {
|
||||
return &MetricStatOperator{}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package metrics
|
||||
@@ -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 先什么都不做
|
||||
|
||||
@@ -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).
|
||||
|
||||
168
internal/db/models/nameservers/ns_record_hourly_stat_dao.go
Normal file
168
internal/db/models/nameservers/ns_record_hourly_stat_dao.go
Normal 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
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package nameservers
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
)
|
||||
@@ -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{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package nameservers
|
||||
@@ -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("")
|
||||
}
|
||||
|
||||
@@ -65,12 +65,16 @@ func (this *NodeClusterMetricItemDAO) FindEnabledNodeClusterMetricItem(tx *dbs.T
|
||||
|
||||
// DisableClusterItem 禁用某个集群的指标
|
||||
func (this *NodeClusterMetricItemDAO) DisableClusterItem(tx *dbs.Tx, clusterId int64, itemId int64) error {
|
||||
return this.Query(tx).
|
||||
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 启用某个集群的指标
|
||||
@@ -82,20 +86,59 @@ func (this *NodeClusterMetricItemDAO) EnableClusterItem(tx *dbs.Tx, clusterId in
|
||||
op.ClusterId = clusterId
|
||||
op.ItemId = itemId
|
||||
op.IsOn = true
|
||||
return this.Save(tx, op)
|
||||
err := this.Save(tx, op)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return this.NotifyUpdate(tx, clusterId)
|
||||
}
|
||||
|
||||
// FindAllClusterItems 查找某个集群的指标
|
||||
func (this *NodeClusterMetricItemDAO) FindAllClusterItems(tx *dbs.Tx, clusterId int64) (result []*NodeClusterMetricItem, err error) {
|
||||
_, err = this.Query(tx).
|
||||
// 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).
|
||||
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).
|
||||
@@ -103,3 +146,17 @@ func (this *NodeClusterMetricItemDAO) CountAllClusterItems(tx *dbs.Tx, 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)
|
||||
}
|
||||
|
||||
@@ -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,8 +683,35 @@ 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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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{} // 监控项
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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{} // 是否已计算费用
|
||||
|
||||
@@ -1198,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 {
|
||||
|
||||
93
internal/db/models/server_stat_board_chart_dao.go
Normal file
93
internal/db/models/server_stat_board_chart_dao.go
Normal 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
|
||||
}
|
||||
6
internal/db/models/server_stat_board_chart_dao_test.go
Normal file
6
internal/db/models/server_stat_board_chart_dao_test.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
)
|
||||
26
internal/db/models/server_stat_board_chart_model.go
Normal file
26
internal/db/models/server_stat_board_chart_model.go
Normal 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{}
|
||||
}
|
||||
1
internal/db/models/server_stat_board_chart_model_ext.go
Normal file
1
internal/db/models/server_stat_board_chart_model_ext.go
Normal file
@@ -0,0 +1 @@
|
||||
package models
|
||||
83
internal/db/models/server_stat_board_dao.go
Normal file
83
internal/db/models/server_stat_board_dao.go
Normal 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
|
||||
}
|
||||
6
internal/db/models/server_stat_board_dao_test.go
Normal file
6
internal/db/models/server_stat_board_dao_test.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
)
|
||||
24
internal/db/models/server_stat_board_model.go
Normal file
24
internal/db/models/server_stat_board_model.go
Normal 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{}
|
||||
}
|
||||
1
internal/db/models/server_stat_board_model_ext.go
Normal file
1
internal/db/models/server_stat_board_model_ext.go
Normal file
@@ -0,0 +1 @@
|
||||
package models
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
188
internal/db/models/stats/node_traffic_hourly_stat_dao.go
Normal file
188
internal/db/models/stats/node_traffic_hourly_stat_dao.go
Normal 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
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
)
|
||||
34
internal/db/models/stats/node_traffic_hourly_stat_model.go
Normal file
34
internal/db/models/stats/node_traffic_hourly_stat_model.go
Normal 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{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package stats
|
||||
155
internal/db/models/stats/server_domain_hourly_stat_dao.go
Normal file
155
internal/db/models/stats/server_domain_hourly_stat_dao.go
Normal 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
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
)
|
||||
36
internal/db/models/stats/server_domain_hourly_stat_model.go
Normal file
36
internal/db/models/stats/server_domain_hourly_stat_model.go
Normal 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{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package stats
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
136
internal/db/models/stats/server_http_firewall_hourly_stat_dao.go
Normal file
136
internal/db/models/stats/server_http_firewall_hourly_stat_dao.go
Normal 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
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
)
|
||||
@@ -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{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package stats
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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{} // 来源
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -6,4 +6,5 @@ type Credentials struct {
|
||||
Username string
|
||||
Password string
|
||||
PrivateKey string
|
||||
Method string
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package installers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"golang.org/x/crypto/ssh"
|
||||
@@ -17,7 +18,7 @@ type BaseInstaller struct {
|
||||
client *SSHClient
|
||||
}
|
||||
|
||||
// 登录SSH服务
|
||||
// Login 登录SSH服务
|
||||
func (this *BaseInstaller) Login(credentials *Credentials) error {
|
||||
var hostKeyCallback ssh.HostKeyCallback = nil
|
||||
|
||||
@@ -41,7 +42,7 @@ func (this *BaseInstaller) Login(credentials *Credentials) error {
|
||||
|
||||
// 认证
|
||||
methods := []ssh.AuthMethod{}
|
||||
if len(credentials.Password) > 0 {
|
||||
if credentials.Method == "user" {
|
||||
{
|
||||
authMethod := ssh.Password(credentials.Password)
|
||||
methods = append(methods, authMethod)
|
||||
@@ -56,16 +57,21 @@ func (this *BaseInstaller) Login(credentials *Credentials) error {
|
||||
})
|
||||
methods = append(methods, authMethod)
|
||||
}
|
||||
} else {
|
||||
} else if credentials.Method == "privateKey" {
|
||||
signer, err := ssh.ParsePrivateKey([]byte(credentials.PrivateKey))
|
||||
if err != nil {
|
||||
return errors.New("parse private key: " + err.Error())
|
||||
}
|
||||
authMethod := ssh.PublicKeys(signer)
|
||||
methods = append(methods, authMethod)
|
||||
} else {
|
||||
return errors.New("invalid method '" + credentials.Method + "'")
|
||||
}
|
||||
|
||||
// SSH客户端
|
||||
if len(credentials.Username) == 0 {
|
||||
credentials.Username = "root"
|
||||
}
|
||||
config := &ssh.ClientConfig{
|
||||
User: credentials.Username,
|
||||
Auth: methods,
|
||||
@@ -73,7 +79,7 @@ func (this *BaseInstaller) Login(credentials *Credentials) error {
|
||||
Timeout: 5 * time.Second, // TODO 后期可以设置这个超时时间
|
||||
}
|
||||
|
||||
sshClient, err := ssh.Dial("tcp", credentials.Host+":"+strconv.Itoa(credentials.Port), config)
|
||||
sshClient, err := ssh.Dial("tcp", configutils.QuoteIP(credentials.Host)+":"+strconv.Itoa(credentials.Port), config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -85,7 +91,7 @@ func (this *BaseInstaller) Login(credentials *Credentials) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 关闭SSH服务
|
||||
// Close 关闭SSH服务
|
||||
func (this *BaseInstaller) Close() error {
|
||||
if this.client != nil {
|
||||
return this.client.Close()
|
||||
@@ -94,7 +100,7 @@ func (this *BaseInstaller) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 查找最新的版本的文件
|
||||
// LookupLatestInstaller 查找最新的版本的文件
|
||||
func (this *BaseInstaller) LookupLatestInstaller(filePrefix string) (string, error) {
|
||||
matches, err := filepath.Glob(Tea.Root + Tea.DS + "deploy" + Tea.DS + "*.zip")
|
||||
if err != nil {
|
||||
@@ -126,7 +132,7 @@ func (this *BaseInstaller) LookupLatestInstaller(filePrefix string) (string, err
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 上传安装助手
|
||||
// InstallHelper 上传安装助手
|
||||
func (this *BaseInstaller) InstallHelper(targetDir string) (env *Env, err error) {
|
||||
uname, _, err := this.client.Exec("uname -a")
|
||||
if err != nil {
|
||||
|
||||
@@ -184,6 +184,7 @@ func (this *Queue) InstallNode(nodeId int64, installStatus *models.NodeInstallSt
|
||||
Username: grant.Username,
|
||||
Password: grant.Password,
|
||||
PrivateKey: grant.PrivateKey,
|
||||
Method: grant.Method,
|
||||
})
|
||||
if err != nil {
|
||||
installStatus.ErrorCode = "SSH_LOGIN_FAILED"
|
||||
@@ -272,6 +273,7 @@ func (this *Queue) StartNode(nodeId int64) error {
|
||||
Username: grant.Username,
|
||||
Password: grant.Password,
|
||||
PrivateKey: grant.PrivateKey,
|
||||
Method: grant.Method,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -376,6 +378,7 @@ func (this *Queue) StopNode(nodeId int64) error {
|
||||
Username: grant.Username,
|
||||
Password: grant.Password,
|
||||
PrivateKey: grant.PrivateKey,
|
||||
Method: grant.Method,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -30,7 +30,7 @@ func NewSSHClient(raw *ssh.Client) (*SSHClient, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// 执行shell命令
|
||||
// Exec 执行shell命令
|
||||
func (this *SSHClient) Exec(cmd string) (stdout string, stderr string, err error) {
|
||||
session, err := this.raw.NewSession()
|
||||
if err != nil {
|
||||
@@ -86,7 +86,7 @@ func (this *SSHClient) Chmod(path string, mode os.FileMode) error {
|
||||
return this.sftp.Chmod(path, mode)
|
||||
}
|
||||
|
||||
// 拷贝文件
|
||||
// Copy 拷贝文件
|
||||
func (this *SSHClient) Copy(localPath string, remotePath string, mode os.FileMode) error {
|
||||
localFp, err := os.Open(localPath)
|
||||
if err != nil {
|
||||
@@ -110,12 +110,12 @@ func (this *SSHClient) Copy(localPath string, remotePath string, mode os.FileMod
|
||||
return this.Chmod(remotePath, mode)
|
||||
}
|
||||
|
||||
// 获取新Session
|
||||
// NewSession 获取新Session
|
||||
func (this *SSHClient) NewSession() (*ssh.Session, error) {
|
||||
return this.raw.NewSession()
|
||||
}
|
||||
|
||||
// 读取文件内容
|
||||
// ReadFile 读取文件内容
|
||||
func (this *SSHClient) ReadFile(path string) ([]byte, error) {
|
||||
fp, err := this.sftp.OpenFile(path, 0444)
|
||||
if err != nil {
|
||||
@@ -134,7 +134,7 @@ func (this *SSHClient) ReadFile(path string) ([]byte, error) {
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// 写入文件内容
|
||||
// WriteFile 写入文件内容
|
||||
func (this *SSHClient) WriteFile(path string, data []byte) (n int, err error) {
|
||||
fp, err := this.sftp.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY)
|
||||
if err != nil {
|
||||
@@ -148,7 +148,7 @@ func (this *SSHClient) WriteFile(path string, data []byte) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// 删除文件
|
||||
// Remove 删除文件
|
||||
func (this *SSHClient) Remove(path string) error {
|
||||
return this.sftp.Remove(path)
|
||||
}
|
||||
|
||||
@@ -262,13 +262,14 @@ func (this *APINode) autoUpgrade() error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// 不使用remotelogs(),因为此时还没有启动完成
|
||||
|
||||
// 不使用remotelog(),因为此时还没有启动完成
|
||||
logs.Println("[API_NODE]upgrade database starting ...")
|
||||
err = setup.NewSQLExecutor(dbConfig).Run()
|
||||
if err != nil {
|
||||
return errors.New("execute sql failed: " + err.Error())
|
||||
}
|
||||
// 不使用remotelogs
|
||||
// 不使用remotelog
|
||||
logs.Println("[API_NODE]upgrade database done")
|
||||
return nil
|
||||
}
|
||||
@@ -292,7 +293,10 @@ func (this *APINode) setupDB() error {
|
||||
if valueInt < 65535 {
|
||||
_, err := db.Exec("SET GLOBAL max_prepared_stmt_count=65535")
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.New("run 'SET GLOBAL max_prepared_stmt_count' on database failed: " + err.Error() + ", \nyou can change the variable in 'my.cnf': \n~~~\n" + `[mysqld]
|
||||
max_prepared_stmt_count=65535
|
||||
~~~
|
||||
then restart mysqld.`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -315,8 +319,20 @@ func (this *APINode) listenPorts(apiNode *models.APINode) (isListening bool) {
|
||||
for _, addr := range listen.Addresses() {
|
||||
listener, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
remotelogs.Error("API_NODE", "listening '"+addr+"' failed: "+err.Error())
|
||||
continue
|
||||
remotelogs.Error("API_NODE", "listening '"+addr+"' failed: "+err.Error()+", we will try to listen port only")
|
||||
|
||||
// 试着只监听端口
|
||||
_, port, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
remotelogs.Println("API_NODE", "retry listening port ':"+port+"' only ...")
|
||||
listener, err = net.Listen("tcp", ":"+port)
|
||||
if err != nil {
|
||||
remotelogs.Error("API_NODE", "listening ':"+port+"' failed: "+err.Error())
|
||||
continue
|
||||
}
|
||||
remotelogs.Println("API_NODE", "retry listening port ':"+port+"' only ok")
|
||||
}
|
||||
go func() {
|
||||
err := this.listenRPC(listener, nil)
|
||||
@@ -351,8 +367,19 @@ func (this *APINode) listenPorts(apiNode *models.APINode) (isListening bool) {
|
||||
for _, addr := range listen.Addresses() {
|
||||
listener, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
remotelogs.Error("API_NODE", "listening '"+addr+"' failed: "+err.Error())
|
||||
continue
|
||||
remotelogs.Error("API_NODE", "listening '"+addr+"' failed: "+err.Error()+", we will try to listen port only")
|
||||
// 试着只监听端口
|
||||
_, port, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
remotelogs.Println("API_NODE", "retry listening port ':"+port+"' only ...")
|
||||
listener, err = net.Listen("tcp", ":"+port)
|
||||
if err != nil {
|
||||
remotelogs.Error("API_NODE", "listening ':"+port+"' failed: "+err.Error())
|
||||
continue
|
||||
}
|
||||
remotelogs.Println("API_NODE", "retry listening port ':"+port+"' only ok")
|
||||
}
|
||||
go func() {
|
||||
err := this.listenRPC(listener, &tls.Config{
|
||||
|
||||
@@ -13,6 +13,11 @@ import (
|
||||
|
||||
// 注册服务
|
||||
func (this *APINode) registerServices(server *grpc.Server) {
|
||||
{
|
||||
instance := this.serviceInstance(&services.APITokenService{}).(*services.APITokenService)
|
||||
pb.RegisterAPITokenServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
instance := this.serviceInstance(&services.AdminService{}).(*services.AdminService)
|
||||
pb.RegisterAdminServiceServer(server, instance)
|
||||
@@ -98,6 +103,11 @@ func (this *APINode) registerServices(server *grpc.Server) {
|
||||
pb.RegisterHTTPFirewallPolicyServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
instance := this.serviceInstance(&services.FirewallService{}).(*services.FirewallService)
|
||||
pb.RegisterFirewallServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
instance := this.serviceInstance(&services.HTTPLocationService{}).(*services.HTTPLocationService)
|
||||
pb.RegisterHTTPLocationServiceServer(server, instance)
|
||||
@@ -438,6 +448,16 @@ func (this *APINode) registerServices(server *grpc.Server) {
|
||||
pb.RegisterNSAccessLogServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
instance := this.serviceInstance(&nameservers.NSRecordHourlyStatService{}).(*nameservers.NSRecordHourlyStatService)
|
||||
pb.RegisterNSRecordHourlyStatServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
instance := this.serviceInstance(&nameservers.NSService{}).(*nameservers.NSService)
|
||||
pb.RegisterNSServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
instance := this.serviceInstance(&services.HTTPAuthPolicyService{}).(*services.HTTPAuthPolicyService)
|
||||
pb.RegisterHTTPAuthPolicyServiceServer(server, instance)
|
||||
@@ -453,6 +473,28 @@ func (this *APINode) registerServices(server *grpc.Server) {
|
||||
pb.RegisterNodeClusterMetricItemServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
instance := this.serviceInstance(&services.MetricStatService{}).(*services.MetricStatService)
|
||||
pb.RegisterMetricStatServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
instance := this.serviceInstance(&services.MetricChartService{}).(*services.MetricChartService)
|
||||
pb.RegisterMetricChartServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
|
||||
{
|
||||
instance := this.serviceInstance(&services.ServerStatBoardService{}).(*services.ServerStatBoardService)
|
||||
pb.RegisterServerStatBoardServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
|
||||
{
|
||||
instance := this.serviceInstance(&services.ServerStatBoardChartService{}).(*services.ServerStatBoardChartService)
|
||||
pb.RegisterServerStatBoardChartServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
|
||||
// TODO check service names
|
||||
for serviceName := range server.GetServiceInfo() {
|
||||
|
||||
188
internal/rpc/services/nameservers/service_ns.go
Normal file
188
internal/rpc/services/nameservers/service_ns.go
Normal file
@@ -0,0 +1,188 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package nameservers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models/nameservers"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NSService 域名服务
|
||||
type NSService struct {
|
||||
services.BaseService
|
||||
}
|
||||
|
||||
// ComposeNSBoard 组合看板数据
|
||||
func (this *NSService) ComposeNSBoard(ctx context.Context, req *pb.ComposeNSBoardRequest) (*pb.ComposeNSBoardResponse, error) {
|
||||
_, err := this.ValidateAdmin(ctx, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var tx = this.NullTx()
|
||||
var result = &pb.ComposeNSBoardResponse{}
|
||||
|
||||
// 域名
|
||||
countDomains, err := nameservers.SharedNSDomainDAO.CountAllEnabledDomains(tx, 0, 0, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.CountNSDomains = countDomains
|
||||
|
||||
// 记录
|
||||
countRecords, err := nameservers.SharedNSRecordDAO.CountAllEnabledRecords(tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.CountNSRecords = countRecords
|
||||
|
||||
// 集群数
|
||||
countClusters, err := nameservers.SharedNSClusterDAO.CountAllEnabledClusters(tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.CountNSClusters = countClusters
|
||||
|
||||
// 节点数
|
||||
countNodes, err := nameservers.SharedNSNodeDAO.CountAllEnabledNodes(tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.CountNSNodes = countNodes
|
||||
|
||||
// 离线节点数
|
||||
countOfflineNodes, err := nameservers.SharedNSNodeDAO.CountAllOfflineNodes(tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.CountOfflineNSNodes = countOfflineNodes
|
||||
|
||||
// 按小时统计
|
||||
hourFrom := timeutil.Format("YmdH", time.Now().Add(-23*time.Hour))
|
||||
hourTo := timeutil.Format("YmdH")
|
||||
hourlyStats, err := nameservers.SharedNSRecordHourlyStatDAO.FindHourlyStats(tx, hourFrom, hourTo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, stat := range hourlyStats {
|
||||
result.HourlyTrafficStats = append(result.HourlyTrafficStats, &pb.ComposeNSBoardResponse_HourlyTrafficStat{
|
||||
Hour: stat.Hour,
|
||||
Bytes: int64(stat.Bytes),
|
||||
CountRequests: int64(stat.CountRequests),
|
||||
})
|
||||
}
|
||||
|
||||
// 按天统计
|
||||
dayFrom := timeutil.Format("Ymd", time.Now().AddDate(0, 0, -14))
|
||||
dayTo := timeutil.Format("Ymd")
|
||||
dailyStats, err := nameservers.SharedNSRecordHourlyStatDAO.FindDailyStats(tx, dayFrom, dayTo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, stat := range dailyStats {
|
||||
result.DailyTrafficStats = append(result.DailyTrafficStats, &pb.ComposeNSBoardResponse_DailyTrafficStat{
|
||||
Day: stat.Day,
|
||||
Bytes: int64(stat.Bytes),
|
||||
CountRequests: int64(stat.CountRequests),
|
||||
})
|
||||
}
|
||||
|
||||
// 域名排行
|
||||
topDomainStats, err := nameservers.SharedNSRecordHourlyStatDAO.ListTopDomains(tx, hourFrom, hourTo, 10)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, stat := range topDomainStats {
|
||||
domainName, err := nameservers.SharedNSDomainDAO.FindNSDomainName(tx, int64(stat.DomainId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(domainName) == 0 {
|
||||
continue
|
||||
}
|
||||
result.TopNSDomainStats = append(result.TopNSDomainStats, &pb.ComposeNSBoardResponse_DomainStat{
|
||||
NsDomainId: int64(stat.DomainId),
|
||||
NsDomainName: domainName,
|
||||
CountRequests: int64(stat.CountRequests),
|
||||
Bytes: int64(stat.Bytes),
|
||||
})
|
||||
}
|
||||
|
||||
// 节点排行
|
||||
topNodeStats, err := nameservers.SharedNSRecordHourlyStatDAO.ListTopNodes(tx, hourFrom, hourTo, 10)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, stat := range topNodeStats {
|
||||
nodeName, err := nameservers.SharedNSNodeDAO.FindEnabledNSNodeName(tx, int64(stat.NodeId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(nodeName) == 0 {
|
||||
continue
|
||||
}
|
||||
result.TopNSNodeStats = append(result.TopNSNodeStats, &pb.ComposeNSBoardResponse_NodeStat{
|
||||
NsClusterId: int64(stat.ClusterId),
|
||||
NsNodeId: int64(stat.NodeId),
|
||||
NsNodeName: nodeName,
|
||||
CountRequests: int64(stat.CountRequests),
|
||||
Bytes: int64(stat.Bytes),
|
||||
})
|
||||
}
|
||||
|
||||
// CPU、内存、负载
|
||||
cpuValues, err := models.SharedNodeValueDAO.ListValuesForNSNodes(tx, nodeconfigs.NodeValueItemCPU, "usage", nodeconfigs.NodeValueRangeMinute)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, v := range cpuValues {
|
||||
valueJSON, err := json.Marshal(types.Float32(v.Value))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.CpuNodeValues = append(result.CpuNodeValues, &pb.NodeValue{
|
||||
ValueJSON: valueJSON,
|
||||
CreatedAt: int64(v.CreatedAt),
|
||||
})
|
||||
}
|
||||
|
||||
memoryValues, err := models.SharedNodeValueDAO.ListValuesForNSNodes(tx, nodeconfigs.NodeValueItemMemory, "usage", nodeconfigs.NodeValueRangeMinute)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, v := range memoryValues {
|
||||
valueJSON, err := json.Marshal(types.Float32(v.Value))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.MemoryNodeValues = append(result.MemoryNodeValues, &pb.NodeValue{
|
||||
ValueJSON: valueJSON,
|
||||
CreatedAt: int64(v.CreatedAt),
|
||||
})
|
||||
}
|
||||
|
||||
loadValues, err := models.SharedNodeValueDAO.ListValuesForNSNodes(tx, nodeconfigs.NodeValueItemLoad, "load5m", nodeconfigs.NodeValueRangeMinute)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, v := range loadValues {
|
||||
valueJSON, err := json.Marshal(types.Float32(v.Value))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.LoadNodeValues = append(result.LoadNodeValues, &pb.NodeValue{
|
||||
ValueJSON: valueJSON,
|
||||
CreatedAt: int64(v.CreatedAt),
|
||||
})
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@@ -16,7 +16,7 @@ type NSAccessLogService struct {
|
||||
// CreateNSAccessLogs 创建访问日志
|
||||
func (this *NSAccessLogService) CreateNSAccessLogs(ctx context.Context, req *pb.CreateNSAccessLogsRequest) (*pb.CreateNSAccessLogsResponse, error) {
|
||||
// 校验请求
|
||||
_, _, err := rpcutils.ValidateRequest(ctx, rpcutils.UserTypeDNS)
|
||||
_, _, _, err := rpcutils.ValidateRequest(ctx, rpcutils.UserTypeDNS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user