Compare commits

...

80 Commits

Author SHA1 Message Date
刘祥超
b60bb5f6da 提交SQL 2023-11-19 09:11:07 +08:00
刘祥超
ff4ea41963 节点配置中增加节点IP信息 2023-11-18 12:09:47 +08:00
刘祥超
b7dccad449 实现用户系统手机号码绑定和登录(商业版) 2023-11-17 11:51:29 +08:00
刘祥超
7fead214d4 更新SQL 2023-11-15 19:10:18 +08:00
刘祥超
d9590ec605 创建反向代理时默认不自动重试50X/源站支持404内容自动重试其他源站 2023-11-15 19:05:43 +08:00
刘祥超
20b936580f 版本号修改为1.3.0 2023-11-14 14:47:32 +08:00
刘祥超
b7b43bc31f 限制访问日志中域名能写入的最大长度 2023-11-13 17:12:11 +08:00
刘祥超
6fd4f26755 自定义页面增加例外URL和限制URL设置 2023-11-13 10:46:12 +08:00
刘祥超
f15d114708 自定义页面增加“跳转URL”功能 2023-11-10 16:36:09 +08:00
刘祥超
fc24195b55 增加访问日志中域名长度 2023-11-10 09:56:17 +08:00
刘祥超
ed5de57244 去除一处多余的日志 2023-11-07 17:34:09 +08:00
刘祥超
4ce347738f 修复无法将OSS源站修改为http/https源站的问题 2023-11-04 08:28:08 +08:00
刘祥超
f6e725781c 优化节点阈值设置 2023-11-03 11:20:47 +08:00
刘祥超
55d70418cc 节点健康检查失败时增加节点名称和节点IP提示 2023-11-03 09:54:42 +08:00
刘祥超
7f5b070e36 优化商业版验证 2023-11-02 17:20:12 +08:00
刘祥超
993c7ee822 上传域名统计数据时限制域名长度不能超过64位 2023-11-02 17:19:56 +08:00
刘祥超
b5bb4e0df9 更新数据库 2023-10-30 19:04:23 +08:00
刘祥超
9f120fd0e0 访问日志存储策略增加“停止默认数据库存储”选项 2023-10-30 19:03:39 +08:00
刘祥超
77d614c9ea 实现网络数据包相关统计(商业版本) 2023-10-26 17:17:43 +08:00
刘祥超
531ec3c55d 优化域名解析文字提示 2023-10-17 15:54:08 +08:00
刘祥超
0d6c064194 将版本号修改为1.2.11 2023-10-17 13:49:39 +08:00
刘祥超
180e86c643 修复消息通知不能指定集群的Bug 2023-10-17 13:49:23 +08:00
刘祥超
86b04b2b6b 将临时的1.2.9.1升级程序版本号修改为1.2.10 2023-10-15 15:10:36 +08:00
刘祥超
7a5ec79ace 将版本号修改为1.2.10 2023-10-15 13:34:18 +08:00
刘祥超
7290ffd2cd 取消默认反向代理默认的50X重试 2023-10-15 09:40:39 +08:00
刘祥超
2f361c5bcc 优化消息任务相关代码 2023-10-15 09:39:46 +08:00
刘祥超
500d72aaf3 WAF记录IP动作中IP名单如果为空时,默认为全局黑名单 2023-10-15 09:34:20 +08:00
刘祥超
9fc391d1e8 删除不必要的代码 2023-10-14 18:15:54 +08:00
刘祥超
c86e3e2047 优化消息通知相关代码 2023-10-14 17:16:08 +08:00
刘祥超
7e72a90f53 优化消息发送相关代码/删除监控相关代码 2023-10-12 20:11:21 +08:00
刘祥超
7692fed38d 支持批量复制WAF设置 2023-10-09 19:52:51 +08:00
刘祥超
bdd7d2a181 申请证书任务列表区分管理员和用户 2023-10-09 16:18:32 +08:00
刘祥超
118c3f79e4 证书列表区分管理员和用户证书 2023-10-09 15:54:00 +08:00
刘祥超
804a33a002 访问日志列表搜索增加请求来源查询语法:referer:example.com 2023-10-08 17:52:53 +08:00
刘祥超
fe00588039 集群设置中增加“自动调节系统参数”选项 2023-10-08 15:08:28 +08:00
刘祥超
67aac200a7 修复常用网站、常用集群查询可能因为updatedAt过大导致的SQL错误 2023-09-22 16:41:44 +08:00
刘祥超
3e01ad4b68 节点配置中对父级节点进行排序,以保证查找的稳定性 2023-09-22 11:55:47 +08:00
刘祥超
b39690484e 将升级程序中的1.2.10改成1.2.9.1,方便在测试版本中也能升级 2023-09-18 17:02:54 +08:00
刘祥超
31a69ecb12 将全局设置的TCP相关设置移到“集群设置--网站设置”中 2023-09-18 16:55:45 +08:00
刘祥超
94b95beadf 将全局的通用设置--域名审核设置移到“集群设置--网站设置”中 2023-09-18 16:09:11 +08:00
刘祥超
6143f08cf2 IP名单删除任务完成后删除任务 2023-09-14 09:12:19 +08:00
刘祥超
73a5814fd6 版本号修改为1.2.9 2023-09-13 17:37:41 +08:00
刘祥超
448152d5c2 优化删除IP名单时操作 2023-09-13 17:16:00 +08:00
刘祥超
eedb3fb338 将节点版本号修改为1.2.9 2023-09-12 15:03:00 +08:00
刘祥超
06f6f68f3a 增加自动升级一处WAF规则 2023-09-12 14:59:07 +08:00
刘祥超
903e524e80 优化访客IP地址设置 2023-09-07 18:03:28 +08:00
刘祥超
fa6b4fcaee 套餐增加请求数(日/月)限制 2023-09-07 11:46:03 +08:00
刘祥超
67cc8e515f 修复一个测试用例 2023-09-06 18:19:25 +08:00
刘祥超
fa29817920 统计带宽计算增加最小样本数 2023-09-06 18:14:08 +08:00
刘祥超
794c3bc132 优化套餐升级程序 2023-09-06 18:01:41 +08:00
刘祥超
9e481d31ac 重新实现套餐相关功能 2023-09-06 16:30:47 +08:00
刘祥超
4ebc03af75 调用自定义HTTP DNS时增加action(值为GetDomains) 2023-08-28 16:28:08 +08:00
刘祥超
80e2face67 更新Agent IP库 2023-08-27 11:58:14 +08:00
刘祥超
815a5187d5 反向代理增加是否重试50X选项,默认为启用 2023-08-20 15:49:34 +08:00
刘祥超
1d7bc42fba 修复节点状态监控中磁盘空间可能为0的问题 2023-08-18 16:01:24 +08:00
刘祥超
1eb9cca793 将WAF策略中的默认省份封禁提示内容长度从255修改为65535 2023-08-14 12:54:11 +08:00
刘祥超
8766f5b1a9 修改版本号为1.2.8 2023-08-14 12:24:29 +08:00
刘祥超
823e42626d DNS任务增加失败重试 2023-08-13 15:26:59 +08:00
刘祥超
c5308cf41c 生成节点时去除停用的WAF规则集 2023-08-13 10:51:52 +08:00
刘祥超
3053157c6e 将节点的api.yaml改为api_node.yaml 2023-08-12 15:27:09 +08:00
刘祥超
d1ba141c65 优化错误处理相关代码 2023-08-11 16:13:33 +08:00
刘祥超
034ababead 静态分发增加例外URL、限制URL、排除隐藏文件等选项 2023-08-10 11:27:05 +08:00
刘祥超
f5450e37be WAF策略可以自定义默认的区域/省份封禁提示 2023-08-10 10:30:50 +08:00
刘祥超
549fca93e6 将版本号修改为1.2.7 2023-08-09 14:24:16 +08:00
刘祥超
efa0f33256 Update .golangci.yaml 2023-08-09 08:11:53 +08:00
刘祥超
977a12843c 添加golangci-lint配置 2023-08-08 18:36:24 +08:00
刘祥超
6de2834a8c 优化代码 2023-08-08 16:46:17 +08:00
刘祥超
51f91e1603 优化代码 2023-08-08 12:09:20 +08:00
刘祥超
d27b7c8fa1 允许用户调用获取缓存策略信息API 2023-08-07 19:55:57 +08:00
刘祥超
c5098c66af 缓存策略增加预热超时时间设置(默认20分钟) 2023-08-06 17:07:48 +08:00
刘祥超
c2635b0d04 修复默认WAF策略模板中分组不能默认关闭的问题 2023-08-02 17:15:26 +08:00
刘祥超
41a1a6a2e5 更新SQL 2023-08-02 17:02:39 +08:00
刘祥超
e437117e69 WAF策略增加“最多检查内容尺寸“选项 2023-08-02 16:59:38 +08:00
刘祥超
fdc8f78229 优化CC配置 2023-08-01 19:50:01 +08:00
刘祥超
2f78d76a1a 修复系统服务相关代码可能不执行的问题 2023-08-01 16:19:05 +08:00
刘祥超
742f2f0216 启动时自动创建相关软链接 2023-08-01 10:47:13 +08:00
刘祥超
89a606329f 修复自定义页面无法保存的问题 2023-07-31 09:46:00 +08:00
刘祥超
3bba79d14c 优化统计 2023-07-31 09:45:48 +08:00
刘祥超
9f9787e30f 版本号更改为1.2.6 2023-07-28 09:27:08 +08:00
刘祥超
529016d4d5 版本号更改为1.2.5 2023-07-26 15:30:37 +08:00
160 changed files with 16632 additions and 3083 deletions

75
.golangci.yaml Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -61,11 +61,12 @@ func (this *DNSTaskDAO) CreateDNSTask(tx *dbs.Tx, clusterId int64, serverId int6
"error": "",
"version": time.Now().UnixNano(),
}, maps.Map{
"updatedAt": time.Now().Unix(),
"isDone": false,
"isOk": false,
"error": "",
"version": time.Now().UnixNano(),
"updatedAt": time.Now().Unix(),
"isDone": false,
"isOk": false,
"error": "",
"version": time.Now().UnixNano(),
"countFails": 0,
})
if err != nil {
return err
@@ -108,7 +109,7 @@ func (this *DNSTaskDAO) CreateDomainTask(tx *dbs.Tx, domainId int64, taskType DN
// FindAllDoingTasks 查找所有正在执行的任务
func (this *DNSTaskDAO) FindAllDoingTasks(tx *dbs.Tx) (result []*DNSTask, err error) {
_, err = this.Query(tx).
Attr("isDone", 0).
Where("(isDone=0 OR (isDone=1 AND isOk=0 AND countFails<3))"). // 3 = retry times
Asc("version").
AscPk().
Slice(&result).
@@ -171,6 +172,7 @@ func (this *DNSTaskDAO) UpdateDNSTaskError(tx *dbs.Tx, taskId int64, err string)
op.IsDone = true
op.Error = err
op.IsOk = false
op.CountFails = dbs.SQL("countFails+1")
return this.Save(tx, op)
}
@@ -197,6 +199,7 @@ func (this *DNSTaskDAO) UpdateDNSTaskDone(tx *dbs.Tx, taskId int64, taskVersion
op.Id = taskId
op.IsDone = true
op.IsOk = true
op.CountFails = 0
op.Error = ""
return this.Save(tx, op)
}
@@ -219,6 +222,7 @@ func (this *DNSTaskDAO) UpdateClusterDNSTasksDone(tx *dbs.Tx, clusterId int64, m
Set("isDone", true).
Set("isOk", true).
Set("error", "").
Set("countFails", 0).
UpdateQuickly()
}

View File

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

View File

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

View File

@@ -232,7 +232,7 @@ Loop:
// CreateHTTPAccessLog 写入单条访问日志
func (this *HTTPAccessLogDAO) CreateHTTPAccessLog(tx *dbs.Tx, dao *HTTPAccessLogDAO, accessLog *pb.HTTPAccessLog) error {
var day = ""
var day string
// 注意:如果你修改了 TimeISO8601 的逻辑,这里也需要同步修改
if len(accessLog.TimeISO8601) > 10 {
day = strings.ReplaceAll(accessLog.TimeISO8601[:10], "-", "")
@@ -245,7 +245,7 @@ func (this *HTTPAccessLogDAO) CreateHTTPAccessLog(tx *dbs.Tx, dao *HTTPAccessLog
return err
}
fields := map[string]interface{}{}
var fields = map[string]any{}
fields["serverId"] = accessLog.ServerId
fields["nodeId"] = accessLog.NodeId
fields["status"] = accessLog.Status
@@ -265,7 +265,11 @@ func (this *HTTPAccessLogDAO) CreateHTTPAccessLog(tx *dbs.Tx, dao *HTTPAccessLog
fields["remoteAddr"] = accessLog.RemoteAddr
}
if tableDef.HasDomain {
fields["domain"] = accessLog.Host
if len(accessLog.Host) > 128 {
fields["domain"] = accessLog.Host[:128]
} else {
fields["domain"] = accessLog.Host
}
}
content, err := json.Marshal(accessLog)
@@ -461,6 +465,7 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx,
var protoReg = regexp.MustCompile(`proto:(\S+)`)
var schemeReg = regexp.MustCompile(`scheme:(\S+)`)
var methodReg = regexp.MustCompile(`(?:method|requestMethod):(\S+)`)
var refererReg = regexp.MustCompile(`referer:(\S+)`)
var count = len(tableQueries)
var wg = &sync.WaitGroup{}
@@ -613,6 +618,11 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx,
var matches = methodReg.FindStringSubmatch(keyword)
query.Where("JSON_EXTRACT(content, '$.requestMethod')=:keyword").
Param("keyword", strings.ToUpper(matches[1]))
} else if refererReg.MatchString(keyword) {
isSpecialKeyword = true
var matches = refererReg.FindStringSubmatch(keyword)
query.Where("JSON_EXTRACT(content, '$.referer') LIKE :keyword").
Param("keyword", dbutils.QuoteLike(matches[1]))
}
if !isSpecialKeyword {
if regexp.MustCompile(`^ip:.+`).MatchString(keyword) {
@@ -857,8 +867,4 @@ func (this *HTTPAccessLogDAO) SetupQueue() {
oldAccessLogQueue = accessLogQueue
accessLogQueue = make(chan *pb.HTTPAccessLog, config.MaxLength)
}
if Tea.IsTesting() {
remotelogs.Println("HTTP_ACCESS_LOG_QUEUE", "change queue "+string(configJSON))
}
}

View File

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

View File

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

View File

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

View File

@@ -96,7 +96,7 @@ func (this *HTTPCachePolicyDAO) FindAllEnabledCachePolicies(tx *dbs.Tx) (result
}
// CreateCachePolicy 创建缓存策略
func (this *HTTPCachePolicyDAO) CreateCachePolicy(tx *dbs.Tx, isOn bool, name string, description string, capacityJSON []byte, maxSizeJSON []byte, storageType string, storageOptionsJSON []byte, syncCompressionCache bool) (int64, error) {
func (this *HTTPCachePolicyDAO) CreateCachePolicy(tx *dbs.Tx, isOn bool, name string, description string, capacityJSON []byte, maxSizeJSON []byte, storageType string, storageOptionsJSON []byte, syncCompressionCache bool, fetchTimeoutJSON []byte) (int64, error) {
var op = NewHTTPCachePolicyOperator()
op.State = HTTPCachePolicyStateEnabled
op.IsOn = isOn
@@ -114,6 +114,10 @@ func (this *HTTPCachePolicyDAO) CreateCachePolicy(tx *dbs.Tx, isOn bool, name st
}
op.SyncCompressionCache = syncCompressionCache
if len(fetchTimeoutJSON) > 0 {
op.FetchTimeout = fetchTimeoutJSON
}
// 默认的缓存条件
cacheRef := &serverconfigs.HTTPCacheRef{
IsOn: true,
@@ -183,7 +187,7 @@ func (this *HTTPCachePolicyDAO) CreateDefaultCachePolicy(tx *dbs.Tx, name string
return 0, err
}
policyId, err := this.CreateCachePolicy(tx, true, "\""+name+"\"缓存策略", "默认创建的缓存策略", capacityJSON, maxSizeJSON, serverconfigs.CachePolicyStorageFile, storageOptionsJSON, false)
policyId, err := this.CreateCachePolicy(tx, true, "\""+name+"\"缓存策略", "默认创建的缓存策略", capacityJSON, maxSizeJSON, serverconfigs.CachePolicyStorageFile, storageOptionsJSON, false, nil)
if err != nil {
return 0, err
}
@@ -191,7 +195,7 @@ func (this *HTTPCachePolicyDAO) CreateDefaultCachePolicy(tx *dbs.Tx, name string
}
// UpdateCachePolicy 修改缓存策略
func (this *HTTPCachePolicyDAO) UpdateCachePolicy(tx *dbs.Tx, policyId int64, isOn bool, name string, description string, capacityJSON []byte, maxSizeJSON []byte, storageType string, storageOptionsJSON []byte, syncCompressionCache bool) error {
func (this *HTTPCachePolicyDAO) UpdateCachePolicy(tx *dbs.Tx, policyId int64, isOn bool, name string, description string, capacityJSON []byte, maxSizeJSON []byte, storageType string, storageOptionsJSON []byte, syncCompressionCache bool, fetchTimeoutJSON []byte) error {
if policyId <= 0 {
return errors.New("invalid policyId")
}
@@ -212,6 +216,9 @@ func (this *HTTPCachePolicyDAO) UpdateCachePolicy(tx *dbs.Tx, policyId int64, is
op.Options = storageOptionsJSON
}
op.SyncCompressionCache = syncCompressionCache
if len(fetchTimeoutJSON) > 0 {
op.FetchTimeout = fetchTimeoutJSON
}
err := this.Save(tx, op)
if err != nil {
return err
@@ -237,7 +244,7 @@ func (this *HTTPCachePolicyDAO) ComposeCachePolicy(tx *dbs.Tx, policyId int64, c
if policy == nil {
return nil, nil
}
config := &serverconfigs.HTTPCachePolicy{}
var config = &serverconfigs.HTTPCachePolicy{}
config.Id = int64(policy.Id)
config.IsOn = policy.IsOn
config.Name = policy.Name
@@ -246,7 +253,7 @@ func (this *HTTPCachePolicyDAO) ComposeCachePolicy(tx *dbs.Tx, policyId int64, c
// capacity
if IsNotNull(policy.Capacity) {
capacityConfig := &shared.SizeCapacity{}
var capacityConfig = &shared.SizeCapacity{}
err = json.Unmarshal(policy.Capacity, capacityConfig)
if err != nil {
return nil, err
@@ -256,7 +263,7 @@ func (this *HTTPCachePolicyDAO) ComposeCachePolicy(tx *dbs.Tx, policyId int64, c
// max size
if IsNotNull(policy.MaxSize) {
maxSizeConfig := &shared.SizeCapacity{}
var maxSizeConfig = &shared.SizeCapacity{}
err = json.Unmarshal(policy.MaxSize, maxSizeConfig)
if err != nil {
return nil, err
@@ -268,7 +275,7 @@ func (this *HTTPCachePolicyDAO) ComposeCachePolicy(tx *dbs.Tx, policyId int64, c
// options
if IsNotNull(policy.Options) {
m := map[string]interface{}{}
var m = map[string]any{}
err = json.Unmarshal(policy.Options, &m)
if err != nil {
return nil, errors.Wrap(err)
@@ -278,7 +285,7 @@ func (this *HTTPCachePolicyDAO) ComposeCachePolicy(tx *dbs.Tx, policyId int64, c
// refs
if IsNotNull(policy.Refs) {
refs := []*serverconfigs.HTTPCacheRef{}
var refs = []*serverconfigs.HTTPCacheRef{}
err = json.Unmarshal(policy.Refs, &refs)
if err != nil {
return nil, err
@@ -286,6 +293,16 @@ func (this *HTTPCachePolicyDAO) ComposeCachePolicy(tx *dbs.Tx, policyId int64, c
config.CacheRefs = refs
}
// fetch timeout
if IsNotNull(policy.FetchTimeout) {
var timeoutDuration = &shared.TimeDuration{}
err = json.Unmarshal(policy.FetchTimeout, timeoutDuration)
if err != nil {
return nil, err
}
config.FetchTimeout = timeoutDuration
}
if cacheMap != nil {
cacheMap.Put(cacheKey, config)
}

View File

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

View File

@@ -172,16 +172,18 @@ func (this *HTTPFirewallPolicyDAO) CreateDefaultFirewallPolicy(tx *dbs.Tx, name
// 初始化
var groupCodes = []string{}
templatePolicy := firewallconfigs.HTTPFirewallTemplate()
var templatePolicy = firewallconfigs.HTTPFirewallTemplate()
for _, group := range templatePolicy.AllRuleGroups() {
groupCodes = append(groupCodes, group.Code)
if group.IsOn {
groupCodes = append(groupCodes, group.Code)
}
}
var inboundConfig = &firewallconfigs.HTTPFirewallInboundConfig{IsOn: true}
var outboundConfig = &firewallconfigs.HTTPFirewallOutboundConfig{IsOn: true}
if templatePolicy.Inbound != nil {
for _, group := range templatePolicy.Inbound.Groups {
isOn := lists.ContainsString(groupCodes, group.Code)
var isOn = lists.ContainsString(groupCodes, group.Code)
group.IsOn = isOn
groupId, err := SharedHTTPFirewallRuleGroupDAO.CreateGroupFromConfig(tx, group)
@@ -196,7 +198,7 @@ func (this *HTTPFirewallPolicyDAO) CreateDefaultFirewallPolicy(tx *dbs.Tx, name
}
if templatePolicy.Outbound != nil {
for _, group := range templatePolicy.Outbound.Groups {
isOn := lists.ContainsString(groupCodes, group.Code)
var isOn = lists.ContainsString(groupCodes, group.Code)
group.IsOn = isOn
groupId, err := SharedHTTPFirewallRuleGroupDAO.CreateGroupFromConfig(tx, group)
@@ -277,6 +279,31 @@ func (this *HTTPFirewallPolicyDAO) UpdateFirewallPolicyInbound(tx *dbs.Tx, polic
return this.NotifyUpdate(tx, policyId)
}
// UpdateFirewallPolicyInboundRegion 修改入站封禁区域设置
func (this *HTTPFirewallPolicyDAO) UpdateFirewallPolicyInboundRegion(tx *dbs.Tx, policyId int64, regionConfig *firewallconfigs.HTTPFirewallRegionConfig) error {
var inboundConfig = &firewallconfigs.HTTPFirewallInboundConfig{IsOn: true}
inboundJSON, err := this.Query(tx).
Pk(policyId).
Result("inbound").
FindJSONCol()
if err != nil {
return err
}
if IsNotNull(inboundJSON) {
err = json.Unmarshal(inboundJSON, inboundConfig)
if err != nil {
return err
}
}
inboundConfig.Region = regionConfig
newInboundJSON, err := json.Marshal(inboundConfig)
if err != nil {
return err
}
return this.UpdateFirewallPolicyInbound(tx, policyId, newInboundJSON)
}
// UpdateFirewallPolicy 修改策略
func (this *HTTPFirewallPolicyDAO) UpdateFirewallPolicy(tx *dbs.Tx,
policyId int64,
@@ -290,7 +317,10 @@ func (this *HTTPFirewallPolicyDAO) UpdateFirewallPolicy(tx *dbs.Tx,
mode firewallconfigs.FirewallMode,
useLocalFirewall bool,
synFloodConfig *firewallconfigs.SYNFloodConfig,
logConfig *firewallconfigs.HTTPFirewallPolicyLogConfig) error {
logConfig *firewallconfigs.HTTPFirewallPolicyLogConfig,
maxRequestBodySize int64,
denyCountryHTML string,
denyProvinceHTML string) error {
if policyId <= 0 {
return errors.New("invalid policyId")
}
@@ -338,6 +368,10 @@ func (this *HTTPFirewallPolicyDAO) UpdateFirewallPolicy(tx *dbs.Tx,
}
op.UseLocalFirewall = useLocalFirewall
op.MaxRequestBodySize = maxRequestBodySize
op.DenyCountryHTML = denyCountryHTML
op.DenyProvinceHTML = denyProvinceHTML
err := this.Save(tx, op)
if err != nil {
return err
@@ -390,7 +424,7 @@ func (this *HTTPFirewallPolicyDAO) ListEnabledFirewallPolicies(tx *dbs.Tx, clust
}
// ComposeFirewallPolicy 组合策略配置
func (this *HTTPFirewallPolicyDAO) ComposeFirewallPolicy(tx *dbs.Tx, policyId int64, cacheMap *utils.CacheMap) (*firewallconfigs.HTTPFirewallPolicy, error) {
func (this *HTTPFirewallPolicyDAO) ComposeFirewallPolicy(tx *dbs.Tx, policyId int64, forNode bool, cacheMap *utils.CacheMap) (*firewallconfigs.HTTPFirewallPolicy, error) {
if cacheMap == nil {
cacheMap = utils.NewCacheMap()
}
@@ -414,6 +448,9 @@ func (this *HTTPFirewallPolicyDAO) ComposeFirewallPolicy(tx *dbs.Tx, policyId in
config.Name = policy.Name
config.Description = policy.Description
config.UseLocalFirewall = policy.UseLocalFirewall == 1
config.MaxRequestBodySize = int64(policy.MaxRequestBodySize)
config.DenyCountryHTML = policy.DenyCountryHTML
config.DenyProvinceHTML = policy.DenyProvinceHTML
if len(policy.Mode) == 0 {
policy.Mode = firewallconfigs.FirewallModeDefend
@@ -421,18 +458,18 @@ func (this *HTTPFirewallPolicyDAO) ComposeFirewallPolicy(tx *dbs.Tx, policyId in
config.Mode = policy.Mode
// Inbound
inbound := &firewallconfigs.HTTPFirewallInboundConfig{}
var inbound = &firewallconfigs.HTTPFirewallInboundConfig{}
if IsNotNull(policy.Inbound) {
err = json.Unmarshal(policy.Inbound, inbound)
if err != nil {
return nil, err
}
if len(inbound.GroupRefs) > 0 {
resultGroupRefs := []*firewallconfigs.HTTPFirewallRuleGroupRef{}
resultGroups := []*firewallconfigs.HTTPFirewallRuleGroup{}
var resultGroupRefs = []*firewallconfigs.HTTPFirewallRuleGroupRef{}
var resultGroups = []*firewallconfigs.HTTPFirewallRuleGroup{}
for _, groupRef := range inbound.GroupRefs {
groupConfig, err := SharedHTTPFirewallRuleGroupDAO.ComposeFirewallRuleGroup(tx, groupRef.GroupId)
groupConfig, err := SharedHTTPFirewallRuleGroupDAO.ComposeFirewallRuleGroup(tx, groupRef.GroupId, forNode)
if err != nil {
return nil, err
}
@@ -449,18 +486,18 @@ func (this *HTTPFirewallPolicyDAO) ComposeFirewallPolicy(tx *dbs.Tx, policyId in
config.Inbound = inbound
// Outbound
outbound := &firewallconfigs.HTTPFirewallOutboundConfig{}
var outbound = &firewallconfigs.HTTPFirewallOutboundConfig{}
if IsNotNull(policy.Outbound) {
err = json.Unmarshal(policy.Outbound, outbound)
if err != nil {
return nil, err
}
if len(outbound.GroupRefs) > 0 {
resultGroupRefs := []*firewallconfigs.HTTPFirewallRuleGroupRef{}
resultGroups := []*firewallconfigs.HTTPFirewallRuleGroup{}
var resultGroupRefs = []*firewallconfigs.HTTPFirewallRuleGroupRef{}
var resultGroups = []*firewallconfigs.HTTPFirewallRuleGroup{}
for _, groupRef := range outbound.GroupRefs {
groupConfig, err := SharedHTTPFirewallRuleGroupDAO.ComposeFirewallRuleGroup(tx, groupRef.GroupId)
groupConfig, err := SharedHTTPFirewallRuleGroupDAO.ComposeFirewallRuleGroup(tx, groupRef.GroupId, forNode)
if err != nil {
return nil, err
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -101,7 +101,7 @@ func (this *HTTPWebDAO) ComposeWebConfig(tx *dbs.Tx, webId int64, isLocationOrGr
// root
if IsNotNull(web.Root) {
var rootConfig = &serverconfigs.HTTPRootConfig{}
var rootConfig = serverconfigs.NewHTTPRootConfig()
err = json.Unmarshal(web.Root, rootConfig)
if err != nil {
return nil, err
@@ -301,7 +301,7 @@ func (this *HTTPWebDAO) ComposeWebConfig(tx *dbs.Tx, webId int64, isLocationOrGr
// 自定义防火墙设置
if firewallRef.FirewallPolicyId > 0 {
firewallPolicy, err := SharedHTTPFirewallPolicyDAO.ComposeFirewallPolicy(tx, firewallRef.FirewallPolicyId, cacheMap)
firewallPolicy, err := SharedHTTPFirewallPolicyDAO.ComposeFirewallPolicy(tx, firewallRef.FirewallPolicyId, forNode, cacheMap)
if err != nil {
return nil, err
}
@@ -519,6 +519,14 @@ func (this *HTTPWebDAO) ComposeWebConfig(tx *dbs.Tx, webId int64, isLocationOrGr
}
if this.shouldCompose(isLocationOrGroup, forNode, ccConfig.IsPrior, ccConfig.IsOn) {
config.CC = ccConfig
if forNode {
for index, threshold := range ccConfig.Thresholds {
if index < len(serverconfigs.DefaultHTTPCCThresholds) {
threshold.MergeIfEmpty(serverconfigs.DefaultHTTPCCThresholds[index])
}
}
}
}
}
@@ -568,6 +576,7 @@ func (this *HTTPWebDAO) CreateWeb(tx *dbs.Tx, adminId int64, userId int64, rootJ
var remoteAddrConfig = &serverconfigs.HTTPRemoteAddrConfig{
IsOn: true,
Value: "${rawRemoteAddr}",
Type: serverconfigs.HTTPRemoteAddrTypeDefault,
}
remoteAddrConfigJSON, err := json.Marshal(remoteAddrConfig)
if err != nil {

View File

@@ -674,6 +674,9 @@ func (this *IPItemDAO) NotifyUpdate(tx *dbs.Tx, itemId int64) error {
}
} else {
clusterIds, err := SharedNodeClusterDAO.FindAllEnabledNodeClusterIds(tx)
if err != nil {
return err
}
for _, clusterId := range clusterIds {
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeIPItemChanged)
if err != nil {

View File

@@ -3,6 +3,7 @@ package models
import (
"encoding/json"
"errors"
"fmt"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/regions"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
@@ -299,7 +300,7 @@ func (this *IPLibraryFileDAO) GenerateIPLibrary(tx *dbs.Tx, libraryFileId int64)
var libraryFile = one.(*IPLibraryFile)
template, err := iplibrary.NewTemplate(libraryFile.Template)
if err != nil {
return errors.New("create template from '" + libraryFile.Template + "' failed: " + err.Error())
return fmt.Errorf("create template from '%s' failed: %w", libraryFile.Template, err)
}
var fileId = int64(libraryFile.FileId)
@@ -314,17 +315,17 @@ func (this *IPLibraryFileDAO) GenerateIPLibrary(tx *dbs.Tx, libraryFileId int64)
if os.IsNotExist(err) {
err = os.Mkdir(dir, 0777)
if err != nil {
return errors.New("can not open dir '" + dir + "' to write: " + err.Error())
return fmt.Errorf("can not open dir '%s' to write: %w", dir, err)
}
} else {
return errors.New("can not open dir '" + dir + "' to write: " + err.Error())
return fmt.Errorf("can not open dir '%s' to write: %w", dir, err)
}
} else if !stat.IsDir() {
_ = os.Remove(dir)
err = os.Mkdir(dir, 0777)
if err != nil {
return errors.New("can not open dir '" + dir + "' to write: " + err.Error())
return fmt.Errorf("can not open dir '%s' to write: %w", dir, err)
}
}
@@ -428,7 +429,7 @@ func (this *IPLibraryFileDAO) GenerateIPLibrary(tx *dbs.Tx, libraryFileId int64)
err = writer.WriteMeta()
if err != nil {
return errors.New("write meta failed: " + err.Error())
return fmt.Errorf("write meta failed: %w", err)
}
chunkIds, err := SharedFileChunkDAO.FindAllFileChunkIds(tx, fileId)
@@ -503,7 +504,7 @@ func (this *IPLibraryFileDAO) GenerateIPLibrary(tx *dbs.Tx, libraryFileId int64)
err = writer.Write(ipFrom, ipTo, countryId, provinceId, cityId, townId, providerId)
if err != nil {
return errors.New("write failed: " + err.Error())
return fmt.Errorf("write failed: %w", err)
}
return nil
@@ -536,7 +537,7 @@ func (this *IPLibraryFileDAO) GenerateIPLibrary(tx *dbs.Tx, libraryFileId int64)
// 将生成的内容写入到文件
stat, err = os.Stat(filePath)
if err != nil {
return errors.New("stat generated file failed: " + err.Error())
return fmt.Errorf("stat generated file failed: %w", err)
}
generatedFileId, err := SharedFileDAO.CreateFile(tx, 0, 0, "ipLibraryFile", "", libraryCode+".db", stat.Size(), "", false)
if err != nil {
@@ -545,7 +546,7 @@ func (this *IPLibraryFileDAO) GenerateIPLibrary(tx *dbs.Tx, libraryFileId int64)
fp, err := os.Open(filePath)
if err != nil {
return errors.New("open generated file failed: " + err.Error())
return fmt.Errorf("open generated file failed: %w", err)
}
var buf = make([]byte, 256*1024)
for {

View File

@@ -11,6 +11,7 @@ import (
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
)
@@ -61,12 +62,16 @@ func (this *IPListDAO) EnableIPList(tx *dbs.Tx, id int64) error {
}
// DisableIPList 禁用条目
func (this *IPListDAO) DisableIPList(tx *dbs.Tx, id int64) error {
func (this *IPListDAO) DisableIPList(tx *dbs.Tx, listId int64) error {
_, err := this.Query(tx).
Pk(id).
Pk(listId).
Set("state", IPListStateDisabled).
Update()
return err
if err != nil {
return err
}
return this.NotifyUpdate(tx, listId, NodeTaskTypeIPListDeleted+"@"+string(maps.Map{"listId": listId}.AsJSON()))
}
// FindEnabledIPList 查找启用中的条目
@@ -258,11 +263,35 @@ func (this *IPListDAO) ExistsEnabledIPList(tx *dbs.Tx, listId int64) (bool, erro
// NotifyUpdate 通知更新
func (this *IPListDAO) NotifyUpdate(tx *dbs.Tx, listId int64, taskType NodeTaskType) error {
// WAF策略中的
httpFirewallPolicyIds, err := SharedHTTPFirewallPolicyDAO.FindEnabledFirewallPolicyIdsWithIPListId(tx, listId)
if err != nil {
return err
}
resultClusterIds := []int64{}
// 规则集动作中使用此名单的策略
ruleSetIds, err := SharedHTTPFirewallRuleSetDAO.FindAllEnabledRuleSetIdsWithIPListId(tx, listId)
if err != nil {
return err
}
for _, ruleSetId := range ruleSetIds {
ruleGroupId, err := SharedHTTPFirewallRuleGroupDAO.FindRuleGroupIdWithRuleSetId(tx, ruleSetId)
if err != nil {
return err
}
if ruleGroupId > 0 {
policyId, err := SharedHTTPFirewallPolicyDAO.FindEnabledFirewallPolicyIdWithRuleGroupId(tx, ruleGroupId)
if err != nil {
return err
}
if policyId > 0 && !lists.ContainsInt64(httpFirewallPolicyIds, policyId) {
httpFirewallPolicyIds = append(httpFirewallPolicyIds, policyId)
}
}
}
// 查找集群
var resultClusterIds = []int64{}
for _, policyId := range httpFirewallPolicyIds {
// 集群
clusterIds, err := SharedNodeClusterDAO.FindAllEnabledNodeClusterIdsWithHTTPFirewallPolicyId(tx, policyId)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -126,7 +126,7 @@ func (this *NodeClusterDAO) FindAllEnableClusterIds(tx *dbs.Tx) (result []int64,
}
// CreateCluster 创建集群
func (this *NodeClusterDAO) CreateCluster(tx *dbs.Tx, adminId int64, name string, grantId int64, installDir string, dnsDomainId int64, dnsName string, dnsTTL int32, cachePolicyId int64, httpFirewallPolicyId int64, systemServices map[string]maps.Map, globalServerConfig *serverconfigs.GlobalServerConfig, autoInstallNftables bool) (clusterId int64, err error) {
func (this *NodeClusterDAO) CreateCluster(tx *dbs.Tx, adminId int64, name string, grantId int64, installDir string, dnsDomainId int64, dnsName string, dnsTTL int32, cachePolicyId int64, httpFirewallPolicyId int64, systemServices map[string]maps.Map, globalServerConfig *serverconfigs.GlobalServerConfig, autoInstallNftables bool, autoSystemTuning bool) (clusterId int64, err error) {
uniqueId, err := this.GenUniqueId(tx)
if err != nil {
return 0, err
@@ -189,6 +189,7 @@ func (this *NodeClusterDAO) CreateCluster(tx *dbs.Tx, adminId int64, name string
op.UniqueId = uniqueId
op.Secret = secret
op.AutoInstallNftables = autoInstallNftables
op.AutoSystemTuning = autoSystemTuning
op.State = NodeClusterStateEnabled
err = this.Save(tx, op)
if err != nil {
@@ -199,7 +200,7 @@ func (this *NodeClusterDAO) CreateCluster(tx *dbs.Tx, adminId int64, name string
}
// UpdateCluster 修改集群
func (this *NodeClusterDAO) UpdateCluster(tx *dbs.Tx, clusterId int64, name string, grantId int64, installDir string, timezone string, nodeMaxThreads int32, autoOpenPorts bool, clockConfig *nodeconfigs.ClockConfig, autoRemoteStart bool, autoInstallTables bool, sshParams *nodeconfigs.SSHParams) error {
func (this *NodeClusterDAO) UpdateCluster(tx *dbs.Tx, clusterId int64, name string, grantId int64, installDir string, timezone string, nodeMaxThreads int32, autoOpenPorts bool, clockConfig *nodeconfigs.ClockConfig, autoRemoteStart bool, autoInstallTables bool, sshParams *nodeconfigs.SSHParams, autoSystemTuning bool) error {
if clusterId <= 0 {
return errors.New("invalid clusterId")
}
@@ -226,6 +227,7 @@ func (this *NodeClusterDAO) UpdateCluster(tx *dbs.Tx, clusterId int64, name stri
op.AutoRemoteStart = autoRemoteStart
op.AutoInstallNftables = autoInstallTables
op.AutoSystemTuning = autoSystemTuning
if sshParams != nil {
sshParamsJSON, err := json.Marshal(sshParams)
@@ -950,11 +952,12 @@ func (this *NodeClusterDAO) GenUniqueId(tx *dbs.Tx) (string, error) {
// FindLatestNodeClusters 查询最近访问的集群
func (this *NodeClusterDAO) FindLatestNodeClusters(tx *dbs.Tx, size int64) (result []*NodeCluster, err error) {
itemTable := SharedLatestItemDAO.Table
itemType := LatestItemTypeCluster
var itemTable = SharedLatestItemDAO.Table
var itemType = LatestItemTypeCluster
_, err = this.Query(tx).
Result(this.Table+".id", this.Table+".name").
Join(SharedLatestItemDAO, dbs.QueryJoinRight, this.Table+".id="+itemTable+".itemId AND "+itemTable+".itemType='"+itemType+"'").
Where(itemTable + ".updatedAt<=UNIX_TIMESTAMP()"). // VERY IMPORTANT
Asc("CEIL((UNIX_TIMESTAMP() - " + itemTable + ".updatedAt) / (7 * 86400))"). // 优先一个星期以内的
Desc(itemTable + ".count").
State(NodeClusterStateEnabled).
@@ -1018,7 +1021,7 @@ func (this *NodeClusterDAO) FindClusterBasicInfo(tx *dbs.Tx, clusterId int64, ca
cluster, err := this.Query(tx).
Pk(clusterId).
State(NodeClusterStateEnabled).
Result("id", "name", "timeZone", "nodeMaxThreads", "cachePolicyId", "httpFirewallPolicyId", "autoOpenPorts", "webp", "uam", "cc", "httpPages", "http3", "isOn", "ddosProtection", "clock", "globalServerConfig", "autoInstallNftables").
Result("id", "name", "timeZone", "nodeMaxThreads", "cachePolicyId", "httpFirewallPolicyId", "autoOpenPorts", "webp", "uam", "cc", "httpPages", "http3", "isOn", "ddosProtection", "clock", "globalServerConfig", "autoInstallNftables", "autoSystemTuning", "networkSecurity").
Find()
if err != nil || cluster == nil {
return nil, err
@@ -1266,6 +1269,57 @@ func (this *NodeClusterDAO) FindClusterHTTP3Policy(tx *dbs.Tx, clusterId int64,
return policy, nil
}
// UpdateClusterNetworkSecurityPolicy 修改网络安全策略设置
func (this *NodeClusterDAO) UpdateClusterNetworkSecurityPolicy(tx *dbs.Tx, clusterId int64, networkSecurityPolicy *nodeconfigs.NetworkSecurityPolicy) error {
if networkSecurityPolicy == nil {
networkSecurityPolicy = nodeconfigs.NewNetworkSecurityPolicy()
}
networkSecurityPolicyJSON, err := json.Marshal(networkSecurityPolicy)
if err != nil {
return err
}
err = this.Query(tx).
Pk(clusterId).
Set("networkSecurity", networkSecurityPolicyJSON).
UpdateQuickly()
if err != nil {
return err
}
return this.NotifyNetworkSecurityUpdate(tx, clusterId)
}
// FindClusterNetworkSecurityPolicy 查询网络安全策略设置
func (this *NodeClusterDAO) FindClusterNetworkSecurityPolicy(tx *dbs.Tx, clusterId int64, cacheMap *utils.CacheMap) (*nodeconfigs.NetworkSecurityPolicy, error) {
var cacheKey = this.Table + ":FindClusterNetworkSecurityPolicy:" + types.String(clusterId)
if cacheMap != nil {
cache, ok := cacheMap.Get(cacheKey)
if ok {
return cache.(*nodeconfigs.NetworkSecurityPolicy), nil
}
}
networkSecurityPolicyJSON, err := this.Query(tx).
Pk(clusterId).
Result("networkSecurity").
FindJSONCol()
if err != nil {
return nil, err
}
if IsNull(networkSecurityPolicyJSON) {
return nodeconfigs.NewNetworkSecurityPolicy(), nil
}
var policy = nodeconfigs.NewNetworkSecurityPolicy()
err = json.Unmarshal(networkSecurityPolicyJSON, policy)
if err != nil {
return nil, err
}
return policy, nil
}
// UpdateClusterHTTPPagesPolicy 修改自定义页面设置
func (this *NodeClusterDAO) UpdateClusterHTTPPagesPolicy(tx *dbs.Tx, clusterId int64, httpPagesPolicy *nodeconfigs.HTTPPagesPolicy) error {
if httpPagesPolicy == nil {
@@ -1449,6 +1503,11 @@ func (this *NodeClusterDAO) NotifyHTTP3Update(tx *dbs.Tx, clusterId int64) error
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeHTTP3PolicyChanged)
}
// NotifyNetworkSecurityUpdate 通知网络安全策略更新
func (this *NodeClusterDAO) NotifyNetworkSecurityUpdate(tx *dbs.Tx, clusterId int64) error {
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeNetworkSecurityPolicyChanged)
}
// NotifyHTTPPagesPolicyUpdate 通知HTTP Pages更新
func (this *NodeClusterDAO) NotifyHTTPPagesPolicyUpdate(tx *dbs.Tx, clusterId int64) error {
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeHTTPPagesPolicyChanged)

View File

@@ -43,6 +43,8 @@ const (
NodeClusterField_HttpPages dbs.FieldName = "httpPages" // 自定义页面设置
NodeClusterField_Cc dbs.FieldName = "cc" // CC设置
NodeClusterField_Http3 dbs.FieldName = "http3" // HTTP3设置
NodeClusterField_AutoSystemTuning dbs.FieldName = "autoSystemTuning" // 是否自动调整系统参数
NodeClusterField_NetworkSecurity dbs.FieldName = "networkSecurity" // 网络安全策略
)
// NodeCluster 节点集群
@@ -87,6 +89,8 @@ type NodeCluster struct {
HttpPages dbs.JSON `field:"httpPages"` // 自定义页面设置
Cc dbs.JSON `field:"cc"` // CC设置
Http3 dbs.JSON `field:"http3"` // HTTP3设置
AutoSystemTuning bool `field:"autoSystemTuning"` // 是否自动调整系统参数
NetworkSecurity dbs.JSON `field:"networkSecurity"` // 网络安全策略
}
type NodeClusterOperator struct {
@@ -130,6 +134,8 @@ type NodeClusterOperator struct {
HttpPages any // 自定义页面设置
Cc any // CC设置
Http3 any // HTTP3设置
AutoSystemTuning any // 是否自动调整系统参数
NetworkSecurity any // 网络安全策略
}
func NewNodeClusterOperator() *NodeClusterOperator {

View File

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

View File

@@ -18,7 +18,6 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ddosconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
@@ -27,6 +26,7 @@ import (
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"sort"
"strconv"
"strings"
"time"
@@ -1018,6 +1018,13 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, dataMap *shared
}
config.AllowedIPs = append(config.AllowedIPs, apiNodeIPs...)
// 当前的节点IP地址
nodeNodeIPs, err := SharedNodeIPAddressDAO.FindAllEnabledAddressStringsWithNode(tx, nodeId, nodeconfigs.NodeRoleNode)
if err != nil {
return nil, err
}
config.IPAddresses = nodeNodeIPs
// 所属集群
var primaryClusterId = int64(node.ClusterId)
var clusterIds = []int64{primaryClusterId}
@@ -1039,9 +1046,7 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, dataMap *shared
if err != nil {
return nil, err
}
for _, clusterServer := range clusterServers {
servers = append(servers, clusterServer)
}
servers = append(servers, clusterServers...)
}
for _, server := range servers {
@@ -1059,30 +1064,6 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, dataMap *shared
}
}
// 全局设置
// TODO 根据用户的不同读取不同的全局设置
var settingCacheKey = "SharedSysSettingDAO:" + systemconfigs.SettingCodeServerGlobalConfig
settingJSONCache, ok := cacheMap.Get(settingCacheKey)
var settingJSON = []byte{}
if ok {
settingJSON = settingJSONCache.([]byte)
} else {
settingJSON, err = SharedSysSettingDAO.ReadSetting(tx, systemconfigs.SettingCodeServerGlobalConfig)
if err != nil {
return nil, err
}
cacheMap.Put(settingCacheKey, settingJSON)
}
if len(settingJSON) > 0 {
globalConfig := &serverconfigs.GlobalConfig{}
err = json.Unmarshal(settingJSON, globalConfig)
if err != nil {
return nil, err
}
config.GlobalConfig = globalConfig
}
var clusterIndex = 0
config.WebPImagePolicies = map[int64]*nodeconfigs.WebPImagePolicy{}
config.UAMPolicies = map[int64]*nodeconfigs.UAMPolicy{}
@@ -1102,7 +1083,7 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, dataMap *shared
continue
}
// 节点IP地址
// 所有节点IP地址
nodeIPAddresses, err := SharedNodeIPAddressDAO.FindAllAccessibleIPAddressesWithClusterId(tx, nodeconfigs.NodeRoleNode, clusterId, cacheMap)
if err != nil {
return nil, err
@@ -1119,7 +1100,7 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, dataMap *shared
// 防火墙
var httpFirewallPolicyId = int64(nodeCluster.HttpFirewallPolicyId)
if httpFirewallPolicyId > 0 {
firewallPolicy, err := SharedHTTPFirewallPolicyDAO.ComposeFirewallPolicy(tx, httpFirewallPolicyId, cacheMap)
firewallPolicy, err := SharedHTTPFirewallPolicyDAO.ComposeFirewallPolicy(tx, httpFirewallPolicyId, true, cacheMap)
if err != nil {
return nil, err
}
@@ -1244,6 +1225,12 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, dataMap *shared
// 自动安装nftables
if clusterIndex == 0 {
config.AutoInstallNftables = nodeCluster.AutoInstallNftables
config.AutoSystemTuning = nodeCluster.AutoSystemTuning
}
// 安全设置
if clusterIndex == 0 {
config.NetworkSecurityPolicy = nodeCluster.DecodeNetworkSecurityPolicy()
}
clusterIndex++
@@ -2124,12 +2111,18 @@ func (this *NodeDAO) FindParentNodeConfigs(tx *dbs.Tx, nodeId int64, groupId int
var secretHash = fmt.Sprintf("%x", sha256.Sum256([]byte(node.UniqueId+"@"+node.Secret)))
for _, clusterId := range node.AllClusterIds() {
parentNodeConfigs, _ := result[clusterId]
var parentNodeConfigs = result[clusterId]
parentNodeConfigs = append(parentNodeConfigs, &nodeconfigs.ParentNodeConfig{
Id: int64(node.Id),
Addrs: addrStrings,
SecretHash: secretHash,
})
// 排序
sort.Slice(parentNodeConfigs, func(i, j int) bool {
return parentNodeConfigs[i].Id < parentNodeConfigs[j].Id
})
result[clusterId] = parentNodeConfigs
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -227,6 +227,8 @@ func (this *OriginDAO) UpdateOrigin(tx *dbs.Tx,
return err
}
op.Oss = ossConfigJSON
} else {
op.Oss = dbs.SQL("NULL")
}
op.Description = description
@@ -400,6 +402,7 @@ func (this *OriginDAO) ComposeOriginConfig(tx *dbs.Tx, originId int64, dataMap *
}
// addr
var isOSS = false
if IsNotNull(origin.Addr) {
var addr = &serverconfigs.NetworkAddressConfig{}
err = json.Unmarshal(origin.Addr, addr)
@@ -407,10 +410,11 @@ func (this *OriginDAO) ComposeOriginConfig(tx *dbs.Tx, originId int64, dataMap *
return nil, err
}
config.Addr = addr
isOSS = ossconfigs.IsOSSProtocol(string(addr.Protocol))
}
// oss
if IsNotNull(origin.Oss) {
if isOSS && IsNotNull(origin.Oss) {
var ossConfig = ossconfigs.NewOSSConfig()
err = json.Unmarshal(origin.Oss, ossConfig)
if err != nil {

View File

@@ -61,15 +61,28 @@ func (this *PlanDAO) DisablePlan(tx *dbs.Tx, id int64) error {
}
// FindEnabledPlan 查找启用中的条目
func (this *PlanDAO) FindEnabledPlan(tx *dbs.Tx, id int64) (*Plan, error) {
func (this *PlanDAO) FindEnabledPlan(tx *dbs.Tx, planId int64, cacheMap *utils.CacheMap) (*Plan, error) {
var cacheKey = this.Table + ":FindEnabledPlan:" + types.String(planId)
if cacheMap != nil {
cache, _ := cacheMap.Get(cacheKey)
if cache != nil {
return cache.(*Plan), nil
}
}
result, err := this.Query(tx).
Pk(id).
Pk(planId).
Attr("state", PlanStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*Plan), err
if cacheMap != nil {
cacheMap.Put(cacheKey, result)
}
return result.(*Plan), nil
}
// FindPlanName 根据主键查找名称

View File

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

View File

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

View File

@@ -127,6 +127,9 @@ func (this *RegionCountryDAO) CreateCountry(tx *dbs.Tx, name string, dataId stri
pinyinResult = append(pinyinResult, strings.Join(piece, " "))
}
pinyinJSON, err := json.Marshal([]string{strings.Join(pinyinResult, " ")})
if err != nil {
return 0, err
}
op.Pinyin = pinyinJSON
codes := []string{name}

View File

@@ -99,7 +99,7 @@ func (this *ReverseProxyDAO) ComposeReverseProxyConfig(tx *dbs.Tx, reverseProxyI
return nil, nil
}
var config = &serverconfigs.ReverseProxyConfig{}
var config = serverconfigs.NewReverseProxyConfig()
config.Id = int64(reverseProxy.Id)
config.IsOn = reverseProxy.IsOn
config.RequestHostType = types.Int8(reverseProxy.RequestHostType)
@@ -109,6 +109,8 @@ func (this *ReverseProxyDAO) ComposeReverseProxyConfig(tx *dbs.Tx, reverseProxyI
config.StripPrefix = reverseProxy.StripPrefix
config.AutoFlush = reverseProxy.AutoFlush == 1
config.FollowRedirects = reverseProxy.FollowRedirects == 1
config.Retry50X = reverseProxy.Retry50X
config.Retry40X = reverseProxy.Retry40X
var schedulingConfig = &serverconfigs.SchedulingConfig{}
if IsNotNull(reverseProxy.Scheduling) {
@@ -218,6 +220,8 @@ func (this *ReverseProxyDAO) CreateReverseProxy(tx *dbs.Tx, adminId int64, userI
op.AdminId = adminId
op.UserId = userId
op.RequestHostType = serverconfigs.RequestHostTypeProxyServer
op.Retry50X = false
op.Retry40X = false
defaultHeaders := []string{"X-Real-IP", "X-Forwarded-For", "X-Forwarded-By", "X-Forwarded-Host", "X-Forwarded-Proto"}
defaultHeadersJSON, err := json.Marshal(defaultHeaders)
@@ -425,7 +429,9 @@ func (this *ReverseProxyDAO) UpdateReverseProxy(tx *dbs.Tx,
maxConns int32,
maxIdleConns int32,
proxyProtocolJSON []byte,
followRedirects bool) error {
followRedirects bool,
retry50X bool,
retry40X bool) error {
if reverseProxyId <= 0 {
return errors.New("invalid reverseProxyId")
}
@@ -490,6 +496,9 @@ func (this *ReverseProxyDAO) UpdateReverseProxy(tx *dbs.Tx,
op.ProxyProtocol = proxyProtocolJSON
}
op.Retry50X = retry50X
op.Retry40X = retry40X
err = this.Save(tx, op)
if err != nil {
return err

View File

@@ -2,6 +2,35 @@ package models
import "github.com/iwind/TeaGo/dbs"
const (
ReverseProxyField_Id dbs.FieldName = "id" // ID
ReverseProxyField_AdminId dbs.FieldName = "adminId" // 管理员ID
ReverseProxyField_UserId dbs.FieldName = "userId" // 用户ID
ReverseProxyField_TemplateId dbs.FieldName = "templateId" // 模版ID
ReverseProxyField_IsOn dbs.FieldName = "isOn" // 是否启用
ReverseProxyField_Scheduling dbs.FieldName = "scheduling" // 调度算法
ReverseProxyField_PrimaryOrigins dbs.FieldName = "primaryOrigins" // 主要源站
ReverseProxyField_BackupOrigins dbs.FieldName = "backupOrigins" // 备用源站
ReverseProxyField_StripPrefix dbs.FieldName = "stripPrefix" // 去除URL前缀
ReverseProxyField_RequestHostType dbs.FieldName = "requestHostType" // 请求Host类型
ReverseProxyField_RequestHost dbs.FieldName = "requestHost" // 请求Host
ReverseProxyField_RequestHostExcludingPort dbs.FieldName = "requestHostExcludingPort" // 移除请求Host中的域名
ReverseProxyField_RequestURI dbs.FieldName = "requestURI" // 请求URI
ReverseProxyField_AutoFlush dbs.FieldName = "autoFlush" // 是否自动刷新缓冲区
ReverseProxyField_AddHeaders dbs.FieldName = "addHeaders" // 自动添加的Header列表
ReverseProxyField_State dbs.FieldName = "state" // 状态
ReverseProxyField_CreatedAt dbs.FieldName = "createdAt" // 创建时间
ReverseProxyField_ConnTimeout dbs.FieldName = "connTimeout" // 连接超时时间
ReverseProxyField_ReadTimeout dbs.FieldName = "readTimeout" // 读取超时时间
ReverseProxyField_IdleTimeout dbs.FieldName = "idleTimeout" // 空闲超时时间
ReverseProxyField_MaxConns dbs.FieldName = "maxConns" // 最大并发连接数
ReverseProxyField_MaxIdleConns dbs.FieldName = "maxIdleConns" // 最大空闲连接数
ReverseProxyField_ProxyProtocol dbs.FieldName = "proxyProtocol" // Proxy Protocol配置
ReverseProxyField_FollowRedirects dbs.FieldName = "followRedirects" // 回源跟随
ReverseProxyField_Retry50X dbs.FieldName = "retry50X" // 启用50X重试
ReverseProxyField_Retry40X dbs.FieldName = "retry40X" // 启用40X重试
)
// ReverseProxy 反向代理配置
type ReverseProxy struct {
Id uint32 `field:"id"` // ID
@@ -28,33 +57,37 @@ type ReverseProxy struct {
MaxIdleConns uint32 `field:"maxIdleConns"` // 最大空闲连接数
ProxyProtocol dbs.JSON `field:"proxyProtocol"` // Proxy Protocol配置
FollowRedirects uint8 `field:"followRedirects"` // 回源跟随
Retry50X bool `field:"retry50X"` // 启用50X重试
Retry40X bool `field:"retry40X"` // 启用40X重试
}
type ReverseProxyOperator struct {
Id interface{} // ID
AdminId interface{} // 管理员ID
UserId interface{} // 用户ID
TemplateId interface{} // 模版ID
IsOn interface{} // 是否启用
Scheduling interface{} // 调度算法
PrimaryOrigins interface{} // 主要源站
BackupOrigins interface{} // 备用源站
StripPrefix interface{} // 去除URL前缀
RequestHostType interface{} // 请求Host类型
RequestHost interface{} // 请求Host
RequestHostExcludingPort interface{} // 移除请求Host中的域名
RequestURI interface{} // 请求URI
AutoFlush interface{} // 是否自动刷新缓冲区
AddHeaders interface{} // 自动添加的Header列表
State interface{} // 状态
CreatedAt interface{} // 创建时间
ConnTimeout interface{} // 连接超时时间
ReadTimeout interface{} // 读取超时时间
IdleTimeout interface{} // 空闲超时时间
MaxConns interface{} // 最大并发连接数
MaxIdleConns interface{} // 最大空闲连接数
ProxyProtocol interface{} // Proxy Protocol配置
FollowRedirects interface{} // 回源跟随
Id any // ID
AdminId any // 管理员ID
UserId any // 用户ID
TemplateId any // 模版ID
IsOn any // 是否启用
Scheduling any // 调度算法
PrimaryOrigins any // 主要源站
BackupOrigins any // 备用源站
StripPrefix any // 去除URL前缀
RequestHostType any // 请求Host类型
RequestHost any // 请求Host
RequestHostExcludingPort any // 移除请求Host中的域名
RequestURI any // 请求URI
AutoFlush any // 是否自动刷新缓冲区
AddHeaders any // 自动添加的Header列表
State any // 状态
CreatedAt any // 创建时间
ConnTimeout any // 连接超时时间
ReadTimeout any // 读取超时时间
IdleTimeout any // 空闲超时时间
MaxConns any // 最大并发连接数
MaxIdleConns any // 最大空闲连接数
ProxyProtocol any // Proxy Protocol配置
FollowRedirects any // 回源跟随
Retry50X any // 启用50X重试
Retry40X any // 启用40X重试
}
func NewReverseProxyOperator() *ReverseProxyOperator {

View File

@@ -25,7 +25,7 @@ import (
type ServerBandwidthStatDAO dbs.DAO
const (
ServerBandwidthStatTablePartials = 20 // 分表数量
ServerBandwidthStatTablePartitions = 20 // 分表数量
)
func init() {
@@ -63,15 +63,15 @@ func init() {
}
// UpdateServerBandwidth 写入数据
// 暂时不使用region区分
func (this *ServerBandwidthStatDAO) UpdateServerBandwidth(tx *dbs.Tx, userId int64, serverId int64, regionId int64, day string, timeAt string, bytes int64, totalBytes int64, cachedBytes int64, attackBytes int64, countRequests int64, countCachedRequests int64, countAttackRequests int64) error {
// 现在不需要把 userPlanId 加入到数据表unique key中因为只会影响5分钟统计影响非常有限
func (this *ServerBandwidthStatDAO) UpdateServerBandwidth(tx *dbs.Tx, userId int64, serverId int64, regionId int64, userPlanId int64, day string, timeAt string, bandwidthBytes int64, totalBytes int64, cachedBytes int64, attackBytes int64, countRequests int64, countCachedRequests int64, countAttackRequests int64) error {
if serverId <= 0 {
return errors.New("invalid server id '" + types.String(serverId) + "'")
}
return this.Query(tx).
Table(this.partialTable(serverId)).
Param("bytes", bytes).
Param("bytes", bandwidthBytes).
Param("totalBytes", totalBytes).
Param("cachedBytes", cachedBytes).
Param("attackBytes", attackBytes).
@@ -84,7 +84,7 @@ func (this *ServerBandwidthStatDAO) UpdateServerBandwidth(tx *dbs.Tx, userId int
"regionId": regionId,
"day": day,
"timeAt": timeAt,
"bytes": bytes,
"bytes": bandwidthBytes,
"totalBytes": totalBytes,
"avgBytes": totalBytes / 300,
"cachedBytes": cachedBytes,
@@ -92,6 +92,7 @@ func (this *ServerBandwidthStatDAO) UpdateServerBandwidth(tx *dbs.Tx, userId int
"countRequests": countRequests,
"countCachedRequests": countCachedRequests,
"countAttackRequests": countAttackRequests,
"userPlanId": userPlanId,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"avgBytes": dbs.SQL("(totalBytes+:totalBytes)/300"), // 因为生成SQL语句时会自动将avgBytes排在totalBytes之前所以这里不用担心先后顺序的问题
@@ -379,14 +380,18 @@ func (this *ServerBandwidthStatDAO) FindAllServerStatsWithMonth(tx *dbs.Tx, serv
}
// FindMonthlyPercentile 获取某月内百分位
func (this *ServerBandwidthStatDAO) FindMonthlyPercentile(tx *dbs.Tx, serverId int64, month string, percentile int, useAvg bool) (result int64, err error) {
func (this *ServerBandwidthStatDAO) FindMonthlyPercentile(tx *dbs.Tx, serverId int64, month string, percentile int, useAvg bool, noPlan bool, minSamples int) (result int64, err error) {
if percentile <= 0 {
percentile = 95
}
// 如果是100%以上,则快速返回
if percentile >= 100 {
result, err = this.Query(tx).
var query = this.Query(tx)
if noPlan {
query.Attr("userPlanId", 0)
}
result, err = query.
Table(this.partialTable(serverId)).
Attr("serverId", serverId).
Result(this.bytesField(useAvg)).
@@ -398,7 +403,11 @@ func (this *ServerBandwidthStatDAO) FindMonthlyPercentile(tx *dbs.Tx, serverId i
}
// 总数量
total, err := this.Query(tx).
var totalQuery = this.Query(tx)
if noPlan {
totalQuery.Attr("userPlanId", 0)
}
total, err := totalQuery.
Table(this.partialTable(serverId)).
Attr("serverId", serverId).
Between("day", month+"01", month+"31").
@@ -406,7 +415,7 @@ func (this *ServerBandwidthStatDAO) FindMonthlyPercentile(tx *dbs.Tx, serverId i
if err != nil {
return 0, err
}
if total == 0 {
if total == 0 || total < int64(minSamples) {
return 0, nil
}
@@ -417,7 +426,11 @@ func (this *ServerBandwidthStatDAO) FindMonthlyPercentile(tx *dbs.Tx, serverId i
}
// 查询 nth 位置
result, err = this.Query(tx).
var query = this.Query(tx)
if noPlan {
query.Attr("userPlanId", 0)
}
result, err = query.
Table(this.partialTable(serverId)).
Attr("serverId", serverId).
Result(this.bytesField(useAvg)).
@@ -745,6 +758,74 @@ func (this *ServerBandwidthStatDAO) SumDailyStat(tx *dbs.Tx, serverId int64, reg
return
}
// SumMonthlyBytes 统计某个网站单月总流量
func (this *ServerBandwidthStatDAO) SumMonthlyBytes(tx *dbs.Tx, serverId int64, month string, noPlan bool) (int64, error) {
if !regexputils.YYYYMM.MatchString(month) {
return 0, errors.New("invalid month '" + month + "'")
}
// 兼容以往版本
hasFullData, err := this.HasFullData(tx, serverId, month)
if err != nil {
return 0, err
}
if !hasFullData {
return SharedServerDailyStatDAO.SumMonthlyBytes(tx, serverId, month)
}
var query = this.Query(tx)
if noPlan {
query.Attr("userPlanId", 0)
}
return query.
Table(this.partialTable(serverId)).
Between("day", month+"01", month+"31").
Attr("serverId", serverId).
SumInt64("totalBytes", 0)
}
// SumServerMonthlyWithRegion 根据服务计算某月合计
// month 格式为YYYYMM
func (this *ServerBandwidthStatDAO) SumServerMonthlyWithRegion(tx *dbs.Tx, serverId int64, regionId int64, month string, noPlan bool) (int64, error) {
var query = this.Query(tx)
query.Table(this.partialTable(serverId))
if regionId > 0 {
query.Attr("regionId", regionId)
}
if noPlan {
query.Attr("userPlanId", 0)
}
return query.Between("day", month+"01", month+"31").
Attr("serverId", serverId).
SumInt64("totalBytes", 0)
}
// FindDistinctServerIdsWithoutPlanAtPartition 查找没有绑定套餐的有流量网站
func (this *ServerBandwidthStatDAO) FindDistinctServerIdsWithoutPlanAtPartition(tx *dbs.Tx, partitionIndex int, month string) (serverIds []int64, err error) {
ones, err := this.Query(tx).
Table(this.partialTable(int64(partitionIndex))).
Between("day", month+"01", month+"31").
Attr("userPlanId", 0). // 没有绑定套餐
Result("DISTINCT serverId").
FindAll()
if err != nil {
return nil, err
}
for _, one := range ones {
var serverId = int64(one.(*ServerBandwidthStat).ServerId)
if serverId <= 0 {
continue
}
serverIds = append(serverIds, serverId)
}
return
}
// CountPartitions 查看分区数量
func (this *ServerBandwidthStatDAO) CountPartitions() int {
return ServerBandwidthStatTablePartitions
}
// CleanDays 清理过期数据
func (this *ServerBandwidthStatDAO) CleanDays(tx *dbs.Tx, days int) error {
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days)) // 保留大约3个月的数据
@@ -777,9 +858,9 @@ func (this *ServerBandwidthStatDAO) CleanDefaultDays(tx *dbs.Tx, defaultDays int
func (this *ServerBandwidthStatDAO) runBatch(f func(table string, locker *sync.Mutex) error) error {
var locker = &sync.Mutex{}
var wg = sync.WaitGroup{}
wg.Add(ServerBandwidthStatTablePartials)
wg.Add(ServerBandwidthStatTablePartitions)
var resultErr error
for i := 0; i < ServerBandwidthStatTablePartials; i++ {
for i := 0; i < ServerBandwidthStatTablePartitions; i++ {
var table = this.partialTable(int64(i))
go func(table string) {
defer wg.Done()
@@ -796,7 +877,7 @@ func (this *ServerBandwidthStatDAO) runBatch(f func(table string, locker *sync.M
// 获取分区表
func (this *ServerBandwidthStatDAO) partialTable(serverId int64) string {
return this.Table + "_" + types.String(serverId%int64(ServerBandwidthStatTablePartials))
return this.Table + "_" + types.String(serverId%int64(ServerBandwidthStatTablePartitions))
}
// 获取字节字段
@@ -844,6 +925,11 @@ func (this *ServerBandwidthStatDAO) fixServerStats(stats []*ServerBandwidthStat,
// HasFullData 检查一个月是否完整数据
// 是为了兼容以前数据,以前的表中没有缓存流量、请求数等字段
func (this *ServerBandwidthStatDAO) HasFullData(tx *dbs.Tx, serverId int64, month string) (bool, error) {
// 最迟在2024年完成过渡
if time.Now().Year() >= 2024 {
return true, nil
}
var monthKey = month + "@" + types.String(serverId)
if !regexputils.YYYYMM.MatchString(month) {

View File

@@ -16,7 +16,7 @@ import (
func TestServerBandwidthStatDAO_UpdateServerBandwidth(t *testing.T) {
var dao = models.NewServerBandwidthStatDAO()
var tx *dbs.Tx
err := dao.UpdateServerBandwidth(tx, 1, 1, 0, timeutil.Format("Ymd"), timeutil.FormatTime("Hi", time.Now().Unix()/300*300), 1024, 300, 0, 0, 0, 0, 0)
err := dao.UpdateServerBandwidth(tx, 1, 1, 0, 0, timeutil.Format("Ymd"), timeutil.FormatTime("Hi", time.Now().Unix()/300*300), 1024, 300, 0, 0, 0, 0, 0)
if err != nil {
t.Fatal(err)
}
@@ -33,7 +33,7 @@ func TestSeverBandwidthStatDAO_InsertManyStats(t *testing.T) {
}
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -rands.Int(0, 200)))
var minute = fmt.Sprintf("%02d%02d", rands.Int(0, 23), rands.Int(0, 59))
err := dao.UpdateServerBandwidth(tx, 1, int64(rands.Int(1, 10000)), 0, day, minute, 1024, 300, 0, 0, 0, 0, 0)
err := dao.UpdateServerBandwidth(tx, 1, int64(rands.Int(1, 10000)), 0, 0, day, minute, 1024, 300, 0, 0, 0, 0, 0)
if err != nil {
t.Fatal(err)
}
@@ -44,8 +44,10 @@ func TestSeverBandwidthStatDAO_InsertManyStats(t *testing.T) {
func TestServerBandwidthStatDAO_FindMonthlyPercentile(t *testing.T) {
var dao = models.NewServerBandwidthStatDAO()
var tx *dbs.Tx
t.Log(dao.FindMonthlyPercentile(tx, 23, timeutil.Format("Ym"), 95, false))
t.Log(dao.FindMonthlyPercentile(tx, 23, timeutil.Format("Ym"), 95, true))
t.Log(dao.FindMonthlyPercentile(tx, 23, timeutil.Format("Ym"), 95, false, false, 0))
t.Log(dao.FindMonthlyPercentile(tx, 23, timeutil.Format("Ym"), 95, true, false, 0))
t.Log(dao.FindMonthlyPercentile(tx, 23, timeutil.Format("Ym"), 95, true, false, 100))
t.Log(dao.FindMonthlyPercentile(tx, 23, timeutil.Format("Ym"), 95, true, true, 0))
}
func TestServerBandwidthStatDAO_FindAllServerStatsWithMonth(t *testing.T) {
@@ -114,3 +116,32 @@ func TestServerBandwidthStatDAO_FindBandwidthStatsBetweenDays(t *testing.T) {
t.Log(stat.Day, stat.TimeAt, "bytes:", stat.Bytes, "bits:", stat.Bits)
}
}
func TestServerBandwidthStatDAO_SumServerMonthlyWithRegion(t *testing.T) {
var dao = models.NewServerBandwidthStatDAO()
var tx *dbs.Tx
{
totalBytes, err := dao.SumServerMonthlyWithRegion(tx, 23, 0, timeutil.Format("Ym"), false)
if err != nil {
t.Fatal(err)
}
t.Log("with plan:", totalBytes)
}
{
totalBytes, err := dao.SumServerMonthlyWithRegion(tx, 23, 0, timeutil.Format("Ym"), true)
if err != nil {
t.Fatal(err)
}
t.Log("without plan:", totalBytes)
}
}
func TestServerBandwidthStatDAO_SumMonthlyBytes(t *testing.T) {
var dao = models.NewServerBandwidthStatDAO()
var tx *dbs.Tx
totalBytes, err := dao.SumMonthlyBytes(tx, 23, timeutil.Format("Ym"), false)
if err != nil {
t.Fatal(err)
}
t.Log("total bytes:", totalBytes)
}

View File

@@ -1,11 +1,32 @@
package models
import "github.com/iwind/TeaGo/dbs"
const (
ServerBandwidthStatField_Id dbs.FieldName = "id" // ID
ServerBandwidthStatField_UserId dbs.FieldName = "userId" // 用户ID
ServerBandwidthStatField_ServerId dbs.FieldName = "serverId" // 服务ID
ServerBandwidthStatField_RegionId dbs.FieldName = "regionId" // 区域ID
ServerBandwidthStatField_UserPlanId dbs.FieldName = "userPlanId" // 用户套餐ID
ServerBandwidthStatField_Day dbs.FieldName = "day" // 日期YYYYMMDD
ServerBandwidthStatField_TimeAt dbs.FieldName = "timeAt" // 时间点HHMM
ServerBandwidthStatField_Bytes dbs.FieldName = "bytes" // 带宽字节
ServerBandwidthStatField_AvgBytes dbs.FieldName = "avgBytes" // 平均流量
ServerBandwidthStatField_CachedBytes dbs.FieldName = "cachedBytes" // 缓存的流量
ServerBandwidthStatField_AttackBytes dbs.FieldName = "attackBytes" // 攻击流量
ServerBandwidthStatField_CountRequests dbs.FieldName = "countRequests" // 请求数
ServerBandwidthStatField_CountCachedRequests dbs.FieldName = "countCachedRequests" // 缓存的请求数
ServerBandwidthStatField_CountAttackRequests dbs.FieldName = "countAttackRequests" // 攻击请求数
ServerBandwidthStatField_TotalBytes dbs.FieldName = "totalBytes" // 总流量
)
// ServerBandwidthStat 服务峰值带宽统计
type ServerBandwidthStat struct {
Id uint64 `field:"id"` // ID
UserId uint64 `field:"userId"` // 用户ID
ServerId uint64 `field:"serverId"` // 服务ID
RegionId uint32 `field:"regionId"` // 区域ID
UserPlanId uint64 `field:"userPlanId"` // 用户套餐ID
Day string `field:"day"` // 日期YYYYMMDD
TimeAt string `field:"timeAt"` // 时间点HHMM
Bytes uint64 `field:"bytes"` // 带宽字节
@@ -23,6 +44,7 @@ type ServerBandwidthStatOperator struct {
UserId any // 用户ID
ServerId any // 服务ID
RegionId any // 区域ID
UserPlanId any // 用户套餐ID
Day any // 日期YYYYMMDD
TimeAt any // 时间点HHMM
Bytes any // 带宽字节

View File

@@ -119,7 +119,7 @@ func (this *ServerDailyStatDAO) SaveStats(tx *dbs.Tx, stats []*pb.ServerDailySta
// 更新流量限制状态
if stat.CheckTrafficLimiting {
trafficLimitConfig, err := SharedServerDAO.CalculateServerTrafficLimitConfig(tx, stat.ServerId, cacheMap)
trafficLimitConfig, err := SharedServerDAO.FindServerTrafficLimitConfig(tx, stat.ServerId, cacheMap)
if err != nil {
return err
}
@@ -129,7 +129,7 @@ func (this *ServerDailyStatDAO) SaveStats(tx *dbs.Tx, stats []*pb.ServerDailySta
return err
}
err = SharedServerDAO.UpdateServerTrafficLimitStatus(tx, trafficLimitConfig, stat.ServerId, false)
err = SharedServerDAO.RenewServerTrafficLimitStatus(tx, trafficLimitConfig, stat.ServerId, false)
if err != nil {
return err
}
@@ -140,6 +140,7 @@ func (this *ServerDailyStatDAO) SaveStats(tx *dbs.Tx, stats []*pb.ServerDailySta
return nil
}
// SumCurrentDailyStat 查找当前时刻的数据统计
func (this *ServerDailyStatDAO) SumCurrentDailyStat(tx *dbs.Tx, serverId int64) (*ServerDailyStat, error) {
var day = timeutil.Format("Ymd")
@@ -164,7 +165,7 @@ func (this *ServerDailyStatDAO) SumServerMonthlyWithRegion(tx *dbs.Tx, serverId
if regionId > 0 {
query.Attr("regionId", regionId)
}
return query.Between("day", month+"01", month+"32").
return query.Between("day", month+"01", month+"31").
Attr("serverId", serverId).
SumInt64("bytes", 0)
}
@@ -178,7 +179,7 @@ func (this *ServerDailyStatDAO) SumUserMonthlyWithoutPlan(tx *dbs.Tx, userId int
}
return query.
Attr("planId", 0).
Between("day", month+"01", month+"32").
Between("day", month+"01", month+"31").
Attr("userId", userId).
SumInt64("bytes", 0)
}
@@ -190,7 +191,7 @@ func (this *ServerDailyStatDAO) SumUserMonthlyPeek(tx *dbs.Tx, userId int64, reg
if regionId > 0 {
query.Attr("regionId", regionId)
}
max, err := query.Between("day", month+"01", month+"32").
max, err := query.Between("day", month+"01", month+"31").
Attr("userId", userId).
Max("bytes", 0)
if err != nil {
@@ -644,7 +645,7 @@ func (this *ServerDailyStatDAO) FindStatsBetweenDays(tx *dbs.Tx, userId int64, s
// month YYYYMM
func (this *ServerDailyStatDAO) FindMonthlyStatsWithPlan(tx *dbs.Tx, month string) (result []*ServerDailyStat, err error) {
_, err = this.Query(tx).
Between("day", month+"01", month+"32").
Between("day", month+"01", month+"31").
Gt("planId", 0).
Slice(&result).
FindAll()

View File

@@ -3,11 +3,13 @@ package models
import (
"encoding/json"
"errors"
"fmt"
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/dns"
dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAPI/internal/utils/regexputils"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
@@ -782,7 +784,7 @@ func (this *ServerDAO) CountAllEnabledServers(tx *dbs.Tx) (int64, error) {
// 参数:
//
// groupId 分组ID如果为-1则搜索没有分组的服务
func (this *ServerDAO) CountAllEnabledServersMatch(tx *dbs.Tx, groupId int64, keyword string, userId int64, clusterId int64, auditingFlag configutils.BoolState, protocolFamilies []string) (int64, error) {
func (this *ServerDAO) CountAllEnabledServersMatch(tx *dbs.Tx, groupId int64, keyword string, userId int64, clusterId int64, auditingFlag configutils.BoolState, protocolFamilies []string, userPlanId int64) (int64, error) {
query := this.Query(tx).
State(ServerStateEnabled)
if groupId > 0 {
@@ -829,6 +831,10 @@ func (this *ServerDAO) CountAllEnabledServersMatch(tx *dbs.Tx, groupId int64, ke
query.Where("(" + strings.Join(protocolConds, " OR ") + ")")
}
if userPlanId > 0 {
query.Attr("userPlanId", userPlanId)
}
return query.Count()
}
@@ -1316,12 +1322,13 @@ func (this *ServerDAO) ComposeServerConfig(tx *dbs.Tx, server *Server, ignoreCer
}
// 套餐是否依然有效
plan, err := SharedPlanDAO.FindEnabledPlan(tx, int64(userPlan.PlanId))
plan, err := SharedPlanDAO.FindEnabledPlan(tx, int64(userPlan.PlanId), cacheMap)
if err != nil {
return nil, err
}
if plan != nil {
config.UserPlan = &serverconfigs.UserPlanConfig{
Id: int64(userPlan.Id),
DayTo: userPlan.DayTo,
Plan: &serverconfigs.PlanConfig{
Id: int64(plan.Id),
@@ -1341,16 +1348,14 @@ func (this *ServerDAO) ComposeServerConfig(tx *dbs.Tx, server *Server, ignoreCer
}
}
if config.TrafficLimit != nil && config.TrafficLimit.IsOn && !config.TrafficLimit.IsEmpty() {
if len(server.TrafficLimitStatus) > 0 {
var status = &serverconfigs.TrafficLimitStatus{}
err := json.Unmarshal(server.TrafficLimitStatus, status)
if err != nil {
return nil, err
}
if status.IsValid() {
config.TrafficLimitStatus = status
}
if len(server.TrafficLimitStatus) > 0 {
var status = &serverconfigs.TrafficLimitStatus{}
err := json.Unmarshal(server.TrafficLimitStatus, status)
if err != nil {
return nil, err
}
if status.IsValid() {
config.TrafficLimitStatus = status
}
}
@@ -1794,6 +1799,7 @@ func (this *ServerDAO) FindServerUserId(tx *dbs.Tx, serverId int64) (userId int6
}
// FindServerUserPlanId 查找服务的套餐ID
// TODO 需要缓存
func (this *ServerDAO) FindServerUserPlanId(tx *dbs.Tx, serverId int64) (userPlanId int64, err error) {
return this.Query(tx).
Pk(serverId).
@@ -2039,11 +2045,12 @@ func (this *ServerDAO) GenDNSName(tx *dbs.Tx) (string, error) {
// FindLatestServers 查询最近访问的服务
func (this *ServerDAO) FindLatestServers(tx *dbs.Tx, size int64) (result []*Server, err error) {
itemTable := SharedLatestItemDAO.Table
itemType := LatestItemTypeServer
var itemTable = SharedLatestItemDAO.Table
var itemType = LatestItemTypeServer
_, err = this.Query(tx).
Result(this.Table+".id", this.Table+".name").
Join(SharedLatestItemDAO, dbs.QueryJoinRight, this.Table+".id="+itemTable+".itemId AND "+itemTable+".itemType='"+itemType+"'").
Where(itemTable + ".updatedAt<=UNIX_TIMESTAMP()"). // VERY IMPORTANT
Asc("CEIL((UNIX_TIMESTAMP() - " + itemTable + ".updatedAt) / (7 * 86400))"). // 优先一个星期以内的
Desc(itemTable + ".count").
State(NodeClusterStateEnabled).
@@ -2306,94 +2313,17 @@ func (this *ServerDAO) FindServerTrafficLimitConfig(tx *dbs.Tx, serverId int64,
return nil, err
}
var limit = &serverconfigs.TrafficLimitConfig{}
if serverOne == nil {
return limit, nil
}
var trafficLimit = serverOne.(*Server).TrafficLimit
if len(trafficLimit) > 0 {
err = json.Unmarshal([]byte(trafficLimit), limit)
if err != nil {
return nil, err
}
}
if cacheMap != nil {
cacheMap.Put(cacheKey, limit)
}
return limit, nil
}
// CalculateServerTrafficLimitConfig 计算服务的流量限制
// TODO 优化性能
func (this *ServerDAO) CalculateServerTrafficLimitConfig(tx *dbs.Tx, serverId int64, cacheMap *utils.CacheMap) (*serverconfigs.TrafficLimitConfig, error) {
if cacheMap == nil {
cacheMap = utils.NewCacheMap()
}
var cacheKey = this.Table + ":FindServerTrafficLimitConfig:" + types.String(serverId)
result, ok := cacheMap.Get(cacheKey)
if ok {
return result.(*serverconfigs.TrafficLimitConfig), nil
}
serverOne, err := this.Query(tx).
Pk(serverId).
Result("trafficLimit", "userPlanId").
Find()
if err != nil {
return nil, err
}
var limitConfig = &serverconfigs.TrafficLimitConfig{}
if serverOne == nil {
return limitConfig, nil
}
var trafficLimit = serverOne.(*Server).TrafficLimit
var userPlanId = int64(serverOne.(*Server).UserPlanId)
var trafficLimitJSON = serverOne.(*Server).TrafficLimit
if len(trafficLimit) == 0 {
if userPlanId > 0 {
userPlan, err := SharedUserPlanDAO.FindEnabledUserPlan(tx, userPlanId, cacheMap)
if err != nil {
return nil, err
}
if userPlan != nil {
planLimit, err := SharedPlanDAO.FindEnabledPlanTrafficLimit(tx, int64(userPlan.PlanId), cacheMap)
if err != nil {
return nil, err
}
if planLimit != nil {
return planLimit, nil
}
}
}
return limitConfig, nil
}
err = json.Unmarshal(trafficLimit, limitConfig)
if err != nil {
return nil, err
}
if !limitConfig.IsOn {
if userPlanId > 0 {
userPlan, err := SharedUserPlanDAO.FindEnabledUserPlan(tx, userPlanId, cacheMap)
if err != nil {
return nil, err
}
if userPlan != nil {
planLimit, err := SharedPlanDAO.FindEnabledPlanTrafficLimit(tx, int64(userPlan.PlanId), cacheMap)
if err != nil {
return nil, err
}
if planLimit != nil {
return planLimit, nil
}
}
if len(trafficLimitJSON) > 0 {
err = json.Unmarshal(trafficLimitJSON, limitConfig)
if err != nil {
return nil, err
}
}
@@ -2423,11 +2353,11 @@ func (this *ServerDAO) UpdateServerTrafficLimitConfig(tx *dbs.Tx, serverId int64
}
// 更新状态
return this.UpdateServerTrafficLimitStatus(tx, trafficLimitConfig, serverId, true)
return this.RenewServerTrafficLimitStatus(tx, trafficLimitConfig, serverId, true)
}
// UpdateServerTrafficLimitStatus 修改服务的流量限制状态
func (this *ServerDAO) UpdateServerTrafficLimitStatus(tx *dbs.Tx, trafficLimitConfig *serverconfigs.TrafficLimitConfig, serverId int64, isUpdatingConfig bool) error {
// RenewServerTrafficLimitStatus 根据限流配置更新网站的流量限制状态
func (this *ServerDAO) RenewServerTrafficLimitStatus(tx *dbs.Tx, trafficLimitConfig *serverconfigs.TrafficLimitConfig, serverId int64, isUpdatingConfig bool) error {
if !trafficLimitConfig.IsOn {
if isUpdatingConfig {
return this.NotifyUpdate(tx, serverId)
@@ -2464,9 +2394,11 @@ func (this *ServerDAO) UpdateServerTrafficLimitStatus(tx *dbs.Tx, trafficLimitCo
var untilDay = ""
// daily
var dateType = ""
if trafficLimitConfig.DailyBytes() > 0 {
if server.TrafficDay == timeutil.Format("Ymd") && server.TotalDailyTraffic >= float64(trafficLimitConfig.DailyBytes())/(1<<30) {
untilDay = timeutil.Format("Ymd")
dateType = "day"
}
}
@@ -2474,6 +2406,7 @@ func (this *ServerDAO) UpdateServerTrafficLimitStatus(tx *dbs.Tx, trafficLimitCo
if server.TrafficMonth == timeutil.Format("Ym") && trafficLimitConfig.MonthlyBytes() > 0 {
if server.TotalMonthlyTraffic >= float64(trafficLimitConfig.MonthlyBytes())/(1<<30) {
untilDay = timeutil.Format("Ym32")
dateType = "month"
}
}
@@ -2481,12 +2414,17 @@ func (this *ServerDAO) UpdateServerTrafficLimitStatus(tx *dbs.Tx, trafficLimitCo
if trafficLimitConfig.TotalBytes() > 0 {
if server.TotalTraffic >= float64(trafficLimitConfig.TotalBytes())/(1<<30) {
untilDay = "30000101"
dateType = "total"
}
}
var isChanged = oldStatus.UntilDay != untilDay
if isChanged {
statusJSON, err := json.Marshal(&serverconfigs.TrafficLimitStatus{UntilDay: untilDay})
statusJSON, err := json.Marshal(&serverconfigs.TrafficLimitStatus{
UntilDay: untilDay,
DateType: dateType,
TargetType: serverconfigs.TrafficLimitTargetTraffic,
})
if err != nil {
return err
}
@@ -2507,6 +2445,91 @@ func (this *ServerDAO) UpdateServerTrafficLimitStatus(tx *dbs.Tx, trafficLimitCo
return nil
}
// UpdateServerTrafficLimitStatus 修改网站的流量限制状态
func (this *ServerDAO) UpdateServerTrafficLimitStatus(tx *dbs.Tx, serverId int64, day string, planId int64, dateType string, targetType string) error {
if !regexputils.YYYYMMDD.MatchString(day) {
return errors.New("invalid 'day' format")
}
if serverId <= 0 {
return nil
}
// lookup old status
statusJSON, err := this.Query(tx).
Pk(serverId).
Result(ServerField_TrafficLimitStatus).
FindJSONCol()
if err != nil {
return err
}
if IsNotNull(statusJSON) {
var oldStatus = &serverconfigs.TrafficLimitStatus{}
err = json.Unmarshal(statusJSON, oldStatus)
if err != nil {
return err
}
if len(oldStatus.UntilDay) > 0 && oldStatus.UntilDay >= day /** 如果已经限制,且比当前日期长,则无需重复 **/ {
// no need to change
return nil
}
}
var status = &serverconfigs.TrafficLimitStatus{
UntilDay: day,
PlanId: planId,
DateType: dateType,
TargetType: targetType,
}
statusJSON, err = json.Marshal(status)
if err != nil {
return err
}
err = this.Query(tx).
Pk(serverId).
Set(ServerField_TrafficLimitStatus, statusJSON).
UpdateQuickly()
if err != nil {
return err
}
return this.NotifyUpdate(tx, serverId)
}
// UpdateServersTrafficLimitStatusWithUserPlanId 修改某个套餐下的网站的流量限制状态
func (this *ServerDAO) UpdateServersTrafficLimitStatusWithUserPlanId(tx *dbs.Tx, userPlanId int64, day string, planId int64, dateType string, targetType serverconfigs.TrafficLimitTarget) error {
if userPlanId <= 0 {
return nil
}
servers, err := this.Query(tx).
State(ServerStateEnabled).
Attr("userPlanId", userPlanId).
ResultPk().
FindAll()
if err != nil {
return err
}
for _, server := range servers {
var serverId = int64(server.(*Server).Id)
err = this.UpdateServerTrafficLimitStatus(tx, serverId, day, planId, dateType, targetType)
if err != nil {
return err
}
}
return nil
}
// ResetServersTrafficLimitStatusWithPlanId 重置网站限流状态
func (this *ServerDAO) ResetServersTrafficLimitStatusWithPlanId(tx *dbs.Tx, planId int64) error {
return this.Query(tx).
Where("JSON_EXTRACT(trafficLimitStatus, '$.planId')=:planId").
Param("planId", planId).
Set("trafficLimitStatus", dbs.SQL("NULL")).
UpdateQuickly()
}
// IncreaseServerTotalTraffic 增加服务的总流量
func (this *ServerDAO) IncreaseServerTotalTraffic(tx *dbs.Tx, serverId int64, bytes int64) error {
if serverId <= 0 {
@@ -2548,17 +2571,16 @@ func (this *ServerDAO) FindEnabledServerIdWithUserPlanId(tx *dbs.Tx, userPlanId
FindInt64Col(0)
}
// FindEnabledServerWithUserPlanId 查找使用某个套餐的服务
func (this *ServerDAO) FindEnabledServerWithUserPlanId(tx *dbs.Tx, userPlanId int64) (*Server, error) {
one, err := this.Query(tx).
// FindEnabledServersWithUserPlanId 查找使用某个套餐的网站
func (this *ServerDAO) FindEnabledServersWithUserPlanId(tx *dbs.Tx, userPlanId int64) (result []*Server, err error) {
_, err = this.Query(tx).
State(ServerStateEnabled).
Attr("userPlanId", userPlanId).
Result("id", "name", "serverNames", "type").
Find()
if err != nil || one == nil {
return nil, err
}
return one.(*Server), nil
AscPk().
Slice(&result).
FindAll()
return
}
// UpdateServersClusterIdWithPlanId 修改套餐所在集群
@@ -2643,7 +2665,7 @@ func (this *ServerDAO) UpdateServerUserPlanId(tx *dbs.Tx, serverId int64, userPl
return errors.New("can not find user plan with id '" + types.String(userPlanId) + "'")
}
plan, err := SharedPlanDAO.FindEnabledPlan(tx, int64(userPlan.PlanId))
plan, err := SharedPlanDAO.FindEnabledPlan(tx, int64(userPlan.PlanId), nil)
if err != nil {
return err
}
@@ -2881,6 +2903,89 @@ func (this *ServerDAO) FindEnabledServersWithIds(tx *dbs.Tx, serverIds []int64)
return
}
// CountAllServerNamesWithUserId 计算某个用户下的所有域名数
func (this *ServerDAO) CountAllServerNamesWithUserId(tx *dbs.Tx, userId int64, userPlanId int64) (int64, error) {
if userId <= 0 {
return 0, nil
}
var query = this.Query(tx).
Attr("userId", userId).
State(ServerStateEnabled).
Where("JSON_TYPE(plainServerNames)='ARRAY'")
if userPlanId > 0 {
query.Attr("userPlanId", userPlanId)
}
return query.
SumInt64("JSON_LENGTH(plainServerNames)", 0)
}
// CountServerNames 计算某个网站下的所有域名数
func (this *ServerDAO) CountServerNames(tx *dbs.Tx, serverId int64) (int64, error) {
if serverId <= 0 {
return 0, nil
}
return this.Query(tx).
Result("JSON_LENGTH(plainServerNames)").
Pk(serverId).
State(ServerStateEnabled).
Where("JSON_TYPE(plainServerNames)='ARRAY'").
FindInt64Col(0)
}
// CheckServerPlanQuota 检查网站套餐限制
func (this *ServerDAO) CheckServerPlanQuota(tx *dbs.Tx, serverId int64, countServerNames int) error {
if serverId <= 0 {
return errors.New("invalid 'serverId'")
}
if countServerNames <= 0 {
return nil
}
userPlanId, err := this.FindServerUserPlanId(tx, serverId)
if err != nil {
return err
}
if userPlanId <= 0 {
return nil
}
userPlan, err := SharedUserPlanDAO.FindEnabledUserPlan(tx, userPlanId, nil)
if err != nil {
return err
}
if userPlan == nil {
return fmt.Errorf("invalid user plan with id %q", types.String(userPlanId))
}
if userPlan.IsExpired() {
return errors.New("the user plan has been expired")
}
if userPlan.UserId == 0 {
return nil
}
plan, err := SharedPlanDAO.FindEnabledPlan(tx, int64(userPlan.PlanId), nil)
if err != nil {
return err
}
if plan == nil {
return fmt.Errorf("invalid plan with id %q", types.String(userPlan.PlanId))
}
if plan.TotalServerNames > 0 {
totalServerNames, err := this.CountAllServerNamesWithUserId(tx, int64(userPlan.UserId), userPlanId)
if err != nil {
return err
}
if totalServerNames+int64(countServerNames) > int64(plan.TotalServerNames) {
return errors.New("server names over plan quota")
}
}
if plan.TotalServerNamesPerServer > 0 {
if countServerNames > types.Int(plan.TotalServerNamesPerServer) {
return errors.New("server names per server over plan quota")
}
}
return nil
}
// NotifyUpdate 同步服务所在的集群
func (this *ServerDAO) NotifyUpdate(tx *dbs.Tx, serverId int64) error {
if serverId <= 0 {

View File

@@ -10,26 +10,26 @@ import (
)
// CopyServerConfigToServers 拷贝服务配置到一组服务
func (this *ServerDAO) CopyServerConfigToServers(tx *dbs.Tx, fromServerId int64, toServerIds []int64, configCode serverconfigs.ConfigCode) error {
func (this *ServerDAO) CopyServerConfigToServers(tx *dbs.Tx, fromServerId int64, toServerIds []int64, configCode serverconfigs.ConfigCode, wafCopyRegions bool) error {
return errors.New("not implemented")
}
// CopyServerConfigToGroups 拷贝服务配置到分组
func (this *ServerDAO) CopyServerConfigToGroups(tx *dbs.Tx, fromServerId int64, groupIds []int64, configCode string) error {
func (this *ServerDAO) CopyServerConfigToGroups(tx *dbs.Tx, fromServerId int64, groupIds []int64, configCode string, wafCopyRegions bool) error {
return errors.New("not implemented")
}
// CopyServerConfigToCluster 拷贝服务配置到集群
func (this *ServerDAO) CopyServerConfigToCluster(tx *dbs.Tx, fromServerId int64, clusterId int64, configCode string) error {
func (this *ServerDAO) CopyServerConfigToCluster(tx *dbs.Tx, fromServerId int64, clusterId int64, configCode string, wafCopyRegions bool) error {
return errors.New("not implemented")
}
// CopyServerConfigToUser 拷贝服务配置到用户
func (this *ServerDAO) CopyServerConfigToUser(tx *dbs.Tx, fromServerId int64, userId int64, configCode string) error {
func (this *ServerDAO) CopyServerConfigToUser(tx *dbs.Tx, fromServerId int64, userId int64, configCode string, wafCopyRegions bool) error {
return errors.New("not implemented")
}
// CopyServerUAMConfigs 复制UAM设置
func (this *ServerDAO) CopyServerUAMConfigs(tx *dbs.Tx, fromServerId int64, toServerIds []int64) error {
func (this *ServerDAO) CopyServerUAMConfigs(tx *dbs.Tx, fromServerId int64, toServerIds []int64, wafCopyRegions bool) error {
return errors.New("not implemented")
}

View File

@@ -242,7 +242,7 @@ func TestServerDAO_FindEnabledServerWithDomain(t *testing.T) {
}
}
func TestServerDAO_UpdateServerTrafficLimitStatus(t *testing.T) {
func TestServerDAO_RenewServerTrafficLimitStatus(t *testing.T) {
dbs.NotifyReady()
var tx *dbs.Tx
@@ -250,7 +250,7 @@ func TestServerDAO_UpdateServerTrafficLimitStatus(t *testing.T) {
defer func() {
t.Log(time.Since(before).Seconds()*1000, "ms")
}()
err := models.NewServerDAO().UpdateServerTrafficLimitStatus(tx, &serverconfigs.TrafficLimitConfig{
err := models.NewServerDAO().RenewServerTrafficLimitStatus(tx, &serverconfigs.TrafficLimitConfig{
IsOn: true,
DailySize: &shared.SizeCapacity{Count: 1, Unit: "mb"},
MonthlySize: &shared.SizeCapacity{Count: 10, Unit: "mb"},
@@ -263,40 +263,15 @@ func TestServerDAO_UpdateServerTrafficLimitStatus(t *testing.T) {
t.Log("ok")
}
func TestServerDAO_CalculateServerTrafficLimitConfig(t *testing.T) {
func TestServerDAO_UpdateServerTrafficLimitStatus(t *testing.T) {
dbs.NotifyReady()
var dao = models.NewServerDAO()
var tx *dbs.Tx
before := time.Now()
defer func() {
t.Log(time.Since(before).Seconds()*1000, "ms")
}()
var cacheMap = utils.NewCacheMap()
config, err := models.SharedServerDAO.CalculateServerTrafficLimitConfig(tx, 23, cacheMap)
err := dao.UpdateServerTrafficLimitStatus(tx, 23, timeutil.Format("Ymd", time.Now().AddDate(0, 0, 20)), 14, "day", "traffic")
if err != nil {
t.Fatal(err)
}
logs.PrintAsJSON(config, t)
}
func TestServerDAO_CalculateServerTrafficLimitConfig_Cache(t *testing.T) {
dbs.NotifyReady()
var tx *dbs.Tx
before := time.Now()
defer func() {
t.Log(time.Since(before).Seconds()*1000, "ms")
}()
var cacheMap = utils.NewCacheMap()
for i := 0; i < 10; i++ {
config, err := models.SharedServerDAO.CalculateServerTrafficLimitConfig(tx, 23, cacheMap)
if err != nil {
t.Fatal(err)
}
_ = config
}
}
func TestServerDAO_FindBytes(t *testing.T) {

View File

@@ -1,6 +1,7 @@
package models
import (
"context"
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
@@ -78,7 +79,7 @@ func (this *Server) DecodeHTTPSPorts() (ports []int) {
if err != nil {
return nil
}
err = config.Init(nil)
err = config.Init(context.TODO())
if err != nil {
return nil
}
@@ -120,7 +121,7 @@ func (this *Server) DecodeTLSPorts() (ports []int) {
if err != nil {
return nil
}
err = config.Init(nil)
err = config.Init(context.TODO())
if err != nil {
return nil
}

View File

@@ -285,7 +285,7 @@ func (this *SSLCertDAO) ComposeCertConfig(tx *dbs.Tx, certId int64, ignoreData b
}
// CountCerts 计算符合条件的证书数量
func (this *SSLCertDAO) CountCerts(tx *dbs.Tx, isCA bool, isAvailable bool, isExpired bool, expiringDays int64, keyword string, userId int64, domains []string) (int64, error) {
func (this *SSLCertDAO) CountCerts(tx *dbs.Tx, isCA bool, isAvailable bool, isExpired bool, expiringDays int64, keyword string, userId int64, domains []string, userOnly bool) (int64, error) {
var query = this.Query(tx).
State(SSLCertStateEnabled)
if isCA {
@@ -308,8 +308,12 @@ func (this *SSLCertDAO) CountCerts(tx *dbs.Tx, isCA bool, isAvailable bool, isEx
if userId > 0 {
query.Attr("userId", userId)
} else {
// 只查询管理员上传的
query.Attr("userId", 0)
if userOnly {
query.Gt("userId", 0)
} else {
// 只查询管理员上传的
query.Attr("userId", 0)
}
}
// 域名
@@ -322,7 +326,7 @@ func (this *SSLCertDAO) CountCerts(tx *dbs.Tx, isCA bool, isAvailable bool, isEx
}
// ListCertIds 列出符合条件的证书
func (this *SSLCertDAO) ListCertIds(tx *dbs.Tx, isCA bool, isAvailable bool, isExpired bool, expiringDays int64, keyword string, userId int64, domains []string, offset int64, size int64) (certIds []int64, err error) {
func (this *SSLCertDAO) ListCertIds(tx *dbs.Tx, isCA bool, isAvailable bool, isExpired bool, expiringDays int64, keyword string, userId int64, domains []string, userOnly bool, offset int64, size int64) (certIds []int64, err error) {
var query = this.Query(tx).
State(SSLCertStateEnabled)
if isCA {
@@ -345,8 +349,12 @@ func (this *SSLCertDAO) ListCertIds(tx *dbs.Tx, isCA bool, isAvailable bool, isE
if userId > 0 {
query.Attr("userId", userId)
} else {
// 只查询管理员上传的
query.Attr("userId", 0)
if userOnly {
query.Gt("userId", 0)
} else {
// 只查询管理员上传的
query.Attr("userId", 0)
}
}
// 域名
@@ -434,6 +442,14 @@ func (this *SSLCertDAO) CheckUserCert(tx *dbs.Tx, certId int64, userId int64) er
return nil
}
// FindCertUserId 查找证书所属用户ID
func (this *SSLCertDAO) FindCertUserId(tx *dbs.Tx, certId int64) (userId int64, err error) {
return this.Query(tx).
Pk(certId).
Result("userId").
FindInt64Col(0)
}
// UpdateCertUser 修改证书所属用户
func (this *SSLCertDAO) UpdateCertUser(tx *dbs.Tx, certId int64, userId int64) error {
if certId <= 0 || userId <= 0 {
@@ -701,7 +717,7 @@ func (this *SSLCertDAO) buildDomainSearchingQuery(query *dbs.Query, domains []st
}
// 检测 JSON_OVERLAPS() 函数是否可用
var canJSONOverlaps = false
var canJSONOverlaps bool
_, funcErr := this.Instance.FindCol(0, "SELECT JSON_OVERLAPS('[1]', '[1]')")
canJSONOverlaps = funcErr == nil
if canJSONOverlaps {

View File

@@ -83,7 +83,7 @@ func (this *ServerDomainHourlyStatDAO) IncreaseHourlyStat(tx *dbs.Tx, clusterId
if len(hour) != 10 {
return errors.New("invalid hour '" + hour + "'")
}
if len(domain) == 0 {
if len(domain) == 0 || len(domain) > 64 {
return nil
}
err := this.Query(tx).

View File

@@ -70,7 +70,7 @@ func TestServerDomainHourlyStatDAO_FindTopDomainStats(t *testing.T) {
func TestServerDomainHourlyStatDAO_Clean(t *testing.T) {
var dao = NewServerDomainHourlyStatDAO()
err := dao.Clean(nil, 10)
err := dao.CleanDays(nil, 10)
if err != nil {
t.Fatal(err)
}

View File

@@ -160,7 +160,7 @@ func (this *SysLockerDAO) Increase(tx *dbs.Tx, key string, defaultValue int64) (
colValue, err := tx.FindCol(0, "INSERT INTO `"+this.Table+"` (`key`, `version`) VALUES ('"+key+"', "+types.String(defaultValue+sysLockerStep)+") ON DUPLICATE KEY UPDATE `version`=`version`+"+types.String(sysLockerStep)+"; SELECT `version` FROM `"+this.Table+"` WHERE `key`='"+key+"'")
if err != nil {
if CheckSQLErrCode(err, 1064 /** syntax error **/) {
// continue to use seperated query
// continue to use separated query
err = nil
} else {
return 0, err

View File

@@ -7,7 +7,6 @@ import (
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeAPI/internal/zero"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
_ "github.com/go-sql-driver/mysql"
@@ -126,23 +125,6 @@ func (this *SysSettingDAO) CompareInt64Setting(tx *dbs.Tx, code string, anotherV
return 0, nil
}
// ReadGlobalConfig 读取全局配置
func (this *SysSettingDAO) ReadGlobalConfig(tx *dbs.Tx) (*serverconfigs.GlobalConfig, error) {
globalConfigData, err := this.ReadSetting(tx, systemconfigs.SettingCodeServerGlobalConfig)
if err != nil {
return nil, err
}
if len(globalConfigData) == 0 {
return &serverconfigs.GlobalConfig{}, nil
}
config := &serverconfigs.GlobalConfig{}
err = json.Unmarshal(globalConfigData, config)
if err != nil {
return nil, err
}
return config, nil
}
// ReadAdminUIConfig 读取管理员界面配置
func (this *SysSettingDAO) ReadAdminUIConfig(tx *dbs.Tx, cacheMap *utils.CacheMap) (*systemconfigs.AdminUIConfig, error) {
var cacheKey = this.Table + ":ReadAdminUIConfig"

View File

@@ -522,16 +522,6 @@ func (this *UserBandwidthStatDAO) sumBytesField(useAvg bool) string {
return "SUM(bytes) AS bytes"
}
func (this *UserBandwidthStatDAO) fixUserStat(stat *UserBandwidthStat, useAvg bool) *UserBandwidthStat {
if stat == nil {
return nil
}
if useAvg {
stat.Bytes = stat.AvgBytes
}
return stat
}
// HasFullData 检查一个月是否完整数据
// 是为了兼容以前数据,以前的表中没有缓存流量、请求数等字段
func (this *UserBandwidthStatDAO) HasFullData(tx *dbs.Tx, userId int64, month string) (bool, error) {

View File

@@ -428,6 +428,20 @@ func (this *UserDAO) CheckUserEmailPassword(tx *dbs.Tx, verifiedEmail string, en
FindInt64Col(0)
}
// CheckUserMobilePassword 检查邮箱+密码
func (this *UserDAO) CheckUserMobilePassword(tx *dbs.Tx, verifiedEmail string, encryptedPassword string) (int64, error) {
if len(verifiedEmail) == 0 || len(encryptedPassword) == 0 {
return 0, nil
}
return this.Query(tx).
Attr("verifiedMobile", verifiedEmail).
Attr("password", encryptedPassword).
Attr("state", UserStateEnabled).
Attr("isOn", true).
ResultPk().
FindInt64Col(0)
}
// FindUserClusterId 查找用户所在集群
func (this *UserDAO) FindUserClusterId(tx *dbs.Tx, userId int64) (int64, error) {
return this.Query(tx).
@@ -663,7 +677,7 @@ func (this *UserDAO) RenewUserServersState(tx *dbs.Tx, userId int64) (bool, erro
// FindUserIdWithVerifiedEmail 使用验证后Email查找用户ID
func (this *UserDAO) FindUserIdWithVerifiedEmail(tx *dbs.Tx, verifiedEmail string) (int64, error) {
if len(verifiedEmail) == 0 {
return 0, nil
}
return this.Query(tx).
ResultPk().
@@ -672,6 +686,18 @@ func (this *UserDAO) FindUserIdWithVerifiedEmail(tx *dbs.Tx, verifiedEmail strin
FindInt64Col(0)
}
// FindUserIdWithVerifiedMobile 使用验证后手机号码查找用户ID
func (this *UserDAO) FindUserIdWithVerifiedMobile(tx *dbs.Tx, verifiedMobile string) (int64, error) {
if len(verifiedMobile) == 0 {
return 0, nil
}
return this.Query(tx).
ResultPk().
State(UserStateEnabled).
Attr("verifiedMobile", verifiedMobile).
FindInt64Col(0)
}
// UpdateUserVerifiedEmail 修改已激活邮箱
func (this *UserDAO) UpdateUserVerifiedEmail(tx *dbs.Tx, userId int64, verifiedEmail string) error {
if userId <= 0 {
@@ -684,6 +710,18 @@ func (this *UserDAO) UpdateUserVerifiedEmail(tx *dbs.Tx, userId int64, verifiedE
UpdateQuickly()
}
// UpdateUserVerifiedMobile 修改已激活手机号码
func (this *UserDAO) UpdateUserVerifiedMobile(tx *dbs.Tx, userId int64, verifiedMobile string) error {
if userId <= 0 {
return nil
}
return this.Query(tx).
Pk(userId).
Set("verifiedMobile", verifiedMobile).
Set("mobileIsVerified", true).
UpdateQuickly()
}
// FindUserBandwidthAlgoForView 获取用户浏览用的带宽算法
func (this *UserDAO) FindUserBandwidthAlgoForView(tx *dbs.Tx, userId int64, uiConfig *systemconfigs.UserUIConfig) (bandwidthAlgo string, err error) {
bandwidthAlgo, err = this.Query(tx).

View File

@@ -0,0 +1,28 @@
package models
import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
)
type UserMobileVerificationDAO dbs.DAO
func NewUserMobileVerificationDAO() *UserMobileVerificationDAO {
return dbs.NewDAO(&UserMobileVerificationDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeUserMobileVerifications",
Model: new(UserMobileVerification),
PkName: "id",
},
}).(*UserMobileVerificationDAO)
}
var SharedUserMobileVerificationDAO *UserMobileVerificationDAO
func init() {
dbs.OnReady(func() {
SharedUserMobileVerificationDAO = NewUserMobileVerificationDAO()
})
}

View File

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

View File

@@ -0,0 +1,41 @@
package models
import "github.com/iwind/TeaGo/dbs"
const (
UserMobileVerificationField_Id dbs.FieldName = "id" // ID
UserMobileVerificationField_Mobile dbs.FieldName = "mobile" // 手机号码
UserMobileVerificationField_UserId dbs.FieldName = "userId" // 用户ID
UserMobileVerificationField_Code dbs.FieldName = "code" // 激活码
UserMobileVerificationField_CreatedAt dbs.FieldName = "createdAt" // 创建时间
UserMobileVerificationField_IsSent dbs.FieldName = "isSent" // 是否已发送
UserMobileVerificationField_IsVerified dbs.FieldName = "isVerified" // 是否已激活
UserMobileVerificationField_Day dbs.FieldName = "day" // YYYYMMDD
)
// UserMobileVerification 邮箱激活邮件队列
type UserMobileVerification struct {
Id uint64 `field:"id"` // ID
Mobile string `field:"mobile"` // 手机号码
UserId uint64 `field:"userId"` // 用户ID
Code string `field:"code"` // 激活码
CreatedAt uint64 `field:"createdAt"` // 创建时间
IsSent bool `field:"isSent"` // 是否已发送
IsVerified bool `field:"isVerified"` // 是否已激活
Day string `field:"day"` // YYYYMMDD
}
type UserMobileVerificationOperator struct {
Id any // ID
Mobile any // 手机号码
UserId any // 用户ID
Code any // 激活码
CreatedAt any // 创建时间
IsSent any // 是否已发送
IsVerified any // 是否已激活
Day any // YYYYMMDD
}
func NewUserMobileVerificationOperator() *UserMobileVerificationOperator {
return &UserMobileVerificationOperator{}
}

View File

@@ -10,6 +10,7 @@ const (
UserField_Fullname dbs.FieldName = "fullname" // 真实姓名
UserField_Mobile dbs.FieldName = "mobile" // 手机号
UserField_VerifiedMobile dbs.FieldName = "verifiedMobile" // 已验证手机号
UserField_MobileIsVerified dbs.FieldName = "mobileIsVerified" // 手机号是否已验证
UserField_Tel dbs.FieldName = "tel" // 联系电话
UserField_Remark dbs.FieldName = "remark" // 备注
UserField_Email dbs.FieldName = "email" // 邮箱地址
@@ -47,6 +48,7 @@ type User struct {
Fullname string `field:"fullname"` // 真实姓名
Mobile string `field:"mobile"` // 手机号
VerifiedMobile string `field:"verifiedMobile"` // 已验证手机号
MobileIsVerified uint8 `field:"mobileIsVerified"` // 手机号是否已验证
Tel string `field:"tel"` // 联系电话
Remark string `field:"remark"` // 备注
Email string `field:"email"` // 邮箱地址
@@ -83,6 +85,7 @@ type UserOperator struct {
Fullname any // 真实姓名
Mobile any // 手机号
VerifiedMobile any // 已验证手机号
MobileIsVerified any // 手机号是否已验证
Tel any // 联系电话
Remark any // 备注
Email any // 邮箱地址

View File

@@ -1,6 +1,7 @@
package models
import (
"context"
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
@@ -36,7 +37,7 @@ func (this *UserNode) DecodeHTTPS(cacheMap *utils.CacheMap) (*serverconfigs.HTTP
return nil, err
}
err = config.Init(nil)
err = config.Init(context.TODO())
if err != nil {
return nil, err
}
@@ -54,7 +55,7 @@ func (this *UserNode) DecodeHTTPS(cacheMap *utils.CacheMap) (*serverconfigs.HTTP
}
}
err = config.Init(nil)
err = config.Init(context.TODO())
if err != nil {
return nil, err
}

View File

@@ -0,0 +1,239 @@
package models
import (
"errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils/regexputils"
_ "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"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"math"
"sync"
"time"
)
type UserPlanBandwidthStatDAO dbs.DAO
const (
UserPlanBandwidthStatTablePartitions = 20 // 分表数量
)
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedUserPlanBandwidthStatDAO.CleanDefaultDays(nil, 100)
if err != nil {
remotelogs.Error("SharedUserPlanBandwidthStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
func NewUserPlanBandwidthStatDAO() *UserPlanBandwidthStatDAO {
return dbs.NewDAO(&UserPlanBandwidthStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeUserPlanBandwidthStats",
Model: new(UserPlanBandwidthStat),
PkName: "id",
},
}).(*UserPlanBandwidthStatDAO)
}
var SharedUserPlanBandwidthStatDAO *UserPlanBandwidthStatDAO
func init() {
dbs.OnReady(func() {
SharedUserPlanBandwidthStatDAO = NewUserPlanBandwidthStatDAO()
})
}
// UpdateUserPlanBandwidth 写入数据
// 暂时不使用region区分
func (this *UserPlanBandwidthStatDAO) UpdateUserPlanBandwidth(tx *dbs.Tx, userId int64, userPlanId int64, regionId int64, day string, timeAt string, bandwidthBytes int64, totalBytes int64, cachedBytes int64, attackBytes int64, countRequests int64, countCachedRequests int64, countAttackRequests int64) error {
if userId <= 0 || userPlanId <= 0 {
return nil
}
return this.Query(tx).
Table(this.partialTable(userPlanId)).
Param("bytes", bandwidthBytes).
Param("totalBytes", totalBytes).
Param("cachedBytes", cachedBytes).
Param("attackBytes", attackBytes).
Param("countRequests", countRequests).
Param("countCachedRequests", countCachedRequests).
Param("countAttackRequests", countAttackRequests).
InsertOrUpdateQuickly(maps.Map{
"userId": userId,
"userPlanId": userPlanId,
"regionId": regionId,
"day": day,
"timeAt": timeAt,
"bytes": bandwidthBytes,
"totalBytes": totalBytes,
"avgBytes": totalBytes / 300,
"cachedBytes": cachedBytes,
"attackBytes": attackBytes,
"countRequests": countRequests,
"countCachedRequests": countCachedRequests,
"countAttackRequests": countAttackRequests,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"avgBytes": dbs.SQL("(totalBytes+:totalBytes)/300"), // 因为生成SQL语句时会自动将avgBytes排在totalBytes之前所以这里不用担心先后顺序的问题
"totalBytes": dbs.SQL("totalBytes+:totalBytes"),
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
})
}
// FindMonthlyPercentile 获取某月内百分位
func (this *UserPlanBandwidthStatDAO) FindMonthlyPercentile(tx *dbs.Tx, userPlanId int64, month string, percentile int, useAvg bool) (result int64, err error) {
if percentile <= 0 {
percentile = 95
}
// 如果是100%以上,则快速返回
if percentile >= 100 {
result, err = this.Query(tx).
Table(this.partialTable(userPlanId)).
Attr("userPlanId", userPlanId).
Result(this.sumBytesField(useAvg)).
Between("day", month+"01", month+"31").
Group("day").
Group("timeAt").
Desc("bytes").
Limit(1).
FindInt64Col(0)
return
}
// 总数量
total, err := this.Query(tx).
Table(this.partialTable(userPlanId)).
Attr("userPlanId", userPlanId).
Between("day", month+"01", month+"31").
CountAttr("DISTINCT day, timeAt")
if err != nil {
return 0, err
}
if total == 0 {
return 0, nil
}
var offset int64
if total > 1 {
offset = int64(math.Ceil(float64(total) * float64(100-percentile) / 100))
}
// 查询 nth 位置
result, err = this.Query(tx).
Table(this.partialTable(userPlanId)).
Attr("userPlanId", userPlanId).
Result(this.sumBytesField(useAvg)).
Between("day", month+"01", month+"31").
Group("day").
Group("timeAt").
Desc("bytes").
Offset(offset).
Limit(1).
FindInt64Col(0)
return
}
// SumMonthlyBytes 读取单月总流量
func (this *UserPlanBandwidthStatDAO) SumMonthlyBytes(tx *dbs.Tx, userPlanId int64, month string) (int64, error) {
if !regexputils.YYYYMM.MatchString(month) {
return 0, errors.New("invalid ")
}
return this.Query(tx).
Table(this.partialTable(userPlanId)).
Attr("userPlanId", userPlanId).
Between("day", month+"01", month+"31").
SumInt64("totalBytes", 0)
}
// CleanDefaultDays 清理过期数据
func (this *UserPlanBandwidthStatDAO) CleanDefaultDays(tx *dbs.Tx, defaultDays int) error {
databaseConfig, err := SharedSysSettingDAO.ReadDatabaseConfig(tx)
if err != nil {
return err
}
if databaseConfig != nil && databaseConfig.UserPlanBandwidthStat.Clean.Days > 0 {
defaultDays = databaseConfig.UserPlanBandwidthStat.Clean.Days
}
if defaultDays <= 0 {
defaultDays = 100
}
return this.CleanDays(tx, defaultDays)
}
// CleanDays 清理过期数据
func (this *UserPlanBandwidthStatDAO) CleanDays(tx *dbs.Tx, days int) error {
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days)) // 保留大约3个月的数据
return this.runBatch(func(table string, locker *sync.Mutex) error {
_, err := this.Query(tx).
Table(table).
Lt("day", day).
Delete()
return err
})
}
// 获取字节字段
func (this *UserPlanBandwidthStatDAO) bytesField(useAvg bool) string {
if useAvg {
return "avgBytes AS bytes"
}
return "bytes"
}
func (this *UserPlanBandwidthStatDAO) sumBytesField(useAvg bool) string {
if useAvg {
return "SUM(avgBytes) AS bytes"
}
return "SUM(bytes) AS bytes"
}
// 批量执行
func (this *UserPlanBandwidthStatDAO) runBatch(f func(table string, locker *sync.Mutex) error) error {
var locker = &sync.Mutex{}
var wg = sync.WaitGroup{}
wg.Add(UserPlanBandwidthStatTablePartitions)
var resultErr error
for i := 0; i < UserPlanBandwidthStatTablePartitions; i++ {
var table = this.partialTable(int64(i))
go func(table string) {
defer wg.Done()
err := f(table, locker)
if err != nil {
resultErr = err
}
}(table)
}
wg.Wait()
return resultErr
}
// 获取分区表
func (this *UserPlanBandwidthStatDAO) partialTable(userPlanId int64) string {
return this.Table + "_" + types.String(userPlanId%int64(UserPlanBandwidthStatTablePartitions))
}

View File

@@ -0,0 +1,39 @@
package models_test
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/dbs"
timeutil "github.com/iwind/TeaGo/utils/time"
"testing"
)
func TestUserPlanBandwidthStatDAO_FindMonthlyPercentile(t *testing.T) {
var dao = models.NewUserPlanBandwidthStatDAO()
var tx *dbs.Tx
{
resultBytes, err := dao.FindMonthlyPercentile(tx, 20, timeutil.Format("Ym"), 100, false)
if err != nil {
t.Fatal(err)
}
t.Log("result bytes0:", resultBytes)
}
{
resultBytes, err := dao.FindMonthlyPercentile(tx, 20, timeutil.Format("Ym"), 95, false)
if err != nil {
t.Fatal(err)
}
t.Log("result bytes1:", resultBytes)
}
{
resultBytes, err := dao.FindMonthlyPercentile(tx, 20, timeutil.Format("Ym"), 95, true)
if err != nil {
t.Fatal(err)
}
t.Log("result bytes2:", resultBytes)
}
}

View File

@@ -0,0 +1,59 @@
package models
import "github.com/iwind/TeaGo/dbs"
const (
UserPlanBandwidthStatField_Id dbs.FieldName = "id" // ID
UserPlanBandwidthStatField_UserId dbs.FieldName = "userId" // 用户ID
UserPlanBandwidthStatField_UserPlanId dbs.FieldName = "userPlanId" // 用户套餐ID
UserPlanBandwidthStatField_Day dbs.FieldName = "day" // 日期YYYYMMDD
UserPlanBandwidthStatField_TimeAt dbs.FieldName = "timeAt" // 时间点HHII
UserPlanBandwidthStatField_Bytes dbs.FieldName = "bytes" // 带宽
UserPlanBandwidthStatField_RegionId dbs.FieldName = "regionId" // 区域ID
UserPlanBandwidthStatField_TotalBytes dbs.FieldName = "totalBytes" // 总流量
UserPlanBandwidthStatField_AvgBytes dbs.FieldName = "avgBytes" // 平均流量
UserPlanBandwidthStatField_CachedBytes dbs.FieldName = "cachedBytes" // 缓存的流量
UserPlanBandwidthStatField_AttackBytes dbs.FieldName = "attackBytes" // 攻击流量
UserPlanBandwidthStatField_CountRequests dbs.FieldName = "countRequests" // 请求数
UserPlanBandwidthStatField_CountCachedRequests dbs.FieldName = "countCachedRequests" // 缓存的请求数
UserPlanBandwidthStatField_CountAttackRequests dbs.FieldName = "countAttackRequests" // 攻击请求数
)
// UserPlanBandwidthStat 用户套餐带宽峰值
type UserPlanBandwidthStat struct {
Id uint64 `field:"id"` // ID
UserId uint64 `field:"userId"` // 用户ID
UserPlanId uint64 `field:"userPlanId"` // 用户套餐ID
Day string `field:"day"` // 日期YYYYMMDD
TimeAt string `field:"timeAt"` // 时间点HHII
Bytes uint64 `field:"bytes"` // 带宽
RegionId uint32 `field:"regionId"` // 区域ID
TotalBytes uint64 `field:"totalBytes"` // 总流量
AvgBytes uint64 `field:"avgBytes"` // 平均流量
CachedBytes uint64 `field:"cachedBytes"` // 缓存的流量
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
CountRequests uint64 `field:"countRequests"` // 请求数
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存的请求数
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击请求数
}
type UserPlanBandwidthStatOperator struct {
Id any // ID
UserId any // 用户ID
UserPlanId any // 用户套餐ID
Day any // 日期YYYYMMDD
TimeAt any // 时间点HHII
Bytes any // 带宽
RegionId any // 区域ID
TotalBytes any // 总流量
AvgBytes any // 平均流量
CachedBytes any // 缓存的流量
AttackBytes any // 攻击流量
CountRequests any // 请求数
CountCachedRequests any // 缓存的请求数
CountAttackRequests any // 攻击请求数
}
func NewUserPlanBandwidthStatOperator() *UserPlanBandwidthStatOperator {
return &UserPlanBandwidthStatOperator{}
}

View File

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

View File

@@ -1 +1,8 @@
package models
import timeutil "github.com/iwind/TeaGo/utils/time"
// IsExpired 判断套餐是否过期
func (this *UserPlan) IsExpired() bool {
return len(this.DayTo) == 0 || this.DayTo < timeutil.Format("Y-m-d")
}

View File

@@ -0,0 +1,28 @@
package models
import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
)
type UserPlanStatDAO dbs.DAO
func NewUserPlanStatDAO() *UserPlanStatDAO {
return dbs.NewDAO(&UserPlanStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeUserPlanStats",
Model: new(UserPlanStat),
PkName: "id",
},
}).(*UserPlanStatDAO)
}
var SharedUserPlanStatDAO *UserPlanStatDAO
func init() {
dbs.OnReady(func() {
SharedUserPlanStatDAO = NewUserPlanStatDAO()
})
}

View File

@@ -0,0 +1,10 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package models
import "github.com/iwind/TeaGo/dbs"
func (this *UserPlanStatDAO) IncreaseUserPlanStat(tx *dbs.Tx, userPlanId int64, trafficBytes int64, countRequests int64) error {
return nil
}

View File

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

View File

@@ -0,0 +1,38 @@
package models
import "github.com/iwind/TeaGo/dbs"
const (
UserPlanStatField_Id dbs.FieldName = "id" // ID
UserPlanStatField_UserPlanId dbs.FieldName = "userPlanId" // 用户套餐ID
UserPlanStatField_Date dbs.FieldName = "date" // 日期YYYYMMDD或YYYYMM
UserPlanStatField_DateType dbs.FieldName = "dateType" // 日期类型day|month
UserPlanStatField_TrafficBytes dbs.FieldName = "trafficBytes" // 流量
UserPlanStatField_CountRequests dbs.FieldName = "countRequests" // 总请求数
UserPlanStatField_IsProcessed dbs.FieldName = "isProcessed" // 是否已处理
)
// UserPlanStat 用户套餐统计
type UserPlanStat struct {
Id uint64 `field:"id"` // ID
UserPlanId uint64 `field:"userPlanId"` // 用户套餐ID
Date string `field:"date"` // 日期YYYYMMDD或YYYYMM
DateType string `field:"dateType"` // 日期类型day|month
TrafficBytes uint64 `field:"trafficBytes"` // 流量
CountRequests uint64 `field:"countRequests"` // 总请求数
IsProcessed bool `field:"isProcessed"` // 是否已处理
}
type UserPlanStatOperator struct {
Id any // ID
UserPlanId any // 用户套餐ID
Date any // 日期YYYYMMDD或YYYYMM
DateType any // 日期类型day|month
TrafficBytes any // 流量
CountRequests any // 总请求数
IsProcessed any // 是否已处理
}
func NewUserPlanStatOperator() *UserPlanStatOperator {
return &UserPlanStatOperator{}
}

View File

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

View File

@@ -61,10 +61,7 @@ func (this *DomainRecordsCache) WriteDomainRecords(providerId int64, domain stri
return
}
var clonedRecords = []*dnstypes.Record{}
for _, record := range records {
clonedRecords = append(clonedRecords, record)
}
var clonedRecords = append([]*dnstypes.Record{}, records...)
this.domainRecordsMap[domain] = &recordList{
version: version,
updatedAt: time.Now().Unix(),

View File

@@ -1,9 +1,8 @@
package dnsclients
import (
"errors"
"fmt"
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/dnstypes"
"github.com/iwind/TeaGo/types"
)
type BaseProvider struct{}
@@ -18,11 +17,11 @@ func (this *BaseProvider) WrapError(err error, domain string, record *dnstypes.R
return err
}
var fullname = ""
var fullname string
if len(record.Name) == 0 {
fullname = domain
} else {
fullname = record.Name + "." + domain
}
return errors.New("record operation failed: '" + fullname + " " + record.Type + " " + record.Value + " " + types.String(record.TTL) + "': " + err.Error())
return fmt.Errorf("record operation failed: '%s %s %s %d': %w", fullname, record.Type, record.Value, record.TTL, err)
}

View File

@@ -6,6 +6,7 @@ import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/cloudflare"
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/dnstypes"
@@ -337,7 +338,7 @@ func (this *CloudFlareProvider) doAPI(method string, apiPath string, args map[st
err = json.Unmarshal(data, respPtr)
if err != nil {
return errors.New("decode json failed: " + err.Error() + ", response text: " + string(data))
return fmt.Errorf("decode json failed: %w, response text: %s", err, string(data))
}
return nil

View File

@@ -55,7 +55,9 @@ func (this *CustomHTTPProvider) Auth(params maps.Map) error {
// GetDomains 获取所有域名列表
func (this *CustomHTTPProvider) GetDomains() (domains []string, err error) {
resp, err := this.post(maps.Map{})
resp, err := this.post(maps.Map{
"action": "GetDomains",
})
if err != nil {
return nil, err
}
@@ -201,7 +203,7 @@ func (this *CustomHTTPProvider) post(params maps.Map) (respData []byte, err erro
defer func() {
_ = resp.Body.Close()
}()
if resp.StatusCode != 200 {
if resp.StatusCode != http.StatusOK {
return nil, errors.New("status should be 200, but got '" + strconv.Itoa(resp.StatusCode) + "'")
}
return io.ReadAll(resp.Body)

View File

@@ -4,6 +4,7 @@ import (
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/dnspod"
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/dnstypes"
"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
@@ -380,7 +381,7 @@ func (this *DNSPodProvider) doAPI(path string, params map[string]string, respPtr
req, err := http.NewRequest(http.MethodPost, apiHost+path, strings.NewReader(query.Encode()))
if err != nil {
return errors.New("create request failed: " + err.Error())
return fmt.Errorf("create request failed: %w", err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("User-Agent", "GoEdge-Client/1.0.0 (iwind.liu@gmail.com)")

View File

@@ -7,6 +7,7 @@ import (
"crypto/tls"
"encoding/json"
"errors"
"fmt"
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/dnstypes"
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/edgeapi"
@@ -435,6 +436,11 @@ func (this *EdgeDNSAPIProvider) doAPI(path string, params map[string]any, respPt
if err != nil {
return err
}
defer func() {
if resp.Body != nil {
_ = resp.Body.Close()
}
}()
if resp.StatusCode != http.StatusOK {
return errors.New("invalid response status code '" + types.String(resp.StatusCode) + "'")
@@ -447,7 +453,7 @@ func (this *EdgeDNSAPIProvider) doAPI(path string, params map[string]any, respPt
err = json.Unmarshal(data, respPtr)
if err != nil {
return errors.New("decode response failed: " + err.Error() + ", JSON: " + string(data))
return fmt.Errorf("decode response failed: %w, JSON: %s", err, string(data))
}
if !respPtr.IsValid() {

View File

@@ -52,7 +52,7 @@ func FindAllProviderTypes() []maps.Map {
typeMaps = append(typeMaps, maps.Map{
"name": "自定义HTTP DNS",
"code": ProviderTypeCustomHTTP,
"description": "通过自定义的HTTP接口提供DNS服务",
"description": "通过自定义的HTTP接口提供DNS服务具体使用方法请参考官网文档https://goedge.cn/docs/DNS/CustomHTTP.md ",
})
return typeMaps
}

View File

@@ -19,7 +19,6 @@ func TestAES128CFBMethod_Encrypt(t *testing.T) {
dst = dst[:len(src)]
t.Log("dst:", string(dst))
src = make([]byte, len(src))
src, err = method.Decrypt(dst)
if err != nil {
t.Fatal(err)
@@ -64,7 +63,6 @@ func TestAES128CFBMethod_Encrypt2(t *testing.T) {
for _, dst := range sources {
dst2 := append([]byte{}, dst...)
src2 := make([]byte, len(dst2))
src2, err := method.Decrypt(dst2)
if err != nil {
t.Fatal(err)

View File

@@ -13,9 +13,9 @@ func (this *DetailedError) Code() string {
return this.code
}
func NewDetailedError(code string, error string) *DetailedError {
func NewDetailedError(code string, errString string) *DetailedError {
return &DetailedError{
msg: error,
msg: errString,
code: code,
}
}

View File

@@ -10,7 +10,7 @@ func On(event string, callback func()) {
locker.Lock()
defer locker.Unlock()
callbacks, _ := eventsMap[event]
var callbacks = eventsMap[event]
callbacks = append(callbacks, callback)
eventsMap[event] = callbacks
}
@@ -18,9 +18,9 @@ func On(event string, callback func()) {
// Notify 通知事件
func Notify(event string) {
locker.Lock()
callbacks, _ := eventsMap[event]
var callbacks = eventsMap[event]
locker.Unlock()
for _, callback := range callbacks {
callback()
}

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