Compare commits

..

52 Commits

Author SHA1 Message Date
刘祥超
6906b3094b 增加edge-api reset命令 2022-10-30 20:07:16 +08:00
刘祥超
aec28b5087 集群中自动设置CNAME记录如果已经存在,则跳过 2022-10-28 15:27:52 +08:00
刘祥超
b59ed1f73e 测试环境下申请ACME证书时打印调试日志 2022-10-27 20:12:00 +08:00
刘祥超
2a83f61bdd 优化代码 2022-10-27 18:12:15 +08:00
刘祥超
4f21d60ca4 更新SQL 2022-10-27 11:18:49 +08:00
刘祥超
0a6111b2e5 自动检测本地数据库磁盘是否已满,如果已满,则不再写入访问日志 2022-10-27 10:27:47 +08:00
刘祥超
4b425e1698 节点SSH登录自动使用集群设置 2022-10-26 19:25:07 +08:00
刘祥超
bee7da807b 节点设置中增加“通过IP名单”选项 2022-10-26 10:41:53 +08:00
刘祥超
a906a7db06 提升流量账单精度 2022-10-26 09:38:13 +08:00
刘祥超
ea62cf0ff7 增加用户账单中流量精度 2022-10-25 19:14:31 +08:00
刘祥超
72e0c55f5d IP名单支持模糊查询 2022-10-25 10:15:32 +08:00
刘祥超
3a88f23181 修复域名解析--集群中单节点多IP时无法修改IP的Bug 2022-10-24 16:34:07 +08:00
刘祥超
967c9080fb 更新用户服务可用状态时同时返回状态 2022-10-23 20:12:28 +08:00
刘祥超
f44b9434ad 更新相关库 2022-10-23 19:57:43 +08:00
刘祥超
e21a3c5f8c 节点所属集群删除后,不再接收API请求 2022-10-23 19:56:58 +08:00
刘祥超
88dae56b6c 优化用户服务整体启用和禁用 2022-10-23 16:22:20 +08:00
刘祥超
40c3475306 提升小数数字精度 2022-10-23 11:54:00 +08:00
刘祥超
318c8dd566 优化小数数字格式化 2022-10-23 09:25:20 +08:00
刘祥超
a5f30b1573 修复默认生成的集群没有开启节点和服务DNS同步的Bug 2022-10-23 09:02:45 +08:00
刘祥超
a8ec959c70 优化小数格式化 2022-10-22 10:22:45 +08:00
刘祥超
c393b2f480 更新SQL 2022-10-22 10:22:31 +08:00
刘祥超
548f56f8f0 用户账单增加字段/优化代码 2022-10-21 15:45:22 +08:00
刘祥超
9aa71365b9 安装生成的默认集群默认的DNS设置包含节点和服务自动同步 2022-10-21 14:48:59 +08:00
刘祥超
292fb72a26 优化代码 2022-10-20 22:59:17 +08:00
刘祥超
e5315c3b8d DNSPod和Alidns记录信息增加缓存 2022-10-20 21:47:21 +08:00
刘祥超
a4dd7bb75a 优化DNSPod和Alidns相关代码 2022-10-20 18:06:27 +08:00
刘祥超
53ef0f3fb2 修复访问日志采样率可能会被放大的Bug 2022-10-20 16:12:35 +08:00
刘祥超
6af8bff802 增加操作节点区域相关接口 2022-10-20 15:11:57 +08:00
刘祥超
d849f7440a 更新SQL 2022-10-20 10:42:06 +08:00
刘祥超
425c0ec44c 增加流量包相关表 2022-10-20 10:24:36 +08:00
刘祥超
aad0b01581 修复在域名同步后,无法看到线路等问题的Bug 2022-10-19 19:56:36 +08:00
刘祥超
bc2c3dfa0b 查询DNSPod域名和记录时每页尺寸从100增加到3000 2022-10-17 19:49:36 +08:00
刘祥超
0fe76430c6 实现用户计费方式相关多个接口 2022-10-15 19:16:08 +08:00
刘祥超
979ff4c44e 优化代码 2022-10-14 16:16:42 +08:00
刘祥超
b0b6b5984f 优化代码/删除不需要的代码 2022-10-14 10:03:29 +08:00
刘祥超
5d4da6cccb 查询带宽统计时,自动调整开始和结束日期顺序 2022-10-04 08:55:03 +08:00
刘祥超
912ffa062f 更新SQL 2022-10-03 20:54:18 +08:00
刘祥超
1db4661c75 增加多个服务流量、带宽统计API 2022-10-03 19:28:03 +08:00
刘祥超
f7dd9e3f39 创建通知消息的时候限制内容长度不超过1024长度 2022-10-03 16:07:14 +08:00
刘祥超
0cc74b920e 版本修改为0.5.6 2022-10-01 08:49:32 +08:00
刘祥超
51c3807d01 删除不必要的文件 2022-10-01 07:14:28 +08:00
刘祥超
c2c42ca2b7 优化服务列表返回速度 2022-09-30 13:50:19 +08:00
刘祥超
2a6db6ebfe 边缘节点远程安装文件最小化(从16.xM减少到2.xM) 2022-09-30 10:34:32 +08:00
刘祥超
30d8edbdcf 执行uname和systemctl时增加命令完整路径 2022-09-30 09:40:38 +08:00
刘祥超
177afafe12 完善订单相关表 2022-09-29 10:22:17 +08:00
刘祥超
98765b6e2a 版本调整为v0.5.5 2022-09-28 18:56:47 +08:00
刘祥超
e4e0aab010 阶段性提交 2022-09-28 17:38:52 +08:00
刘祥超
ed87b4e2a9 用户节点版本改为0.5.4 2022-09-28 08:56:40 +08:00
刘祥超
337eb36d25 DNS版本改为0.2.8 2022-09-28 08:16:57 +08:00
刘祥超
c44e40d72d systemd服务增加BEGIN INIT INFO 2022-09-28 08:16:49 +08:00
刘祥超
2e8ba831a1 DNS版本修改为0.2.7.1 2022-09-27 08:06:16 +08:00
刘祥超
a706c2a5a5 将版本修改为0.5.4 2022-09-26 15:17:00 +08:00
167 changed files with 5144 additions and 2962 deletions

View File

@@ -5,6 +5,7 @@ import (
"flag"
"fmt"
"github.com/TeaOSLab/EdgeAPI/internal/apps"
"github.com/TeaOSLab/EdgeAPI/internal/configs"
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
"github.com/TeaOSLab/EdgeAPI/internal/nodes"
"github.com/TeaOSLab/EdgeAPI/internal/setup"
@@ -71,6 +72,14 @@ func main() {
}
fmt.Println("done")
})
app.On("reset", func() {
err := configs.ResetAPIConfig()
if err != nil {
fmt.Println("[ERROR]reset failed: " + err.Error())
return
}
fmt.Println("done")
})
app.On("goman", func() {
var sock = gosock.NewTmpSock(teaconst.ProcessName)
reply, err := sock.Send(&gosock.Command{Code: "goman"})

View File

@@ -2,7 +2,7 @@ package main
import (
"flag"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeAPI/internal/installers/helpers"
"github.com/iwind/gosock/pkg/gosock"
"os"
"os/exec"
@@ -51,7 +51,7 @@ func main() {
return
}
unzip := utils.NewUnzip(zipPath, targetPath)
unzip := helpers.NewUnzip(zipPath, targetPath)
err := unzip.Run()
if err != nil {
stderr("ERROR: " + err.Error())

View File

@@ -1,8 +1,9 @@
package main
// 注意这里的依赖文件应该最小化,从而使编译后的文件最小化
import (
"flag"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeAPI/internal/installers/helpers"
"github.com/iwind/gosock/pkg/gosock"
"os"
"os/exec"
@@ -51,7 +52,7 @@ func main() {
return
}
unzip := utils.NewUnzip(zipPath, targetPath)
unzip := helpers.NewUnzip(zipPath, targetPath)
err := unzip.Run()
if err != nil {
stderr("ERROR: " + err.Error())

13
go.mod
View File

@@ -14,11 +14,14 @@ require (
github.com/golang/protobuf v1.5.2
github.com/iwind/TeaGo v0.0.0-20220811034530-657e3f15b79e
github.com/iwind/gosock v0.0.0-20220505115348-f88412125a62
github.com/miekg/dns v1.1.43
github.com/mozillazg/go-pinyin v0.18.0
github.com/pkg/sftp v1.12.0
github.com/shirou/gopsutil/v3 v3.22.2
golang.org/x/crypto v0.0.0-20220214200702-86341886e292
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8
github.com/smartwalle/alipay/v3 v3.1.7
golang.org/x/crypto v0.1.0
golang.org/x/net v0.1.0
golang.org/x/sys v0.1.0
google.golang.org/grpc v1.45.0
google.golang.org/protobuf v1.27.1
gopkg.in/yaml.v3 v3.0.1
@@ -26,21 +29,21 @@ require (
require (
github.com/cenkalti/backoff/v4 v4.1.1 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/miekg/dns v1.1.43 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/smartwalle/crypto4go v1.0.2 // indirect
github.com/tklauser/go-sysconf v0.3.9 // indirect
github.com/tklauser/numcpus v0.3.0 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/text v0.4.0 // indirect
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect

32
go.sum
View File

@@ -66,6 +66,7 @@ github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7F
github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -236,13 +237,8 @@ github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhK
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/infobloxopen/infoblox-go-client v1.1.1/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI=
github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/TeaGo v0.0.0-20220304043459-0dd944a5b475/go.mod h1:HRHK0zoC/og3c9/hKosD9yYVMTnnzm3PgXUdhRYHaLc=
github.com/iwind/TeaGo v0.0.0-20220408111647-f36b9bba3570 h1:zqz2FiMMkSHXWO1EsTRJDPTwX9xQ4uuyD5GAE4JGlhM=
github.com/iwind/TeaGo v0.0.0-20220408111647-f36b9bba3570/go.mod h1:HRHK0zoC/og3c9/hKosD9yYVMTnnzm3PgXUdhRYHaLc=
github.com/iwind/TeaGo v0.0.0-20220811034530-657e3f15b79e h1:cw4b6ecXdXvLd45YSstD8r9ClcnVK4ljZMZCept2aOk=
github.com/iwind/TeaGo v0.0.0-20220811034530-657e3f15b79e/go.mod h1:fi/Pq+/5m2HZoseM+39dMF57ANXRt6w4PkGu3NXPc5s=
github.com/iwind/gosock v0.0.0-20210722083328-12b2d66abec3 h1:aBSonas7vFcgTj9u96/bWGILGv1ZbUSTLiOzcI1ZT6c=
github.com/iwind/gosock v0.0.0-20210722083328-12b2d66abec3/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
github.com/iwind/gosock v0.0.0-20220505115348-f88412125a62 h1:HJH6RDheAY156DnIfJSD/bEvqyXzsZuE2gzs8PuUjoo=
github.com/iwind/gosock v0.0.0-20220505115348-f88412125a62/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
@@ -409,6 +405,10 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/smartwalle/alipay/v3 v3.1.7 h1:J4U5slABafKVD/b9gPCZe/3HAPB8Pa2NOYOPcugEJBo=
github.com/smartwalle/alipay/v3 v3.1.7/go.mod h1:cZUMCCnsux9YAxA0/f3PWUR+7wckWtE1BqxbVRtGij0=
github.com/smartwalle/crypto4go v1.0.2 h1:9DUEOOsPhmp00438L4oBdcL8EZG1zumecft5bWj5phI=
github.com/smartwalle/crypto4go v1.0.2/go.mod h1:LQ7vCZIb7BE5+MuMtJBuO8ORkkQ01m4DXDBWPzLbkMY=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w=
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
@@ -481,6 +481,7 @@ golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -492,8 +493,8 @@ golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -560,8 +561,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -630,14 +631,12 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -646,8 +645,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -789,7 +788,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -9,30 +9,11 @@ type Provider struct {
Code string `json:"code"`
Description string `json:"description"`
APIURL string `json:"apiURL"`
TestAPIURL string `json:"testAPIURL"`
RequireEAB bool `json:"requireEAB"`
EABDescription string `json:"eabDescription"`
}
func FindAllProviders() []*Provider {
return []*Provider{
{
Name: "Let's Encrypt",
Code: DefaultProviderCode,
Description: "非盈利组织Let's Encrypt提供的免费证书。",
APIURL: "https://acme-v02.api.letsencrypt.org/directory",
RequireEAB: false,
},
{
Name: "ZeroSSL",
Code: "zerossl",
Description: "相关文档 <a href=\"https://zerossl.com/documentation/acme/\" target=\"_blank\">https://zerossl.com/documentation/acme/</a>。",
APIURL: "https://acme.zerossl.com/v2/DV90",
RequireEAB: true,
EABDescription: "在官网<a href=\"https://app.zerossl.com/developer\" target=\"_blank\">[Developer]</a>页面底部点击\"Generate\"按钮生成。",
},
}
}
func FindProviderWithCode(code string) *Provider {
for _, provider := range FindAllProviders() {
if provider.Code == code {

View File

@@ -0,0 +1,24 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package acme
func FindAllProviders() []*Provider {
return []*Provider{
{
Name: "Let's Encrypt",
Code: DefaultProviderCode,
Description: "非盈利组织Let's Encrypt提供的免费证书。",
APIURL: "https://acme-v02.api.letsencrypt.org/directory",
RequireEAB: false,
},
{
Name: "ZeroSSL",
Code: "zerossl",
Description: "相关文档 <a href=\"https://zerossl.com/documentation/acme/\" target=\"_blank\">https://zerossl.com/documentation/acme/</a>。",
APIURL: "https://acme.zerossl.com/v2/DV90",
RequireEAB: true,
EABDescription: "在官网<a href=\"https://app.zerossl.com/developer\" target=\"_blank\">[Developer]</a>页面底部点击\"Generate\"按钮生成。",
},
}
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/go-acme/lego/v4/lego"
acmelog "github.com/go-acme/lego/v4/log"
"github.com/go-acme/lego/v4/registration"
"github.com/iwind/TeaGo/Tea"
"io"
"log"
)
@@ -139,7 +140,9 @@ func (this *Request) runDNS() (certData []byte, keyData []byte, err error) {
func (this *Request) runHTTP() (certData []byte, keyData []byte, err error) {
if !this.debug {
acmelog.Logger = log.New(io.Discard, "", log.LstdFlags)
if !Tea.IsTesting() {
acmelog.Logger = log.New(io.Discard, "", log.LstdFlags)
}
}
if this.task.User == nil {

View File

@@ -132,3 +132,47 @@ func (this *APIConfig) WriteFile(path string) error {
return os.WriteFile(path, data, 0666)
}
// ResetAPIConfig 重置配置
func ResetAPIConfig() error {
for _, filename := range []string{"api.yaml", "db.yaml"} {
// 重置 configs/api.yaml
{
var configFile = Tea.ConfigFile(filename)
stat, err := os.Stat(configFile)
if err == nil && !stat.IsDir() {
err = os.Remove(configFile)
if err != nil {
return err
}
}
}
// 重置 ~/.edge-api/api.yaml
homeDir, homeErr := os.UserHomeDir()
if homeErr == nil {
var configFile = homeDir + "/." + teaconst.ProcessName + "/" + filename
stat, err := os.Stat(configFile)
if err == nil && !stat.IsDir() {
err = os.Remove(configFile)
if err != nil {
return err
}
}
}
// 重置 /etc/edge-api/api.yaml
{
var configFile = "/etc/" + teaconst.ProcessName + "/" + filename
stat, err := os.Stat(configFile)
if err == nil && !stat.IsDir() {
err = os.Remove(configFile)
if err != nil {
return err
}
}
}
}
return nil
}

View File

@@ -1,7 +1,7 @@
package teaconst
const (
Version = "0.5.3.1"
Version = "0.5.6"
ProductName = "Edge API"
ProcessName = "edge-api"
@@ -18,9 +18,9 @@ const (
// 其他节点版本号,用来检测是否有需要升级的节点
NodeVersion = "0.5.3"
UserNodeVersion = "0.5.0"
DNSNodeVersion = "0.2.7"
NodeVersion = "0.5.6"
UserNodeVersion = "0.5.6"
DNSNodeVersion = "0.2.8"
AuthorityNodeVersion = "0.0.2"
MonitorNodeVersion = "0.0.4"
ReportNodeVersion = "0.1.1"

View File

@@ -13,22 +13,26 @@ type OrderMethod struct {
Url string `field:"url"` // URL
Secret string `field:"secret"` // 密钥
Params dbs.JSON `field:"params"` // 参数
ClientType string `field:"clientType"` // 客户端类型
QrcodeTitle string `field:"qrcodeTitle"` // 二维码标题
Order uint32 `field:"order"` // 排序
State uint8 `field:"state"` // 状态
}
type OrderMethodOperator struct {
Id interface{} // ID
Name interface{} // 名称
IsOn interface{} // 是否启用
Description interface{} // 描述
ParentCode interface{} // 内置的父级代号
Code interface{} // 代号
Url interface{} // URL
Secret interface{} // 密钥
Params interface{} // 参数
Order interface{} // 排序
State interface{} // 状态
Id any // ID
Name any // 名称
IsOn any // 是否启用
Description any // 描述
ParentCode any // 内置的父级代号
Code any // 代号
Url any // URL
Secret any // 密钥
Params any // 参数
ClientType any // 客户端类型
QrcodeTitle any // 二维码标题
Order any // 排序
State any // 状态
}
func NewOrderMethodOperator() *OrderMethodOperator {

View File

@@ -1,80 +0,0 @@
package accounts
import (
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type UserAccountDailyStatDAO dbs.DAO
func NewUserAccountDailyStatDAO() *UserAccountDailyStatDAO {
return dbs.NewDAO(&UserAccountDailyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeUserAccountDailyStats",
Model: new(UserAccountDailyStat),
PkName: "id",
},
}).(*UserAccountDailyStatDAO)
}
var SharedUserAccountDailyStatDAO *UserAccountDailyStatDAO
func init() {
dbs.OnReady(func() {
SharedUserAccountDailyStatDAO = NewUserAccountDailyStatDAO()
})
}
// UpdateDailyStat 更新当天统计数据
func (this *UserAccountDailyStatDAO) UpdateDailyStat(tx *dbs.Tx) error {
var day = timeutil.Format("Ymd")
var month = timeutil.Format("Ym")
income, err := SharedUserAccountLogDAO.SumDailyEventTypes(tx, day, userconfigs.AccountIncomeEventTypes)
if err != nil {
return err
}
expense, err := SharedUserAccountLogDAO.SumDailyEventTypes(tx, day, userconfigs.AccountExpenseEventTypes)
if err != nil {
return err
}
if expense < 0 {
expense = -expense
}
return this.Query(tx).
InsertOrUpdateQuickly(maps.Map{
"day": day,
"month": month,
"income": income,
"expense": expense,
}, maps.Map{
"income": income,
"expense": expense,
})
}
// FindDailyStats 查看按天统计
func (this *UserAccountDailyStatDAO) FindDailyStats(tx *dbs.Tx, dayFrom string, dayTo string) (result []*UserAccountDailyStat, err error) {
_, err = this.Query(tx).
Between("day", dayFrom, dayTo).
Slice(&result).
FindAll()
return
}
// FindMonthlyStats 查看某月统计
func (this *UserAccountDailyStatDAO) FindMonthlyStats(tx *dbs.Tx, dayFrom string, dayTo string) (result []*UserAccountDailyStat, err error) {
_, err = this.Query(tx).
Result("SUM(income) AS income", "SUM(expense) AS expense", "month").
Between("day", dayFrom, dayTo).
Group("month").
Slice(&result).
FindAll()
return
}

View File

@@ -1,253 +0,0 @@
package accounts
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
_ "github.com/go-sql-driver/mysql"
"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"
"time"
)
func init() {
dbs.OnReadyDone(func() {
goman.New(func() {
// 自动支付账单任务
var ticker = time.NewTicker(12 * time.Hour)
for range ticker.C {
if SharedUserAccountDAO.Instance != nil {
err := SharedUserAccountDAO.Instance.RunTx(func(tx *dbs.Tx) error {
return SharedUserAccountDAO.PayBills(tx)
})
if err != nil {
remotelogs.Error("USER_ACCOUNT_DAO", "pay bills task failed: "+err.Error())
}
}
}
})
})
}
type UserAccountDAO dbs.DAO
func NewUserAccountDAO() *UserAccountDAO {
return dbs.NewDAO(&UserAccountDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeUserAccounts",
Model: new(UserAccount),
PkName: "id",
},
}).(*UserAccountDAO)
}
var SharedUserAccountDAO *UserAccountDAO
func init() {
dbs.OnReady(func() {
SharedUserAccountDAO = NewUserAccountDAO()
})
}
// FindUserAccountWithUserId 根据用户ID查找用户账户
func (this *UserAccountDAO) FindUserAccountWithUserId(tx *dbs.Tx, userId int64) (*UserAccount, error) {
if userId <= 0 {
return nil, errors.New("invalid userId '" + types.String(userId) + "'")
}
// 用户是否存在
user, err := models.SharedUserDAO.FindEnabledUser(tx, userId, nil)
if err != nil {
return nil, err
}
if user == nil {
return nil, errors.New("invalid userId '" + types.String(userId) + "'")
}
account, err := this.Query(tx).
Attr("userId", userId).
Find()
if err != nil {
return nil, err
}
if account != nil {
return account.(*UserAccount), nil
}
var op = NewUserAccountOperator()
op.UserId = userId
_, err = this.SaveInt64(tx, op)
if err != nil {
return nil, err
}
return this.FindUserAccountWithUserId(tx, userId)
}
// FindUserAccountWithAccountId 根据ID查找用户账户
func (this *UserAccountDAO) FindUserAccountWithAccountId(tx *dbs.Tx, accountId int64) (*UserAccount, error) {
one, err := this.Query(tx).
Pk(accountId).
Find()
if one != nil {
return one.(*UserAccount), nil
}
return nil, err
}
// UpdateUserAccount 操作用户账户
func (this *UserAccountDAO) UpdateUserAccount(tx *dbs.Tx, accountId int64, delta float32, eventType userconfigs.AccountEventType, description string, params maps.Map) error {
account, err := this.FindUserAccountWithAccountId(tx, accountId)
if err != nil {
return err
}
if account == nil {
return errors.New("invalid account id '" + types.String(accountId) + "'")
}
var userId = int64(account.UserId)
var deltaFloat64 = float64(delta)
if deltaFloat64 < 0 && account.Total < -deltaFloat64 {
return errors.New("not enough account quota to decrease")
}
// 操作账户
err = this.Query(tx).
Pk(account.Id).
Set("total", dbs.SQL("total+:delta")).
Param("delta", delta).
UpdateQuickly()
if err != nil {
return err
}
// 生成日志
err = SharedUserAccountLogDAO.CreateAccountLog(tx, userId, accountId, delta, 0, eventType, description, params)
if err != nil {
return err
}
return nil
}
// UpdateUserAccountFrozen 操作用户账户冻结余额
func (this *UserAccountDAO) UpdateUserAccountFrozen(tx *dbs.Tx, userId int64, delta float32, eventType userconfigs.AccountEventType, description string, params maps.Map) error {
account, err := this.FindUserAccountWithUserId(tx, userId)
if err != nil {
return err
}
var deltaFloat64 = float64(delta)
if deltaFloat64 < 0 && account.TotalFrozen < -deltaFloat64 {
return errors.New("not enough account frozen quota to decrease")
}
// 操作账户
err = this.Query(tx).
Pk(account.Id).
Set("totalFrozen", dbs.SQL("total+:delta")).
Param("delta", delta).
UpdateQuickly()
if err != nil {
return err
}
// 生成日志
err = SharedUserAccountLogDAO.CreateAccountLog(tx, userId, int64(account.Id), 0, delta, eventType, description, params)
if err != nil {
return err
}
return nil
}
// CountAllAccounts 计算所有账户数量
func (this *UserAccountDAO) CountAllAccounts(tx *dbs.Tx, keyword string) (int64, error) {
var query = this.Query(tx)
if len(keyword) > 0 {
query.Where("userId IN (SELECT id FROM " + models.SharedUserDAO.Table + " WHERE state=1 AND (username LIKE :keyword OR fullname LIKE :keyword))")
query.Param("keyword", keyword)
} else {
query.Where("userId IN (SELECT id FROM " + models.SharedUserDAO.Table + " WHERE state=1)")
}
return query.Count()
}
// ListAccounts 列出单页账户
func (this *UserAccountDAO) ListAccounts(tx *dbs.Tx, keyword string, offset int64, size int64) (result []*UserAccount, err error) {
var query = this.Query(tx)
if len(keyword) > 0 {
query.Where("userId IN (SELECT id FROM " + models.SharedUserDAO.Table + " WHERE state=1 AND (username LIKE :keyword OR fullname LIKE :keyword))")
query.Param("keyword", keyword)
} else {
query.Where("userId IN (SELECT id FROM " + models.SharedUserDAO.Table + " WHERE state=1)")
}
_, err = query.
DescPk().
Offset(offset).
Limit(size).
Slice(&result).
FindAll()
return
}
// PayBills 尝试自动支付账单
func (this *UserAccountDAO) PayBills(tx *dbs.Tx) error {
bills, err := models.SharedUserBillDAO.FindUnpaidBills(tx, 10000)
if err != nil {
return err
}
// 先支付久远的
lists.Reverse(bills)
for _, bill := range bills {
if bill.Amount <= 0 {
err = models.SharedUserBillDAO.UpdateUserBillIsPaid(tx, int64(bill.Id), true)
if err != nil {
return err
}
continue
}
account, err := SharedUserAccountDAO.FindUserAccountWithUserId(tx, int64(bill.UserId))
if err != nil {
return err
}
if account == nil || account.Total < bill.Amount {
continue
}
// 扣款
err = SharedUserAccountDAO.UpdateUserAccount(tx, int64(account.Id), -float32(bill.Amount), userconfigs.AccountEventTypePayBill, "支付账单"+bill.Code, maps.Map{"billId": bill.Id})
if err != nil {
return err
}
// 改为已支付
err = models.SharedUserBillDAO.UpdateUserBillIsPaid(tx, int64(bill.Id), true)
if err != nil {
return err
}
}
return nil
}
// CheckUserAccount 检查用户账户
func (this *UserAccountDAO) CheckUserAccount(tx *dbs.Tx, userId int64, accountId int64) error {
exists, err := this.Query(tx).
Pk(accountId).
Attr("userId", userId).
Exist()
if err != nil {
return err
}
if !exists {
return models.ErrNotFound
}
return nil
}

View File

@@ -1,18 +0,0 @@
package accounts
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/dbs"
"testing"
)
func TestUserAccountDAO_PayBills(t *testing.T) {
dbs.NotifyReady()
err := NewUserAccountDAO().PayBills(nil)
if err != nil {
t.Fatal(err)
}
t.Log("ok")
}

View File

@@ -1,129 +0,0 @@
package accounts
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
_ "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"
)
type UserAccountLogDAO dbs.DAO
func NewUserAccountLogDAO() *UserAccountLogDAO {
return dbs.NewDAO(&UserAccountLogDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeUserAccountLogs",
Model: new(UserAccountLog),
PkName: "id",
},
}).(*UserAccountLogDAO)
}
var SharedUserAccountLogDAO *UserAccountLogDAO
func init() {
dbs.OnReady(func() {
SharedUserAccountLogDAO = NewUserAccountLogDAO()
})
}
// CreateAccountLog 生成用户账户日志
func (this *UserAccountLogDAO) CreateAccountLog(tx *dbs.Tx, userId int64, accountId int64, delta float32, deltaFrozen float32, eventType userconfigs.AccountEventType, description string, params maps.Map) error {
var op = NewUserAccountLogOperator()
op.UserId = userId
op.AccountId = accountId
op.Delta = delta
op.DeltaFrozen = deltaFrozen
account, err := SharedUserAccountDAO.FindUserAccountWithAccountId(tx, accountId)
if err != nil {
return err
}
if account == nil {
return errors.New("invalid account id '" + types.String(accountId) + "'")
}
op.Total = account.Total
op.TotalFrozen = account.TotalFrozen
op.EventType = eventType
op.Description = description
if params == nil {
params = maps.Map{}
}
op.Params = params.AsJSON()
op.Day = timeutil.Format("Ymd")
err = this.Save(tx, op)
if err != nil {
return err
}
return SharedUserAccountDailyStatDAO.UpdateDailyStat(tx)
}
// CountAccountLogs 计算日志数量
func (this *UserAccountLogDAO) CountAccountLogs(tx *dbs.Tx, userId int64, accountId int64, keyword string, eventType string) (int64, error) {
var query = this.Query(tx)
if userId > 0 {
query.Attr("userId", userId)
}
if accountId > 0 {
query.Attr("accountId", accountId)
}
if len(keyword) > 0 {
query.Where("(userId IN (SELECT id FROM " + models.SharedUserDAO.Table + " WHERE state=1 AND (username LIKE :keyword OR fullname LIKE :keyword)) OR description LIKE :keyword)")
query.Param("keyword", dbutils.QuoteLike(keyword))
}
if len(eventType) > 0 {
query.Attr("eventType", eventType)
}
return query.Count()
}
// ListAccountLogs 列出单页日志
func (this *UserAccountLogDAO) ListAccountLogs(tx *dbs.Tx, userId int64, accountId int64, keyword string, eventType string, offset int64, size int64) (result []*UserAccountLog, err error) {
var query = this.Query(tx)
if userId > 0 {
query.Attr("userId", userId)
}
if accountId > 0 {
query.Attr("accountId", accountId)
}
if len(keyword) > 0 {
query.Where("(userId IN (SELECT id FROM " + models.SharedUserDAO.Table + " WHERE state=1 AND (username LIKE :keyword OR fullname LIKE :keyword)) OR description LIKE :keyword)")
query.Param("keyword", dbutils.QuoteLike(keyword))
}
if len(eventType) > 0 {
query.Attr("eventType", eventType)
}
_, err = query.
DescPk().
Offset(offset).
Limit(size).
Slice(&result).
FindAll()
return
}
// SumDailyEventTypes 统计某天数据总和
func (this *UserAccountLogDAO) SumDailyEventTypes(tx *dbs.Tx, day string, eventTypes []userconfigs.AccountEventType) (float32, error) {
if len(eventTypes) == 0 {
return 0, nil
}
result, err := this.Query(tx).
Attr("day", day).
Attr("eventType", eventTypes).
Sum("delta", 0)
if err != nil {
return 0, err
}
return types.Float32(result), nil
}

View File

@@ -356,7 +356,7 @@ func (this *ACMETaskDAO) runTaskWithoutLog(tx *dbs.Tx, taskId int64) (isOk bool,
errMsg = "找不到DNS服务商账号"
return
}
providerInterface := dnsclients.FindProvider(dnsProvider.Type)
providerInterface := dnsclients.FindProvider(dnsProvider.Type, int64(dnsProvider.Id))
if providerInterface == nil {
errMsg = "暂不支持此类型的DNS服务商 '" + dnsProvider.Type + "'"
return

View File

@@ -77,7 +77,7 @@ func (this *ApiTokenDAO) FindEnabledTokenWithNodeCacheable(tx *dbs.Tx, nodeId st
State(ApiTokenStateEnabled).
Find()
if one != nil {
token := one.(*ApiToken)
token = one.(*ApiToken)
SharedCacheLocker.Lock()
apiTokenCacheMap[nodeId] = token
SharedCacheLocker.Unlock()

View File

@@ -2,6 +2,7 @@ package models
import (
"fmt"
dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
@@ -29,8 +30,9 @@ var httpAccessLogDAOMapping = map[int64]*HTTPAccessLogDAOWrapper{} // dbNodeId =
// HTTPAccessLogDAOWrapper HTTP访问日志DAO
type HTTPAccessLogDAOWrapper struct {
DAO *HTTPAccessLogDAO
NodeId int64
DAO *HTTPAccessLogDAO
NodeId int64
IsLocal bool
}
func init() {
@@ -195,7 +197,7 @@ func (this *DBNodeInitializer) loop() error {
continue
}
daoObject := dbs.DAOObject{
var daoObject = dbs.DAOObject{
Instance: db,
DB: node.Name + "(id:" + strconv.Itoa(int(node.Id)) + ")",
Table: tableDef.Name,
@@ -210,12 +212,13 @@ func (this *DBNodeInitializer) loop() error {
accessLogLocker.Lock()
accessLogDBMapping[nodeId] = db
dao := &HTTPAccessLogDAO{
var dao = &HTTPAccessLogDAO{
DAOObject: daoObject,
}
httpAccessLogDAOMapping[nodeId] = &HTTPAccessLogDAOWrapper{
DAO: dao,
NodeId: nodeId,
DAO: dao,
NodeId: nodeId,
IsLocal: dbutils.IsLocalAddr(node.Host),
}
accessLogLocker.Unlock()
}

View File

@@ -67,7 +67,7 @@ func CheckClusterDNS(tx *dbs.Tx, cluster *models.NodeCluster, checkNodeIssues bo
})
return
}
var dnsProvider = dnsclients.FindProvider(provider.Type)
var dnsProvider = dnsclients.FindProvider(provider.Type, int64(provider.Id))
if dnsProvider == nil {
issues = append(issues, &pb.DNSIssue{
Target: cluster.Name,
@@ -200,7 +200,7 @@ func FindDefaultDomainRoute(tx *dbs.Tx, domain *dns.DNSDomain) (string, error) {
if err != nil {
return "", errors.New("decode provider params failed: " + err.Error())
}
var dnsProvider = dnsclients.FindProvider(provider.Type)
var dnsProvider = dnsclients.FindProvider(provider.Type, int64(provider.Id))
if dnsProvider == nil {
return "", errors.New("not supported provider type '" + provider.Type + "'")
}

View File

@@ -51,6 +51,10 @@ var (
accessLogRowsPerTable int64 = 500_000 // 自动分表的单表最大值
)
func AccessLogQueuePercent() int {
return accessLogQueuePercent
}
type accessLogTableQuery struct {
daoWrapper *HTTPAccessLogDAOWrapper
name string
@@ -131,13 +135,13 @@ func (this *HTTPAccessLogDAO) CreateHTTPAccessLogs(tx *dbs.Tx, accessLogs []*pb.
// 写入队列
var queue = accessLogQueue // 这样写非常重要,防止在写入过程中队列有切换
for _, accessLog := range accessLogs {
if accessLog.FirewallPolicyId == 0 { // 如果是WAF记录则采取采样率
if accessLog.FirewallPolicyId == 0 { // 如果是WAF记录则采取采样率
// 采样率
if accessLogQueuePercent <= 0 {
return nil
}
if accessLogQueuePercent < 100 && rands.Int(1, 100) > accessLogQueuePercent {
return nil
continue
}
}
@@ -157,16 +161,27 @@ func (this *HTTPAccessLogDAO) DumpAccessLogsFromQueue(size int) (hasMore bool, e
size = 100
}
if len(oldAccessLogQueue) == 0 && len(accessLogQueue) == 0 {
return false, nil
}
var dao = randomHTTPAccessLogDAO()
if dao == nil {
dao = &HTTPAccessLogDAOWrapper{
DAO: SharedHTTPAccessLogDAO,
NodeId: 0,
}
}
if len(oldAccessLogQueue) == 0 && len(accessLogQueue) == 0 {
return false, nil
// 检查本地数据库空间
if dbutils.IsLocalDatabase && !dbutils.HasFreeSpace {
return false, errors.New("dump accesslog failed: there is no enough space left for database (" + dbutils.LocalDatabaseDataDir + ")")
}
} else if dao.IsLocal {
// 检查本地数据库空间
// 我们假定本地只能安装一个数据库访问日志中的数据库和当前API连接的数据库一致
if !dbutils.HasFreeSpace {
return true, errors.New("dump accesslog failed: there is no enough space left for database (" + dbutils.LocalDatabaseDataDir + ")")
}
}
// 开始事务

View File

@@ -353,7 +353,7 @@ func (this *IPItemDAO) UpdateIPItem(tx *dbs.Tx, itemId int64, ipFrom string, ipT
}
// CountIPItemsWithListId 计算IP数量
func (this *IPItemDAO) CountIPItemsWithListId(tx *dbs.Tx, listId int64, ipFrom string, ipTo string, keyword string, eventLevel string) (int64, error) {
func (this *IPItemDAO) CountIPItemsWithListId(tx *dbs.Tx, listId int64, keyword string, ipFrom string, ipTo string, eventLevel string) (int64, error) {
var query = this.Query(tx).
State(IPItemStateEnabled).
Attr("listId", listId)
@@ -466,8 +466,11 @@ func (this *IPItemDAO) ExistsEnabledItem(tx *dbs.Tx, itemId int64) (bool, error)
}
// CountAllEnabledIPItems 计算数量
func (this *IPItemDAO) CountAllEnabledIPItems(tx *dbs.Tx, ip string, listId int64, unread bool, eventLevel string, listType string) (int64, error) {
func (this *IPItemDAO) CountAllEnabledIPItems(tx *dbs.Tx, keyword string, ip string, listId int64, unread bool, eventLevel string, listType string) (int64, error) {
var query = this.Query(tx)
if len(keyword) > 0 {
query.Like("ipFrom", dbutils.QuoteLike(keyword))
}
if len(ip) > 0 {
query.Attr("ipFrom", ip)
}
@@ -496,8 +499,11 @@ func (this *IPItemDAO) CountAllEnabledIPItems(tx *dbs.Tx, ip string, listId int6
}
// ListAllEnabledIPItems 搜索所有IP
func (this *IPItemDAO) ListAllEnabledIPItems(tx *dbs.Tx, ip string, listId int64, unread bool, eventLevel string, listType string, offset int64, size int64) (result []*IPItem, err error) {
func (this *IPItemDAO) ListAllEnabledIPItems(tx *dbs.Tx, keyword string, ip string, listId int64, unread bool, eventLevel string, listType string, offset int64, size int64) (result []*IPItem, err error) {
var query = this.Query(tx)
if len(keyword) > 0 {
query.Like("ipFrom", dbutils.QuoteLike(keyword))
}
if len(ip) > 0 {
query.Attr("ipFrom", ip)
}
@@ -608,7 +614,7 @@ func (this *IPItemDAO) NotifyUpdate(tx *dbs.Tx, itemId int64) error {
return err
}
for _, clusterId := range clusterIds {
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, NodeTaskTypeIPItemChanged)
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeIPItemChanged)
if err != nil {
return err
}
@@ -616,7 +622,7 @@ func (this *IPItemDAO) NotifyUpdate(tx *dbs.Tx, itemId int64) error {
} else {
clusterIds, err := SharedNodeClusterDAO.FindAllEnabledNodeClusterIds(tx)
for _, clusterId := range clusterIds {
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, NodeTaskTypeIPItemChanged)
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeIPItemChanged)
if err != nil {
return err
}
@@ -668,7 +674,7 @@ func (this *IPItemDAO) NotifyUpdate(tx *dbs.Tx, itemId int64) error {
if len(resultClusterIds) > 0 {
for _, clusterId := range resultClusterIds {
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, NodeTaskTypeIPItemChanged)
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeIPItemChanged)
if err != nil {
return err
}

View File

@@ -301,7 +301,7 @@ func (this *IPListDAO) NotifyUpdate(tx *dbs.Tx, listId int64, taskType NodeTaskT
if len(resultClusterIds) > 0 {
for _, clusterId := range resultClusterIds {
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, taskType)
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, taskType)
if err != nil {
return err
}

View File

@@ -4,6 +4,7 @@ import (
"crypto/md5"
"fmt"
"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"
@@ -154,19 +155,16 @@ func (this *MessageDAO) CreateNodeMessage(tx *dbs.Tx, role string, clusterId int
// CreateMessage 创建普通消息
func (this *MessageDAO) CreateMessage(tx *dbs.Tx, adminId int64, userId int64, messageType MessageType, level string, subject string, body string, paramsJSON []byte) error {
body = utils.LimitString(subject, 100)
body = utils.LimitString(body, 1024)
var op = NewMessageOperator()
op.AdminId = adminId
op.UserId = userId
op.Type = messageType
op.Level = level
subjectRunes := []rune(subject)
if len(subjectRunes) > 100 {
op.Subject = string(subjectRunes[:100]) + "..."
} else {
op.Subject = subject
}
op.Subject = subject
op.Body = body
if len(paramsJSON) > 0 {
op.Params = paramsJSON

View File

@@ -368,7 +368,7 @@ func (this *MetricItemDAO) NotifyUpdate(tx *dbs.Tx, itemId int64, isPublic bool)
return err
}
for _, clusterId := range clusterIds {
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, NodeTaskTypeConfigChanged)
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeConfigChanged)
if err != nil {
return err
}
@@ -380,7 +380,7 @@ func (this *MetricItemDAO) NotifyUpdate(tx *dbs.Tx, itemId int64, isPublic bool)
return err
}
for _, clusterId := range clusterIds {
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, NodeTaskTypeConfigChanged)
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeConfigChanged)
if err != nil {
return err
}

View File

@@ -198,7 +198,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) 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) error {
if clusterId <= 0 {
return errors.New("invalid clusterId")
}
@@ -226,6 +226,14 @@ func (this *NodeClusterDAO) UpdateCluster(tx *dbs.Tx, clusterId int64, name stri
op.AutoRemoteStart = autoRemoteStart
op.AutoInstallNftables = autoInstallTables
if sshParams != nil {
sshParamsJSON, err := json.Marshal(sshParams)
if err != nil {
return err
}
op.SshParams = sshParamsJSON
}
err := this.Save(tx, op)
if err != nil {
return err
@@ -454,6 +462,27 @@ func (this *NodeClusterDAO) FindClusterGrantId(tx *dbs.Tx, clusterId int64) (int
FindInt64Col(0)
}
// FindClusterSSHParams 查找集群的SSH默认参数
func (this *NodeClusterDAO) FindClusterSSHParams(tx *dbs.Tx, clusterId int64) (*nodeconfigs.SSHParams, error) {
sshParamsJSON, err := this.Query(tx).
Pk(clusterId).
Result("sshParams").
FindJSONCol()
if err != nil {
return nil, err
}
var params = nodeconfigs.DefaultSSHParams()
if len(sshParamsJSON) == 0 {
return params, nil
}
err = json.Unmarshal(sshParamsJSON, params)
if err != nil {
return nil, err
}
return params, nil
}
// FindClusterDNSInfo 查找DNS信息
func (this *NodeClusterDAO) FindClusterDNSInfo(tx *dbs.Tx, clusterId int64, cacheMap *utils.CacheMap) (*NodeCluster, error) {
var cacheKey = this.Table + ":FindClusterDNSInfo:" + types.String(clusterId)
@@ -967,7 +996,7 @@ func (this *NodeClusterDAO) FindClusterBasicInfo(tx *dbs.Tx, clusterId int64, ca
cluster, err := this.Query(tx).
Pk(clusterId).
State(NodeClusterStateEnabled).
Result("id", "timeZone", "nodeMaxThreads", "cachePolicyId", "httpFirewallPolicyId", "autoOpenPorts", "webp", "uam", "isOn", "ddosProtection", "clock", "globalServerConfig", "autoInstallNftables").
Result("id", "name", "timeZone", "nodeMaxThreads", "cachePolicyId", "httpFirewallPolicyId", "autoOpenPorts", "webp", "uam", "isOn", "ddosProtection", "clock", "globalServerConfig", "autoInstallNftables").
Find()
if err != nil || cluster == nil {
return nil, err
@@ -1132,7 +1161,7 @@ func (this *NodeClusterDAO) UpdateClusterDDoSProtection(tx *dbs.Tx, clusterId in
if err != nil {
return err
}
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, NodeTaskTypeDDosProtectionChanged)
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeDDosProtectionChanged)
}
// FindClusterGlobalServerConfig 查询全局服务配置
@@ -1175,12 +1204,12 @@ func (this *NodeClusterDAO) UpdateClusterGlobalServerConfig(tx *dbs.Tx, clusterI
return err
}
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, NodeTaskTypeGlobalServerConfigChanged)
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeGlobalServerConfigChanged)
}
// NotifyUpdate 通知更新
func (this *NodeClusterDAO) NotifyUpdate(tx *dbs.Tx, clusterId int64) error {
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, NodeTaskTypeConfigChanged)
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeConfigChanged)
}
// NotifyDNSUpdate 通知DNS更新

View File

@@ -177,5 +177,5 @@ func (this *NodeClusterMetricItemDAO) ExistsClusterItem(tx *dbs.Tx, clusterId in
// NotifyUpdate 通知更新
func (this *NodeClusterMetricItemDAO) NotifyUpdate(tx *dbs.Tx, clusterId int64) error {
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, NodeTaskTypeConfigChanged)
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeConfigChanged)
}

View File

@@ -15,6 +15,7 @@ type NodeCluster struct {
Order uint32 `field:"order"` // 排序
CreatedAt uint64 `field:"createdAt"` // 创建时间
GrantId uint32 `field:"grantId"` // 默认认证方式
SshParams dbs.JSON `field:"sshParams"` // SSH默认参数
State uint8 `field:"state"` // 状态
AutoRegister uint8 `field:"autoRegister"` // 是否开启自动注册
UniqueId string `field:"uniqueId"` // 唯一ID
@@ -53,6 +54,7 @@ type NodeClusterOperator struct {
Order any // 排序
CreatedAt any // 创建时间
GrantId any // 默认认证方式
SshParams any // SSH默认参数
State any // 状态
AutoRegister any // 是否开启自动注册
UniqueId any // 唯一ID

View File

@@ -13,17 +13,9 @@ import (
func (this *NodeCluster) DecodeDNSConfig() (*dnsconfigs.ClusterDNSConfig, error) {
if len(this.Dns) == 0 {
// 一定要返回一个默认的值防止产生nil
return &dnsconfigs.ClusterDNSConfig{
NodesAutoSync: false,
ServersAutoSync: false,
CNAMEAsDomain: true,
IncludingLnNodes: true,
}, nil
}
var dnsConfig = &dnsconfigs.ClusterDNSConfig{
CNAMEAsDomain: true,
IncludingLnNodes: true,
return dnsconfigs.DefaultClusterDNSConfig(), nil
}
var dnsConfig = dnsconfigs.DefaultClusterDNSConfig()
err := json.Unmarshal(this.Dns, &dnsConfig)
if err != nil {
return nil, err

View File

@@ -11,6 +11,7 @@ import (
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAPI/internal/utils/sizes"
"github.com/TeaOSLab/EdgeAPI/internal/utils/ttlcache"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
@@ -35,8 +36,6 @@ const (
NodeStateDisabled = 0 // 已禁用
)
var nodeIdCacheMap = map[string]int64{} // uniqueId => nodeId
type NodeDAO dbs.DAO
func NewNodeDAO() *NodeDAO {
@@ -77,9 +76,7 @@ func (this *NodeDAO) DisableNode(tx *dbs.Tx, nodeId int64) (err error) {
return err
}
if len(uniqueId) > 0 {
SharedCacheLocker.Lock()
delete(nodeIdCacheMap, uniqueId)
SharedCacheLocker.Unlock()
ttlcache.SharedCache.Delete("nodeId@uniqueId@" + uniqueId)
}
_, err = this.Query(tx).
@@ -167,6 +164,7 @@ func (this *NodeDAO) CreateNode(tx *dbs.Tx, adminId int64, name string, clusterI
op.GroupId = groupId
op.RegionId = regionId
op.IsOn = 1
op.EnableIPLists = 1
op.State = NodeStateEnabled
err = this.Save(tx, op)
if err != nil {
@@ -190,7 +188,7 @@ func (this *NodeDAO) CreateNode(tx *dbs.Tx, adminId int64, name string, clusterI
}
// UpdateNode 修改节点
func (this *NodeDAO) UpdateNode(tx *dbs.Tx, nodeId int64, name string, clusterId int64, secondaryClusterIds []int64, groupId int64, regionId int64, isOn bool, level int, lnAddrs []string) error {
func (this *NodeDAO) UpdateNode(tx *dbs.Tx, nodeId int64, name string, clusterId int64, secondaryClusterIds []int64, groupId int64, regionId int64, isOn bool, level int, lnAddrs []string, enableIPLists bool) error {
if nodeId <= 0 {
return errors.New("invalid nodeId")
}
@@ -250,6 +248,8 @@ func (this *NodeDAO) UpdateNode(tx *dbs.Tx, nodeId int64, name string, clusterId
op.LnAddrs = lnAddrsJSON
}
op.EnableIPLists = enableIPLists
err = this.Save(tx, op)
if err != nil {
return err
@@ -949,17 +949,18 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, cacheMap *utils
}
var config = &nodeconfigs.NodeConfig{
Id: int64(node.Id),
NodeId: node.UniqueId,
Secret: node.Secret,
IsOn: node.IsOn,
Servers: nil,
Version: int64(node.Version),
Name: node.Name,
MaxCPU: types.Int32(node.MaxCPU),
RegionId: int64(node.RegionId),
Level: types.Int32(node.Level),
GroupId: int64(node.GroupId),
Id: int64(node.Id),
NodeId: node.UniqueId,
Secret: node.Secret,
IsOn: node.IsOn,
Servers: nil,
Version: int64(node.Version),
Name: node.Name,
MaxCPU: types.Int32(node.MaxCPU),
RegionId: int64(node.RegionId),
Level: types.Int32(node.Level),
GroupId: int64(node.GroupId),
EnableIPLists: node.EnableIPLists,
}
// API节点IP
@@ -976,7 +977,7 @@ func (this *NodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64, cacheMap *utils
}
for _, server := range servers {
serverConfig, err := SharedServerDAO.ComposeServerConfig(tx, server, cacheMap, true)
serverConfig, err := SharedServerDAO.ComposeServerConfig(tx, server, cacheMap, true, false)
if err != nil {
return nil, err
}
@@ -1279,36 +1280,39 @@ func (this *NodeDAO) UpdateNodeConnectedAPINodes(tx *dbs.Tx, nodeId int64, apiNo
// FindEnabledNodeIdWithUniqueId 根据UniqueId获取ID
func (this *NodeDAO) FindEnabledNodeIdWithUniqueId(tx *dbs.Tx, uniqueId string) (int64, error) {
return this.Query(tx).
State(NodeStateEnabled).
Attr("uniqueId", uniqueId).
ResultPk().
FindInt64Col(0)
}
// FindEnabledNodeIdWithUniqueIdCacheable 根据UniqueId获取ID并可以使用缓存
func (this *NodeDAO) FindEnabledNodeIdWithUniqueIdCacheable(tx *dbs.Tx, uniqueId string) (int64, error) {
SharedCacheLocker.RLock()
nodeId, ok := nodeIdCacheMap[uniqueId]
if ok {
SharedCacheLocker.RUnlock()
return nodeId, nil
var cacheKey = "nodeId@uniqueId@" + uniqueId
var item = ttlcache.SharedCache.Read(cacheKey)
if item != nil {
return types.Int64(item.Value), nil
}
SharedCacheLocker.RUnlock()
nodeId, err := this.Query(tx).
one, err := this.Query(tx).
State(NodeStateEnabled).
Attr("uniqueId", uniqueId).
ResultPk().
FindInt64Col(0)
Result("id", "clusterId").
Find()
if err != nil || one == nil {
return 0, err
}
// 检查集群
var node = one.(*Node)
var clusterId = int64(node.ClusterId)
if clusterId <= 0 {
return 0, nil
}
isOn, err := SharedNodeClusterDAO.CheckNodeClusterIsOn(tx, clusterId)
if err != nil {
return 0, err
}
if nodeId > 0 {
SharedCacheLocker.Lock()
nodeIdCacheMap[uniqueId] = nodeId
SharedCacheLocker.Unlock()
if !isOn {
return 0, nil
}
return nodeId, nil
ttlcache.SharedCache.Write(cacheKey, int64(node.Id), time.Now().Unix()+60)
return int64(node.Id), nil
}
// CountAllEnabledNodesWithGrantId 计算使用某个认证的节点数量
@@ -1417,6 +1421,49 @@ func (this *NodeDAO) CountAllEnabledNodesWithRegionId(tx *dbs.Tx, regionId int64
Count()
}
// CountAllNodeRegionInfo 查找所有节点区域信息数量
func (this *NodeDAO) CountAllNodeRegionInfo(tx *dbs.Tx, regionId int64) (int64, error) {
var query = this.Query(tx).
State(NodeStateEnabled).
Where("clusterId IN (SELECT id FROM " + SharedNodeClusterDAO.Table + " WHERE state=1)")
if regionId > 0 {
query.Attr("regionId", regionId)
}
return query.Count()
}
// ListNodeRegionInfo 列出节点区域信息
func (this *NodeDAO) ListNodeRegionInfo(tx *dbs.Tx, regionId int64, offset int64, size int64) (result []*Node, err error) {
var query = this.Query(tx).
Result("id", "name", "clusterId", "regionId").
State(NodeStateEnabled).
Where("clusterId IN (SELECT id FROM " + SharedNodeClusterDAO.Table + " WHERE state=1)").
Asc("IF(regionId=0, 0, 1)"). // 按照 regionId 排序是为了让没有设置区域的节点排在最上面
DescPk().
Offset(offset).
Limit(size).
Slice(&result)
if regionId > 0 {
query.Attr("regionId", regionId)
}
_, err = query.FindAll()
return
}
// UpdateNodeRegionId 修改节点所在区域
func (this *NodeDAO) UpdateNodeRegionId(tx *dbs.Tx, nodeId int64, regionId int64) error {
// 这里允许 regionId 为 0
err := this.Query(tx).
Pk(nodeId).
Set("regionId", regionId).
UpdateQuickly()
if err != nil {
return err
}
return this.NotifyUpdate(tx, nodeId)
}
// FindAllEnabledNodesDNSWithClusterId 获取一个集群的节点DNS信息
func (this *NodeDAO) FindAllEnabledNodesDNSWithClusterId(tx *dbs.Tx, clusterId int64, includeSecondaryNodes bool, includingLnNodes bool) (result []*Node, err error) {
if clusterId <= 0 {
@@ -1975,7 +2022,7 @@ func (this *NodeDAO) UpdateNodeDDoSProtection(tx *dbs.Tx, nodeId int64, ddosProt
return err
}
if clusterId > 0 {
return SharedNodeTaskDAO.CreateNodeTask(tx, nodeconfigs.NodeRoleNode, clusterId, nodeId, 0, NodeTaskTypeDDosProtectionChanged, 0)
return SharedNodeTaskDAO.CreateNodeTask(tx, nodeconfigs.NodeRoleNode, clusterId, nodeId, 0, 0, NodeTaskTypeDDosProtectionChanged, 0)
}
return nil
}
@@ -1987,7 +2034,7 @@ func (this *NodeDAO) NotifyUpdate(tx *dbs.Tx, nodeId int64) error {
if err != nil {
return err
}
err = SharedNodeTaskDAO.CreateNodeTask(tx, nodeconfigs.NodeRoleNode, clusterId, nodeId, 0, NodeTaskTypeConfigChanged, 0)
err = SharedNodeTaskDAO.CreateNodeTask(tx, nodeconfigs.NodeRoleNode, clusterId, nodeId, 0, 0, NodeTaskTypeConfigChanged, 0)
if err != nil {
return err
}
@@ -2003,7 +2050,7 @@ func (this *NodeDAO) NotifyLevelUpdate(tx *dbs.Tx, nodeId int64) error {
return err
}
for _, clusterId := range clusterIds {
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, NodeTaskTypeNodeLevelChanged)
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypeNodeLevelChanged)
if err != nil {
return err
}

View File

@@ -77,3 +77,34 @@ func TestNodeDAO_ComposeNodeConfig_ParentNodes(t *testing.T) {
}
logs.PrintAsJSON(nodeConfig.ParentNodes, t)
}
func TestNodeDAO_FindEnabledNodeIdWithUniqueId(t *testing.T) {
dbs.NotifyReady()
var tx *dbs.Tx
// init
{
_, err := models.SharedNodeDAO.FindEnabledNodeIdWithUniqueId(tx, "a186380dbd26ccd49e75d178ec59df1b")
if err != nil {
t.Fatal(err)
}
}
var before = time.Now()
nodeId, err := models.SharedNodeDAO.FindEnabledNodeIdWithUniqueId(tx, "a186380dbd26ccd49e75d178ec59df1b")
if err != nil {
t.Fatal(err)
}
t.Log("cost:", time.Since(before).Seconds()*1000, "ms")
t.Log("nodeId:", nodeId)
{
before = time.Now()
nodeId, err := models.SharedNodeDAO.FindEnabledNodeIdWithUniqueId(tx, "a186380dbd26ccd49e75d178ec59df1b")
if err != nil {
t.Fatal(err)
}
t.Log("cost:", time.Since(before).Seconds()*1000, "ms")
t.Log("nodeId:", nodeId)
}
}

View File

@@ -40,6 +40,7 @@ type Node struct {
MaxCacheMemoryCapacity dbs.JSON `field:"maxCacheMemoryCapacity"` // 内存缓存容量
CacheDiskDir string `field:"cacheDiskDir"` // 缓存目录
DnsResolver dbs.JSON `field:"dnsResolver"` // DNS解析器
EnableIPLists bool `field:"enableIPLists"` // 启用IP名单
}
type NodeOperator struct {
@@ -79,6 +80,7 @@ type NodeOperator struct {
MaxCacheMemoryCapacity any // 内存缓存容量
CacheDiskDir any // 缓存目录
DnsResolver any // DNS解析器
EnableIPLists any // 启用IP名单
}
func NewNodeOperator() *NodeOperator {

View File

@@ -1,134 +0,0 @@
package models
import (
"errors"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
)
const (
NodePriceItemStateEnabled = 1 // 已启用
NodePriceItemStateDisabled = 0 // 已禁用
NodePriceTypeTraffic = "traffic" // 价格类型之流量
)
type NodePriceItemDAO dbs.DAO
func NewNodePriceItemDAO() *NodePriceItemDAO {
return dbs.NewDAO(&NodePriceItemDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeNodePriceItems",
Model: new(NodePriceItem),
PkName: "id",
},
}).(*NodePriceItemDAO)
}
var SharedNodePriceItemDAO *NodePriceItemDAO
func init() {
dbs.OnReady(func() {
SharedNodePriceItemDAO = NewNodePriceItemDAO()
})
}
// EnableNodePriceItem 启用条目
func (this *NodePriceItemDAO) EnableNodePriceItem(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
Set("state", NodePriceItemStateEnabled).
Update()
return err
}
// DisableNodePriceItem 禁用条目
func (this *NodePriceItemDAO) DisableNodePriceItem(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
Set("state", NodePriceItemStateDisabled).
Update()
return err
}
// FindEnabledNodePriceItem 查找启用中的条目
func (this *NodePriceItemDAO) FindEnabledNodePriceItem(tx *dbs.Tx, id int64) (*NodePriceItem, error) {
result, err := this.Query(tx).
Pk(id).
Attr("state", NodePriceItemStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*NodePriceItem), err
}
// FindNodePriceItemName 根据主键查找名称
func (this *NodePriceItemDAO) FindNodePriceItemName(tx *dbs.Tx, id int64) (string, error) {
return this.Query(tx).
Pk(id).
Result("name").
FindStringCol("")
}
// CreateItem 创建价格
func (this *NodePriceItemDAO) CreateItem(tx *dbs.Tx, name string, itemType string, bitsFrom, bitsTo int64) (int64, error) {
var op = NewNodePriceItemOperator()
op.Name = name
op.Type = itemType
op.BitsFrom = bitsFrom
op.BitsTo = bitsTo
op.IsOn = true
op.State = NodePriceItemStateEnabled
return this.SaveInt64(tx, op)
}
// UpdateItem 修改价格
func (this *NodePriceItemDAO) UpdateItem(tx *dbs.Tx, itemId int64, name string, bitsFrom, bitsTo int64) error {
if itemId <= 0 {
return errors.New("invalid itemId")
}
var op = NewNodePriceItemOperator()
op.Id = itemId
op.Name = name
op.BitsFrom = bitsFrom
op.BitsTo = bitsTo
return this.Save(tx, op)
}
// FindAllEnabledRegionPrices 列出某个区域的所有价格
func (this *NodePriceItemDAO) FindAllEnabledRegionPrices(tx *dbs.Tx, priceType string) (result []*NodePriceItem, err error) {
_, err = this.Query(tx).
Attr("type", priceType).
State(NodePriceItemStateEnabled).
Asc("bitsFrom").
Slice(&result).
FindAll()
return
}
// FindAllEnabledAndOnRegionPrices 列出某个区域的所有启用的价格
func (this *NodePriceItemDAO) FindAllEnabledAndOnRegionPrices(tx *dbs.Tx, priceType string) (result []*NodePriceItem, err error) {
_, err = this.Query(tx).
Attr("type", priceType).
State(NodePriceItemStateEnabled).
Attr("isOn", true).
Asc("bitsFrom").
Slice(&result).
FindAll()
return
}
// SearchItemsWithBytes 根据字节查找付费项目
func (this *NodePriceItemDAO) SearchItemsWithBytes(items []*NodePriceItem, bytes int64) int64 {
bytes *= 8
for _, item := range items {
if bytes >= int64(item.BitsFrom) && (bytes < int64(item.BitsTo) || item.BitsTo == 0) {
return int64(item.Id)
}
}
return 0
}

View File

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

View File

@@ -120,8 +120,8 @@ func (this *NodeRegionDAO) FindAllEnabledRegionPrices(tx *dbs.Tx) (result []*Nod
return
}
// FindAllEnabledAndOnRegions 列出所有启用的区域
func (this *NodeRegionDAO) FindAllEnabledAndOnRegions(tx *dbs.Tx) (result []*NodeRegion, err error) {
// FindAllAvailableRegions 列出所有启用的区域
func (this *NodeRegionDAO) FindAllAvailableRegions(tx *dbs.Tx) (result []*NodeRegion, err error) {
_, err = this.Query(tx).
State(NodeRegionStateEnabled).
Attr("isOn", true).
@@ -132,6 +132,25 @@ func (this *NodeRegionDAO) FindAllEnabledAndOnRegions(tx *dbs.Tx) (result []*Nod
return
}
// FindAllRegionIds 查找所有区域ID
func (this *NodeRegionDAO) FindAllRegionIds(tx *dbs.Tx) ([]int64, error) {
ones, err := this.Query(tx).
ResultPk().
State(NodeRegionStateEnabled).
Desc("order").
AscPk().
FindAll()
if err != nil {
return nil, err
}
var regionIds = []int64{}
for _, one := range ones {
regionIds = append(regionIds, int64(one.(*NodeRegion).Id))
}
return regionIds, nil
}
// UpdateRegionOrders 排序
func (this *NodeRegionDAO) UpdateRegionOrders(tx *dbs.Tx, regionIds []int64) error {
order := len(regionIds)

View File

@@ -11,20 +11,20 @@ type NodeRegion struct {
Description string `field:"description"` // 描述
Order uint32 `field:"order"` // 排序
CreatedAt uint64 `field:"createdAt"` // 创建时间
Prices dbs.JSON `field:"prices"` // 价格
Prices dbs.JSON `field:"prices"` // 流量价格
State uint8 `field:"state"` // 状态
}
type NodeRegionOperator struct {
Id interface{} // ID
AdminId interface{} // 管理员ID
IsOn interface{} // 是否启用
Name interface{} // 名称
Description interface{} // 描述
Order interface{} // 排序
CreatedAt interface{} // 创建时间
Prices interface{} // 价格
State interface{} // 状态
Id any // ID
AdminId any // 管理员ID
IsOn any // 是否启用
Name any // 名称
Description any // 描述
Order any // 排序
CreatedAt any // 创建时间
Prices any // 流量价格
State any // 状态
}
func NewNodeRegionOperator() *NodeRegionOperator {

View File

@@ -14,13 +14,16 @@ import (
type NodeTaskType = string
const (
// CDN相关
NodeTaskTypeConfigChanged NodeTaskType = "configChanged" // 节点整体配置变化
NodeTaskTypeDDosProtectionChanged NodeTaskType = "ddosProtectionChanged" // 节点DDoS配置变更
NodeTaskTypeGlobalServerConfigChanged NodeTaskType = "globalServerConfigChanged" // 全局服务设置变化
NodeTaskTypeIPItemChanged NodeTaskType = "ipItemChanged"
NodeTaskTypeNodeVersionChanged NodeTaskType = "nodeVersionChanged"
NodeTaskTypeScriptsChanged NodeTaskType = "scriptsChanged"
NodeTaskTypeNodeLevelChanged NodeTaskType = "nodeLevelChanged"
NodeTaskTypeIPItemChanged NodeTaskType = "ipItemChanged" // IP条目变更
NodeTaskTypeNodeVersionChanged NodeTaskType = "nodeVersionChanged" // 节点版本变化
NodeTaskTypeScriptsChanged NodeTaskType = "scriptsChanged" // 脚本配置变化
NodeTaskTypeNodeLevelChanged NodeTaskType = "nodeLevelChanged" // 节点级别变化
NodeTaskTypeUserServersStateChanged NodeTaskType = "userServersStateChanged" // 用户服务状态变化
// NS相关
@@ -54,17 +57,25 @@ func init() {
}
// CreateNodeTask 创建单个节点任务
func (this *NodeTaskDAO) CreateNodeTask(tx *dbs.Tx, role string, clusterId int64, nodeId int64, serverId int64, taskType NodeTaskType, version int64) error {
func (this *NodeTaskDAO) CreateNodeTask(tx *dbs.Tx, role string, clusterId int64, nodeId int64, userId int64, serverId int64, taskType NodeTaskType, version int64) error {
if clusterId <= 0 || nodeId <= 0 {
return nil
}
var uniqueId = role + "@" + types.String(nodeId) + "@node@" + types.String(serverId) + "@" + taskType
// 用户信息
// 没有直接加入到 uniqueId 中,是为了兼容以前的字段值
if userId > 0 {
uniqueId += "@" + types.String(userId)
}
var updatedAt = time.Now().Unix()
_, _, err := this.Query(tx).
InsertOrUpdate(maps.Map{
"role": role,
"clusterId": clusterId,
"nodeId": nodeId,
"userId": userId,
"serverId": serverId,
"type": taskType,
"uniqueId": uniqueId,
@@ -87,17 +98,25 @@ func (this *NodeTaskDAO) CreateNodeTask(tx *dbs.Tx, role string, clusterId int64
}
// CreateClusterTask 创建集群任务
func (this *NodeTaskDAO) CreateClusterTask(tx *dbs.Tx, role string, clusterId int64, serverId int64, taskType NodeTaskType) error {
func (this *NodeTaskDAO) CreateClusterTask(tx *dbs.Tx, role string, clusterId int64, userId int64, serverId int64, taskType NodeTaskType) error {
if clusterId <= 0 {
return nil
}
var uniqueId = role + "@" + types.String(clusterId) + "@" + types.String(serverId) + "@cluster@" + types.String(serverId) + "@" + taskType
// 用户信息
// 没有直接加入到 uniqueId 中,是为了兼容以前的字段值
if userId > 0 {
uniqueId += "@" + types.String(userId)
}
var updatedAt = time.Now().Unix()
_, _, err := this.Query(tx).
InsertOrUpdate(maps.Map{
"role": role,
"clusterId": clusterId,
"userId": userId,
"serverId": serverId,
"nodeId": 0,
"type": taskType,
@@ -121,7 +140,7 @@ func (this *NodeTaskDAO) CreateClusterTask(tx *dbs.Tx, role string, clusterId in
}
// ExtractNodeClusterTask 分解边缘节点集群任务
func (this *NodeTaskDAO) ExtractNodeClusterTask(tx *dbs.Tx, clusterId int64, serverId int64, taskType NodeTaskType) error {
func (this *NodeTaskDAO) ExtractNodeClusterTask(tx *dbs.Tx, clusterId int64, userId int64, serverId int64, taskType NodeTaskType) error {
nodeIds, err := SharedNodeDAO.FindAllNodeIdsMatch(tx, clusterId, true, configutils.BoolStateYes)
if err != nil {
return err
@@ -140,7 +159,7 @@ func (this *NodeTaskDAO) ExtractNodeClusterTask(tx *dbs.Tx, clusterId int64, ser
var version = time.Now().UnixNano()
for _, nodeId := range nodeIds {
err = this.CreateNodeTask(tx, nodeconfigs.NodeRoleNode, clusterId, nodeId, serverId, taskType, version)
err = this.CreateNodeTask(tx, nodeconfigs.NodeRoleNode, clusterId, nodeId, userId, serverId, taskType, version)
if err != nil {
return err
}
@@ -169,11 +188,11 @@ func (this *NodeTaskDAO) ExtractAllClusterTasks(tx *dbs.Tx, role string) error {
return err
}
for _, one := range ones {
clusterId := int64(one.(*NodeTask).ClusterId)
var clusterId = int64(one.(*NodeTask).ClusterId)
switch role {
case nodeconfigs.NodeRoleNode:
var nodeTask = one.(*NodeTask)
err = this.ExtractNodeClusterTask(tx, clusterId, int64(nodeTask.ServerId), nodeTask.Type)
err = this.ExtractNodeClusterTask(tx, clusterId, int64(nodeTask.UserId), int64(nodeTask.ServerId), nodeTask.Type)
if err != nil {
return err
}

View File

@@ -11,7 +11,7 @@ func TestNodeTaskDAO_CreateNodeTask(t *testing.T) {
dbs.NotifyReady()
var tx *dbs.Tx
err := SharedNodeTaskDAO.CreateNodeTask(tx, nodeconfigs.NodeRoleNode, 1, 2, 0, NodeTaskTypeConfigChanged, 0)
err := SharedNodeTaskDAO.CreateNodeTask(tx, nodeconfigs.NodeRoleNode, 1, 2, 0, 0, NodeTaskTypeConfigChanged, 0)
if err != nil {
t.Fatal(err)
}
@@ -22,7 +22,7 @@ func TestNodeTaskDAO_CreateClusterTask(t *testing.T) {
dbs.NotifyReady()
var tx *dbs.Tx
err := SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, 1, 0, NodeTaskTypeConfigChanged)
err := SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, 1, 0, 0, NodeTaskTypeConfigChanged)
if err != nil {
t.Fatal(err)
}
@@ -33,7 +33,7 @@ func TestNodeTaskDAO_ExtractClusterTask(t *testing.T) {
dbs.NotifyReady()
var tx *dbs.Tx
err := SharedNodeTaskDAO.ExtractNodeClusterTask(tx, 1, 0, NodeTaskTypeConfigChanged)
err := SharedNodeTaskDAO.ExtractNodeClusterTask(tx, 1, 0, 0, NodeTaskTypeConfigChanged)
if err != nil {
t.Fatal(err)
}

View File

@@ -6,7 +6,8 @@ type NodeTask struct {
Role string `field:"role"` // 节点角色
NodeId uint32 `field:"nodeId"` // 节点ID
ClusterId uint32 `field:"clusterId"` // 集群ID
ServerId uint32 `field:"serverId"` // 服务ID
ServerId uint64 `field:"serverId"` // 服务ID
UserId uint64 `field:"userId"` // 用户ID
Type string `field:"type"` // 任务类型
UniqueId string `field:"uniqueId"` // 唯一IDnodeId@type
UpdatedAt uint64 `field:"updatedAt"` // 修改时间
@@ -18,19 +19,20 @@ type NodeTask struct {
}
type NodeTaskOperator struct {
Id interface{} // ID
Role interface{} // 节点角色
NodeId interface{} // 节点ID
ClusterId interface{} // 集群ID
ServerId interface{} // 服务ID
Type interface{} // 任务类型
UniqueId interface{} // 唯一IDnodeId@type
UpdatedAt interface{} // 修改时间
IsDone interface{} // 是否已完成
IsOk interface{} // 是否已完成
Error interface{} // 错误信息
IsNotified interface{} // 是否已通知更新
Version interface{} // 版本
Id any // ID
Role any // 节点角色
NodeId any // 节点ID
ClusterId any // 集群ID
ServerId any // 服务ID
UserId any // 用户ID
Type any // 任务类型
UniqueId any // 唯一IDnodeId@type
UpdatedAt any // 修改时间
IsDone any // 是否已完成
IsOk any // 是否已完成
Error any // 错误信息
IsNotified any // 是否已通知更新
Version any // 版本
}
func NewNodeTaskOperator() *NodeTaskOperator {

View File

@@ -149,5 +149,5 @@ func (this *NSClusterDAO) CountAllClustersWithSSLPolicyIds(tx *dbs.Tx, sslPolicy
// NotifyUpdate 通知更改
func (this *NSClusterDAO) NotifyUpdate(tx *dbs.Tx, clusterId int64) error {
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleDNS, clusterId, 0, NSNodeTaskTypeConfigChanged)
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleDNS, clusterId, 0, 0, NSNodeTaskTypeConfigChanged)
}

View File

@@ -1,8 +1,9 @@
//go:build !plus
package models
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
_ "github.com/go-sql-driver/mysql"
@@ -79,129 +80,6 @@ func (this *PlanDAO) FindPlanName(tx *dbs.Tx, id int64) (string, error) {
FindStringCol("")
}
// CreatePlan 创建套餐
func (this *PlanDAO) CreatePlan(tx *dbs.Tx,
name string,
clusterId int64,
trafficLimitJSON []byte,
featuresJSON []byte,
priceType serverconfigs.PlanPriceType,
trafficPriceJSON []byte,
bandwidthPriceJSON []byte,
monthlyPrice float32,
seasonallyPrice float32,
yearlyPrice float32) (int64, error) {
var op = NewPlanOperator()
op.Name = name
op.ClusterId = clusterId
if len(trafficLimitJSON) > 0 {
op.TrafficLimit = trafficLimitJSON
}
if len(featuresJSON) > 0 {
op.Features = featuresJSON
}
op.PriceType = priceType
if len(trafficPriceJSON) > 0 {
op.TrafficPrice = trafficPriceJSON
}
if len(bandwidthPriceJSON) > 0 {
op.BandwidthPrice = bandwidthPriceJSON
}
if monthlyPrice >= 0 {
op.MonthlyPrice = monthlyPrice
}
if seasonallyPrice >= 0 {
op.SeasonallyPrice = seasonallyPrice
}
if yearlyPrice >= 0 {
op.YearlyPrice = yearlyPrice
}
op.IsOn = true
op.State = PlanStateEnabled
return this.SaveInt64(tx, op)
}
// UpdatePlan 修改套餐
func (this *PlanDAO) UpdatePlan(tx *dbs.Tx,
planId int64,
name string,
isOn bool,
clusterId int64,
trafficLimitJSON []byte,
featuresJSON []byte,
priceType serverconfigs.PlanPriceType,
trafficPriceJSON []byte,
bandwidthPriceJSON []byte,
monthlyPrice float32,
seasonallyPrice float32,
yearlyPrice float32) error {
if planId <= 0 {
return errors.New("invalid planId")
}
// 检查集群有无变化
oldClusterId, err := this.Query(tx).
Pk(planId).
Result("clusterId").
FindInt64Col(0)
if err != nil {
return err
}
var op = NewPlanOperator()
op.Id = planId
op.Name = name
op.IsOn = isOn
op.ClusterId = clusterId
if len(trafficLimitJSON) > 0 {
op.TrafficLimit = trafficLimitJSON
}
if len(featuresJSON) > 0 {
op.Features = featuresJSON
}
op.PriceType = priceType
if len(trafficPriceJSON) > 0 {
op.TrafficPrice = trafficPriceJSON
}
if len(bandwidthPriceJSON) > 0 {
op.BandwidthPrice = bandwidthPriceJSON
}
if monthlyPrice >= 0 {
op.MonthlyPrice = monthlyPrice
} else {
op.MonthlyPrice = 0
}
if seasonallyPrice >= 0 {
op.SeasonallyPrice = seasonallyPrice
} else {
op.SeasonallyPrice = 0
}
if yearlyPrice >= 0 {
op.YearlyPrice = yearlyPrice
} else {
op.YearlyPrice = 0
}
err = this.Save(tx, op)
if err != nil {
return err
}
if oldClusterId != clusterId {
// 修改服务所属集群
err = SharedServerDAO.UpdateServersClusterIdWithPlanId(tx, planId, clusterId)
if err != nil {
return err
}
err = SharedNodeClusterDAO.NotifyUpdate(tx, oldClusterId)
if err != nil {
return err
}
}
return this.NotifyUpdate(tx, planId)
}
// CountAllEnabledPlans 计算套餐的数量
func (this *PlanDAO) CountAllEnabledPlans(tx *dbs.Tx) (int64, error) {
return this.Query(tx).

View File

@@ -1,38 +1 @@
package models
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
)
// DecodeTrafficPrice 流量价格配置
func (this *Plan) DecodeTrafficPrice() *serverconfigs.PlanTrafficPriceConfig {
var config = &serverconfigs.PlanTrafficPriceConfig{}
if len(this.TrafficPrice) == 0 {
return config
}
err := json.Unmarshal(this.TrafficPrice, config)
if err != nil {
// 忽略错误
}
return config
}
// DecodeBandwidthPrice 带宽价格配置
func (this *Plan) DecodeBandwidthPrice() *serverconfigs.PlanBandwidthPriceConfig {
var config = &serverconfigs.PlanBandwidthPriceConfig{}
if len(this.BandwidthPrice) == 0 {
return config
}
err := json.Unmarshal(this.BandwidthPrice, config)
if err != nil {
// 忽略错误
}
return config
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeAPI/internal/utils/regexputils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
@@ -117,9 +118,10 @@ func (this *ServerBandwidthStatDAO) FindHourlyBandwidthStats(tx *dbs.Tx, serverI
var timePieces = strings.Split(fullTime, ".")
var day = timePieces[0]
var hour = timePieces[1]
var bytes = one.GetInt64("bytes")
m[day+hour] = &pb.FindHourlyServerBandwidthStatsResponse_Stat{
Bytes: one.GetInt64("bytes"),
Bytes: bytes,
Bits: bytes * 8,
Day: day,
Hour: types.Int32(hour),
}
@@ -136,6 +138,7 @@ func (this *ServerBandwidthStatDAO) FindHourlyBandwidthStats(tx *dbs.Tx, serverI
} else {
result = append(result, &pb.FindHourlyServerBandwidthStatsResponse_Stat{
Bytes: 0,
Bits: 0,
Day: fullHour[:8],
Hour: types.Int32(fullHour[8:]),
})
@@ -178,9 +181,11 @@ func (this *ServerBandwidthStatDAO) FindDailyBandwidthStats(tx *dbs.Tx, serverId
var m = map[string]*pb.FindDailyServerBandwidthStatsResponse_Stat{}
for _, one := range ones {
var day = one.GetString("day")
var bytes = one.GetInt64("bytes")
m[day] = &pb.FindDailyServerBandwidthStatsResponse_Stat{
Bytes: one.GetInt64("bytes"),
Bytes: bytes,
Bits: bytes * 8,
Day: day,
}
}
@@ -196,6 +201,7 @@ func (this *ServerBandwidthStatDAO) FindDailyBandwidthStats(tx *dbs.Tx, serverId
} else {
result = append(result, &pb.FindDailyServerBandwidthStatsResponse_Stat{
Bytes: 0,
Bits: 0,
Day: day,
})
}
@@ -204,6 +210,85 @@ func (this *ServerBandwidthStatDAO) FindDailyBandwidthStats(tx *dbs.Tx, serverId
return result, nil
}
// FindBandwidthStatsBetweenDays 查找日期段内的带宽峰值
// dayFrom YYYYMMDD
// dayTo YYYYMMDD
func (this *ServerBandwidthStatDAO) FindBandwidthStatsBetweenDays(tx *dbs.Tx, serverId int64, dayFrom string, dayTo string) (result []*pb.FindDailyServerBandwidthStatsBetweenDaysResponse_Stat, err error) {
if serverId <= 0 {
return nil, nil
}
if !regexputils.YYYYMMDD.MatchString(dayFrom) {
return nil, errors.New("invalid dayFrom '" + dayFrom + "'")
}
if !regexputils.YYYYMMDD.MatchString(dayTo) {
return nil, errors.New("invalid dayTo '" + dayTo + "'")
}
if dayFrom > dayTo {
dayFrom, dayTo = dayTo, dayFrom
}
ones, _, err := this.Query(tx).
Table(this.partialTable(serverId)).
Result("bytes", "day", "timeAt").
Attr("serverId", serverId).
Between("day", dayFrom, dayTo).
FindOnes()
if err != nil {
return nil, err
}
var m = map[string]*pb.FindDailyServerBandwidthStatsBetweenDaysResponse_Stat{}
for _, one := range ones {
var day = one.GetString("day")
var bytes = one.GetInt64("bytes")
var timeAt = one.GetString("timeAt")
var key = day + "@" + timeAt
m[key] = &pb.FindDailyServerBandwidthStatsBetweenDaysResponse_Stat{
Bytes: bytes,
Bits: bytes * 8,
Day: day,
TimeAt: timeAt,
}
}
allDays, err := utils.RangeDays(dayFrom, dayTo)
if err != nil {
return nil, err
}
dayTimes, err := utils.Range24HourTimes(5)
if err != nil {
return nil, err
}
// 截止到当前时间
var currentTime = timeutil.Format("Ymd@Hi")
for _, day := range allDays {
for _, timeAt := range dayTimes {
var key = day + "@" + timeAt
if key >= currentTime {
break
}
stat, ok := m[key]
if ok {
result = append(result, stat)
} else {
result = append(result, &pb.FindDailyServerBandwidthStatsBetweenDaysResponse_Stat{
Day: day,
TimeAt: timeAt,
})
}
}
}
return result, nil
}
// FindMonthlyPeekBandwidthBytes 获取某月的带宽峰值
// month YYYYMM
func (this *ServerBandwidthStatDAO) FindMonthlyPeekBandwidthBytes(tx *dbs.Tx, serverId int64, month string) (int64, error) {
@@ -308,6 +393,65 @@ func (this *ServerBandwidthStatDAO) FindMonthlyPercentile(tx *dbs.Tx, serverId i
return
}
// FindPercentileBetweenDays 获取日期段内内百分位
func (this *ServerBandwidthStatDAO) FindPercentileBetweenDays(tx *dbs.Tx, serverId int64, dayFrom string, dayTo string, percentile int32) (result *ServerBandwidthStat, err error) {
if dayFrom > dayTo {
dayFrom, dayTo = dayTo, dayFrom
}
if percentile <= 0 {
percentile = 95
}
// 如果是100%以上,则快速返回
if percentile >= 100 {
one, err := this.Query(tx).
Table(this.partialTable(serverId)).
Attr("serverId", serverId).
Between("day", dayFrom, dayTo).
Desc("bytes").
Find()
if err != nil || one == nil {
return nil, err
}
return one.(*ServerBandwidthStat), nil
}
// 总数量
total, err := this.Query(tx).
Table(this.partialTable(serverId)).
Attr("serverId", serverId).
Between("day", dayFrom, dayTo).
Count()
if err != nil {
return nil, err
}
if total == 0 {
return nil, nil
}
var offset int64
if total > 1 {
offset = int64(math.Ceil(float64(total) * float64(100-percentile) / 100))
}
// 查询 nth 位置
one, err := this.Query(tx).
Table(this.partialTable(serverId)).
Attr("serverId", serverId).
Between("day", dayFrom, dayTo).
Desc("bytes").
Offset(offset).
Find()
if err != nil || one == nil {
return nil, err
}
return one.(*ServerBandwidthStat), nil
}
// Clean 清理过期数据
func (this *ServerBandwidthStatDAO) Clean(tx *dbs.Tx) error {
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -100)) // 保留大约3个月的数据

View File

@@ -89,7 +89,6 @@ func TestServerBandwidthStatDAO_FindHourlyBandwidthStats(t *testing.T) {
logs.PrintAsJSON(stats, t)
}
func TestServerBandwidthStatDAO_FindDailyBandwidthStats(t *testing.T) {
var dao = models.NewServerBandwidthStatDAO()
var tx *dbs.Tx
@@ -98,4 +97,16 @@ func TestServerBandwidthStatDAO_FindDailyBandwidthStats(t *testing.T) {
t.Fatal(err)
}
logs.PrintAsJSON(stats, t)
}
}
func TestServerBandwidthStatDAO_FindBandwidthStatsBetweenDays(t *testing.T) {
var dao = models.NewServerBandwidthStatDAO()
var tx *dbs.Tx
stats, err := dao.FindBandwidthStatsBetweenDays(tx, 23, timeutil.Format("Ymd", time.Now().AddDate(0, 0, -2)), timeutil.Format("Ymd"))
if err != nil {
t.Fatal(err)
}
for _, stat := range stats {
t.Log(stat.Day, stat.TimeAt, "bytes:", stat.Bytes, "bits:", stat.Bits)
}
}

View File

@@ -5,18 +5,20 @@ type ServerBandwidthStat struct {
Id uint64 `field:"id"` // ID
UserId uint64 `field:"userId"` // 用户ID
ServerId uint64 `field:"serverId"` // 服务ID
RegionId uint32 `field:"regionId"` // 区域ID
Day string `field:"day"` // 日期YYYYMMDD
TimeAt string `field:"timeAt"` // 时间点HHMM
Bytes uint64 `field:"bytes"` // 带宽字节
}
type ServerBandwidthStatOperator struct {
Id interface{} // ID
UserId interface{} // 用户ID
ServerId interface{} // 服务ID
Day interface{} // 日期YYYYMMDD
TimeAt interface{} // 时间点HHMM
Bytes interface{} // 带宽字节
Id any // ID
UserId any // 用户ID
ServerId any // 服务ID
RegionId any // 区域ID
Day any // 日期YYYYMMDD
TimeAt any // 时间点HHMM
Bytes any // 带宽字节
}
func NewServerBandwidthStatOperator() *ServerBandwidthStatOperator {

View File

@@ -1,9 +1,11 @@
package models
import (
"fmt"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeAPI/internal/utils/regexputils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
@@ -11,6 +13,7 @@ import (
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"regexp"
"strings"
@@ -83,7 +86,7 @@ func (this *ServerDailyStatDAO) SaveStats(tx *dbs.Tx, stats []*pb.ServerDailySta
InsertOrUpdate(maps.Map{
"userId": serverUserId,
"serverId": stat.ServerId,
"regionId": stat.RegionId,
"regionId": stat.NodeRegionId,
"bytes": stat.Bytes,
"cachedBytes": stat.CachedBytes,
"countRequests": stat.CountRequests,
@@ -193,7 +196,7 @@ func (this *ServerDailyStatDAO) SumUserMonthlyPeek(tx *dbs.Tx, userId int64, reg
// SumUserDaily 获取某天流量总和
// day 格式为YYYYMMDD
func (this *ServerDailyStatDAO) SumUserDaily(tx *dbs.Tx, userId int64, regionId int64, day string) (int64, error) {
query := this.Query(tx)
var query = this.Query(tx)
if regionId > 0 {
query.Attr("regionId", regionId)
}
@@ -203,6 +206,27 @@ func (this *ServerDailyStatDAO) SumUserDaily(tx *dbs.Tx, userId int64, regionId
SumInt64("bytes", 0)
}
// SumUserTrafficBytesBetweenDays 获取用户某个日期段内的流量总和
func (this *ServerDailyStatDAO) SumUserTrafficBytesBetweenDays(tx *dbs.Tx, userId int64, regionId int64, dayFrom string, dayTo string) (int64, error) {
if !regexputils.YYYYMMDD.MatchString(dayFrom) {
return 0, errors.New("invalid 'dayFrom':" + dayFrom)
}
if !regexputils.YYYYMMDD.MatchString(dayTo) {
return 0, errors.New("invalid 'dayTo':" + dayTo)
}
var query = this.Query(tx)
if regionId > 0 {
query.Attr("regionId", regionId)
} else if regionId < 0 { // 表示没有分配区域的流量
query.Attr("regionId", 0)
}
return query.
Attr("userId", userId).
Between("day", dayFrom, dayTo).
SumInt64("bytes", 0)
}
// SumUserMonthly 获取某月流量总和
// month 格式为YYYYMM
func (this *ServerDailyStatDAO) SumUserMonthly(tx *dbs.Tx, userId int64, month string) (int64, error) {
@@ -295,19 +319,48 @@ func (this *ServerDailyStatDAO) SumHourlyStat(tx *dbs.Tx, serverId int64, hour s
}
// SumDailyStat 获取某天内的流量
// day 格式为YYYYMMDD
func (this *ServerDailyStatDAO) SumDailyStat(tx *dbs.Tx, serverId int64, day string) (stat *pb.ServerDailyStat, err error) {
// dayFrom 格式为YYYYMMDD
// dayTo 格式为YYYYMMDD
func (this *ServerDailyStatDAO) SumDailyStat(tx *dbs.Tx, userId int64, serverId int64, regionId int64, dayFrom string, dayTo string) (stat *pb.ServerDailyStat, err error) {
stat = &pb.ServerDailyStat{}
if !regexp.MustCompile(`^\d{8}$`).MatchString(day) {
return nil, errors.New("invalid day '" + day + "'")
if userId <= 0 && serverId <= 0 {
return
}
one, _, err := this.Query(tx).
Result("SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Attr("serverId", serverId).
Attr("day", day).
FindOne()
if !regexputils.YYYYMMDD.MatchString(dayFrom) {
return nil, errors.New("invalid dayFrom '" + dayFrom + "'")
}
if !regexputils.YYYYMMDD.MatchString(dayTo) {
return nil, errors.New("invalid dayTo '" + dayTo + "'")
}
if dayFrom > dayTo {
dayFrom, dayTo = dayTo, dayFrom
}
var query = this.Query(tx).
Result("SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes")
if userId > 0 {
query.Attr("userId", userId)
}
if serverId > 0 {
query.Attr("serverId", serverId)
}
if regionId > 0 {
query.Attr("regionId", regionId)
}
if dayFrom == dayTo {
query.Attr("day", dayFrom)
} else {
query.Between("day", dayFrom, dayTo)
}
one, _, err := query.FindOne()
if err != nil {
return nil, err
}
@@ -332,7 +385,7 @@ func (this *ServerDailyStatDAO) SumDailyStat(tx *dbs.Tx, serverId int64, day str
func (this *ServerDailyStatDAO) SumDailyStatBeforeMinute(tx *dbs.Tx, serverId int64, day string, minute string) (stat *pb.ServerDailyStat, err error) {
stat = &pb.ServerDailyStat{}
if !regexp.MustCompile(`^\d{8}$`).MatchString(day) {
if !regexputils.YYYYMMDD.MatchString(day) {
return nil, errors.New("invalid day '" + day + "'")
}
@@ -364,7 +417,7 @@ func (this *ServerDailyStatDAO) SumDailyStatBeforeMinute(tx *dbs.Tx, serverId in
func (this *ServerDailyStatDAO) SumMonthlyStat(tx *dbs.Tx, serverId int64, month string) (stat *pb.ServerDailyStat, err error) {
stat = &pb.ServerDailyStat{}
if !regexp.MustCompile(`^\d{6}$`).MatchString(month) {
if !regexputils.YYYYMM.MatchString(month) {
return
}
@@ -393,7 +446,7 @@ func (this *ServerDailyStatDAO) SumMonthlyStat(tx *dbs.Tx, serverId int64, month
// SumMonthlyBytes 获取某月内的流量
// month 格式为YYYYMM
func (this *ServerDailyStatDAO) SumMonthlyBytes(tx *dbs.Tx, serverId int64, month string) (result int64, err error) {
if !regexp.MustCompile(`^\d{6}$`).MatchString(month) {
if !regexputils.YYYYMM.MatchString(month) {
return
}
@@ -439,15 +492,18 @@ func (this *ServerDailyStatDAO) FindDailyStats(tx *dbs.Tx, serverId int64, dayFr
// FindStatsWithDay 按天查找5分钟级统计
// day YYYYMMDD
// timeFrom HHII00
// timeTo HHII59
func (this *ServerDailyStatDAO) FindStatsWithDay(tx *dbs.Tx, serverId int64, day string, timeFrom string, timeTo string) (result []*ServerDailyStat, err error) {
if !regexp.MustCompile(`^\d{8}$`).MatchString(day) {
if !regexputils.YYYYMMDD.MatchString(day) {
return
}
var query = this.Query(tx).
Result("SUM(bytes) AS bytes", "SUM(cachedBytes) AS cachedBytes", "SUM(countRequests) AS countRequests", "SUM(countCachedRequests) AS countCachedRequests", "SUM(countAttackRequests) AS countAttackRequests", "SUM(attackBytes) AS attackBytes", "day", "timeFrom", "MIN(timeTo) AS timeTo").
Attr("serverId", serverId).
Attr("day", day).
DescPk()
Group("day").Group("timeFrom", dbs.QueryOrderDesc)
if len(timeFrom) > 0 {
query.Gte("timeFrom", timeFrom)
@@ -459,6 +515,111 @@ func (this *ServerDailyStatDAO) FindStatsWithDay(tx *dbs.Tx, serverId int64, day
_, err = query.
Slice(&result).
FindAll()
if err != nil {
return nil, err
}
return
}
// FindStatsBetweenDays 查找日期段内的5分钟统计
func (this *ServerDailyStatDAO) FindStatsBetweenDays(tx *dbs.Tx, userId int64, serverId int64, regionId int64, dayFrom string, dayTo string) (result []*ServerDailyStat, err error) {
if !regexputils.YYYYMMDD.MatchString(dayFrom) || !regexputils.YYYYMMDD.MatchString(dayTo) {
return
}
if userId <= 0 && serverId <= 0 {
return
}
if dayFrom > dayTo {
dayFrom, dayTo = dayTo, dayFrom
}
var query = this.Query(tx)
if userId > 0 {
query.Attr("userId", userId)
}
if serverId > 0 {
query.Attr("serverId", serverId)
} else {
query.Result("SUM(bytes) AS bytes", "SUM(cachedBytes) AS cachedBytes", "SUM(countRequests) AS countRequests", "SUM(countCachedRequests) AS countCachedRequests", "SUM(countAttackRequests) AS countAttackRequests", "SUM(attackBytes) AS attackBytes", "MIN(day) AS day", "MIN(timeFrom) AS timeFrom", "MIN(timeTo) AS timeTo")
query.Group("day").Group("timeFrom")
}
if regionId > 0 {
query.Attr("regionId", regionId)
} else if regionId < 0 { // 表示未分配区域的流量
query.Attr("regionId", 0)
}
// 不需要排序
query.Between("day", dayFrom, dayTo)
_, err = query.
Slice(&result).
FindAll()
if err != nil {
return
}
var m = map[string]*ServerDailyStat{} // day @ timeFrom => *ServerDailyStat
for _, stat := range result {
var key = stat.Day + "@" + stat.TimeFrom
mStat, ok := m[key]
if ok {
mStat.Bytes += stat.Bytes
mStat.CachedBytes += stat.CachedBytes
mStat.AttackBytes += stat.AttackBytes
mStat.CountRequests += stat.CountRequests
mStat.CountAttackRequests += stat.CountAttackRequests
mStat.CountCachedRequests += stat.CountCachedRequests
} else {
m[key] = stat
}
}
// 填充空白
rangeDays, err := utils.RangeDays(dayFrom, dayTo)
if err != nil {
return nil, err
}
dayTimes, err := utils.Range24HourTimes(5)
if err != nil {
return nil, err
}
// 截止到当前时间
var currentTime = timeutil.Format("Ymd@Hi00")
result = nil
for _, day := range rangeDays {
for _, timeAt /** HHII **/ := range dayTimes {
var key = day + "@" + timeAt + "00"
if key >= currentTime {
break
}
stat, ok := m[key]
if ok {
result = append(result, stat)
} else {
var hour = types.Int(timeAt[:2])
var minute = types.Int(timeAt[2:])
minute += 4
result = append(result, &ServerDailyStat{
Day: day,
TimeFrom: timeAt + "00",
TimeTo: fmt.Sprintf("%02d%02d59", hour, minute),
})
}
}
}
return
}
@@ -537,6 +698,23 @@ func (this *ServerDailyStatDAO) FindDistinctServerIds(tx *dbs.Tx, dayFrom string
return serverIds, nil
}
// FindDistinctUserIds 查找所有有流量的用户ID
func (this *ServerDailyStatDAO) FindDistinctUserIds(tx *dbs.Tx, dayFrom string, dayTo string) (userIds []int64, err error) {
dayFrom = strings.ReplaceAll(dayFrom, "-", "")
dayTo = strings.ReplaceAll(dayTo, "-", "")
ones, _, err := this.Query(tx).
Result("DISTINCT(userId) AS userId").
Between("day", dayFrom, dayTo).
FindOnes()
if err != nil {
return nil, err
}
for _, one := range ones {
userIds = append(userIds, one.GetInt64("userId"))
}
return userIds, nil
}
// UpdateStatFee 设置费用
func (this *ServerDailyStatDAO) UpdateStatFee(tx *dbs.Tx, statId int64, fee float32) error {
return this.Query(tx).

View File

@@ -7,16 +7,17 @@ import (
"github.com/iwind/TeaGo/logs"
timeutil "github.com/iwind/TeaGo/utils/time"
"testing"
"time"
)
func TestServerDailyStatDAO_SaveStats(t *testing.T) {
var tx *dbs.Tx
stats := []*pb.ServerDailyStat{
{
ServerId: 1,
RegionId: 2,
Bytes: 1,
CreatedAt: 1607671488,
ServerId: 1,
NodeRegionId: 2,
Bytes: 1,
CreatedAt: 1607671488,
},
}
err := NewServerDailyStatDAO().SaveStats(tx, stats)
@@ -30,10 +31,10 @@ func TestServerDailyStatDAO_SaveStats2(t *testing.T) {
var tx *dbs.Tx
stats := []*pb.ServerDailyStat{
{
ServerId: 1,
RegionId: 3,
Bytes: 1,
CreatedAt: 1607671488,
ServerId: 1,
NodeRegionId: 3,
Bytes: 1,
CreatedAt: 1607671488,
},
}
err := NewServerDailyStatDAO().SaveStats(tx, stats)
@@ -83,3 +84,26 @@ func TestServerDailyStatDAO_FindDistinctPlanServerIdsBetweenDay(t *testing.T) {
}
t.Log(serverIds)
}
func TestServerDailyStatDAO_FindStatsBetweenDays(t *testing.T) {
var tx *dbs.Tx
stats, err := NewServerDailyStatDAO().FindStatsBetweenDays(tx, 1, 0, 0, timeutil.Format("Ymd", time.Now().AddDate(0, 0, -1)), timeutil.Format("Ymd"))
if err != nil {
t.Fatal(err)
}
for _, stat := range stats {
t.Log(stat.Day, stat.TimeFrom, stat.TimeTo, stat.Bytes)
}
}
func TestServerDailyStatDAO_FindStatsWithDay(t *testing.T) {
var dao = NewServerDailyStatDAO()
var tx *dbs.Tx
stats, err := dao.FindStatsWithDay(tx, 23, timeutil.Format("Ymd"), "000000", "235900")
if err != nil {
t.Fatal(err)
}
for _, stat := range stats {
t.Log(stat.TimeFrom, stat.TimeTo, stat.Bytes)
}
}

View File

@@ -1009,12 +1009,12 @@ func (this *ServerDAO) ComposeServerConfigWithServerId(tx *dbs.Tx, serverId int6
if server == nil {
return nil, ErrNotFound
}
return this.ComposeServerConfig(tx, server, nil, forNode)
return this.ComposeServerConfig(tx, server, nil, forNode, false)
}
// ComposeServerConfig 构造服务的Config
// forNode 是否是节点请求
func (this *ServerDAO) ComposeServerConfig(tx *dbs.Tx, server *Server, cacheMap *utils.CacheMap, forNode bool) (*serverconfigs.ServerConfig, error) {
func (this *ServerDAO) ComposeServerConfig(tx *dbs.Tx, server *Server, cacheMap *utils.CacheMap, forNode bool, forList bool) (*serverconfigs.ServerConfig, error) {
if server == nil {
return nil, ErrNotFound
}
@@ -1039,7 +1039,7 @@ func (this *ServerDAO) ComposeServerConfig(tx *dbs.Tx, server *Server, cacheMap
var groupConfig *serverconfigs.ServerGroupConfig
for _, groupId := range server.DecodeGroupIds() {
groupConfig1, err := SharedServerGroupDAO.ComposeGroupConfig(tx, groupId, cacheMap)
groupConfig1, err := SharedServerGroupDAO.ComposeGroupConfig(tx, groupId, forList, cacheMap)
if err != nil {
return nil, err
}
@@ -1062,28 +1062,30 @@ func (this *ServerDAO) ComposeServerConfig(tx *dbs.Tx, server *Server, cacheMap
}
// CNAME
config.SupportCNAME = server.SupportCNAME == 1
if server.ClusterId > 0 && len(server.DnsName) > 0 {
clusterDNS, err := SharedNodeClusterDAO.FindClusterDNSInfo(tx, int64(server.ClusterId), cacheMap)
if err != nil {
return nil, err
}
if clusterDNS != nil && clusterDNS.DnsDomainId > 0 {
clusterDNSConfig, err := clusterDNS.DecodeDNSConfig()
if !forList {
config.SupportCNAME = server.SupportCNAME == 1
if server.ClusterId > 0 && len(server.DnsName) > 0 {
clusterDNS, err := SharedNodeClusterDAO.FindClusterDNSInfo(tx, int64(server.ClusterId), cacheMap)
if err != nil {
return nil, err
}
if clusterDNS != nil && clusterDNS.DnsDomainId > 0 {
clusterDNSConfig, err := clusterDNS.DecodeDNSConfig()
if err != nil {
return nil, err
}
domain, err := dns.SharedDNSDomainDAO.FindEnabledDNSDomain(tx, int64(clusterDNS.DnsDomainId), cacheMap)
if err != nil {
return nil, err
}
if domain != nil {
var cname = server.DnsName + "." + domain.Name
config.CNameDomain = cname
if clusterDNSConfig.CNAMEAsDomain {
config.CNameAsDomain = true
config.AliasServerNames = append(config.AliasServerNames, cname)
domain, err := dns.SharedDNSDomainDAO.FindEnabledDNSDomain(tx, int64(clusterDNS.DnsDomainId), cacheMap)
if err != nil {
return nil, err
}
if domain != nil {
var cname = server.DnsName + "." + domain.Name
config.CNameDomain = cname
if clusterDNSConfig.CNAMEAsDomain {
config.CNameAsDomain = true
config.AliasServerNames = append(config.AliasServerNames, cname)
}
}
}
}
@@ -1174,61 +1176,71 @@ func (this *ServerDAO) ComposeServerConfig(tx *dbs.Tx, server *Server, cacheMap
}
// Web
if server.WebId > 0 {
webConfig, err := SharedHTTPWebDAO.ComposeWebConfig(tx, int64(server.WebId), cacheMap)
if err != nil {
return nil, err
}
if webConfig != nil {
config.Web = webConfig
if !forList {
if server.WebId > 0 {
webConfig, err := SharedHTTPWebDAO.ComposeWebConfig(tx, int64(server.WebId), cacheMap)
if err != nil {
return nil, err
}
if webConfig != nil {
config.Web = webConfig
}
}
}
// ReverseProxy
if IsNotNull(server.ReverseProxy) {
var reverseProxyRef = &serverconfigs.ReverseProxyRef{}
err := json.Unmarshal(server.ReverseProxy, reverseProxyRef)
if err != nil {
return nil, err
}
config.ReverseProxyRef = reverseProxyRef
if !forList {
if IsNotNull(server.ReverseProxy) {
var reverseProxyRef = &serverconfigs.ReverseProxyRef{}
err := json.Unmarshal(server.ReverseProxy, reverseProxyRef)
if err != nil {
return nil, err
}
config.ReverseProxyRef = reverseProxyRef
reverseProxyConfig, err := SharedReverseProxyDAO.ComposeReverseProxyConfig(tx, reverseProxyRef.ReverseProxyId, cacheMap)
if err != nil {
return nil, err
}
if reverseProxyConfig != nil {
config.ReverseProxy = reverseProxyConfig
reverseProxyConfig, err := SharedReverseProxyDAO.ComposeReverseProxyConfig(tx, reverseProxyRef.ReverseProxyId, cacheMap)
if err != nil {
return nil, err
}
if reverseProxyConfig != nil {
config.ReverseProxy = reverseProxyConfig
}
}
}
// WAF策略
var clusterId = int64(server.ClusterId)
httpFirewallPolicyId, err := SharedNodeClusterDAO.FindClusterHTTPFirewallPolicyId(tx, clusterId, cacheMap)
if err != nil {
return nil, err
}
if httpFirewallPolicyId > 0 {
config.HTTPFirewallPolicyId = httpFirewallPolicyId
}
// 缓存策略
httpCachePolicyId, err := SharedNodeClusterDAO.FindClusterHTTPCachePolicyId(tx, clusterId, cacheMap)
if err != nil {
return nil, err
}
if httpCachePolicyId > 0 {
config.HTTPCachePolicyId = httpCachePolicyId
}
// traffic limit
if len(server.TrafficLimit) > 0 {
var trafficLimitConfig = &serverconfigs.TrafficLimitConfig{}
err = json.Unmarshal(server.TrafficLimit, trafficLimitConfig)
if !forList {
httpFirewallPolicyId, err := SharedNodeClusterDAO.FindClusterHTTPFirewallPolicyId(tx, clusterId, cacheMap)
if err != nil {
return nil, err
}
config.TrafficLimit = trafficLimitConfig
if httpFirewallPolicyId > 0 {
config.HTTPFirewallPolicyId = httpFirewallPolicyId
}
}
// 缓存策略
if !forList {
httpCachePolicyId, err := SharedNodeClusterDAO.FindClusterHTTPCachePolicyId(tx, clusterId, cacheMap)
if err != nil {
return nil, err
}
if httpCachePolicyId > 0 {
config.HTTPCachePolicyId = httpCachePolicyId
}
}
// traffic limit
if !forList {
if len(server.TrafficLimit) > 0 {
var trafficLimitConfig = &serverconfigs.TrafficLimitConfig{}
err := json.Unmarshal(server.TrafficLimit, trafficLimitConfig)
if err != nil {
return nil, err
}
config.TrafficLimit = trafficLimitConfig
}
}
// 用户套餐
@@ -1271,7 +1283,7 @@ func (this *ServerDAO) ComposeServerConfig(tx *dbs.Tx, server *Server, cacheMap
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)
err := json.Unmarshal(server.TrafficLimitStatus, status)
if err != nil {
return nil, err
}
@@ -1282,14 +1294,16 @@ func (this *ServerDAO) ComposeServerConfig(tx *dbs.Tx, server *Server, cacheMap
}
// UAM
if teaconst.IsPlus && IsNotNull(server.Uam) {
var uamConfig = &serverconfigs.UAMConfig{}
err = json.Unmarshal(server.Uam, uamConfig)
if err != nil {
return nil, err
}
if uamConfig.IsOn {
config.UAM = uamConfig
if !forList {
if teaconst.IsPlus && IsNotNull(server.Uam) {
var uamConfig = &serverconfigs.UAMConfig{}
err := json.Unmarshal(server.Uam, uamConfig)
if err != nil {
return nil, err
}
if uamConfig.IsOn {
config.UAM = uamConfig
}
}
}
@@ -1747,11 +1761,11 @@ func (this *ServerDAO) UpdateUserServersClusterId(tx *dbs.Tx, userId int64, oldC
}
if oldClusterId > 0 {
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, oldClusterId, 0, NodeTaskTypeConfigChanged)
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, oldClusterId, 0, 0, NodeTaskTypeConfigChanged)
if err != nil {
return err
}
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, oldClusterId, 0, NodeTaskTypeIPItemChanged)
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, oldClusterId, 0, 0, NodeTaskTypeIPItemChanged)
if err != nil {
return err
}
@@ -1762,11 +1776,11 @@ func (this *ServerDAO) UpdateUserServersClusterId(tx *dbs.Tx, userId int64, oldC
}
if newClusterId > 0 {
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, newClusterId, 0, NodeTaskTypeConfigChanged)
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, newClusterId, 0, 0, NodeTaskTypeConfigChanged)
if err != nil {
return err
}
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, newClusterId, 0, NodeTaskTypeIPItemChanged)
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, newClusterId, 0, 0, NodeTaskTypeIPItemChanged)
if err != nil {
return err
}
@@ -1779,11 +1793,24 @@ func (this *ServerDAO) UpdateUserServersClusterId(tx *dbs.Tx, userId int64, oldC
return err
}
// FindAllEnabledServersWithUserId 查找用户的所有服务
func (this *ServerDAO) FindAllEnabledServersWithUserId(tx *dbs.Tx, userId int64) (result []*Server, err error) {
// FindAllBasicServersWithUserId 查找用户的所有服务的基础信息
func (this *ServerDAO) FindAllBasicServersWithUserId(tx *dbs.Tx, userId int64) (result []*Server, err error) {
_, err = this.Query(tx).
Result("id", "serverNames", "name", "isOn", "type", "groupIds", "clusterId", "dnsName").
State(ServerStateEnabled).
Attr("userId", userId).
DescPk().
Slice(&result).
FindAll()
return
}
// FindAllAvailableServersWithUserId 查找用户的所有可用服务信息
func (this *ServerDAO) FindAllAvailableServersWithUserId(tx *dbs.Tx, userId int64) (result []*Server, err error) {
_, err = this.Query(tx).
State(ServerStateEnabled).
Attr("userId", userId).
Attr("isOn", true).
DescPk().
Slice(&result).
FindAll()
@@ -2364,21 +2391,21 @@ func (this *ServerDAO) UpdateServerTrafficLimitStatus(tx *dbs.Tx, trafficLimitCo
// daily
if trafficLimitConfig.DailyBytes() > 0 {
if server.TrafficDay == timeutil.Format("Ymd") && server.TotalDailyTraffic >= float64(trafficLimitConfig.DailyBytes())/1024/1024/1024 {
if server.TrafficDay == timeutil.Format("Ymd") && server.TotalDailyTraffic >= float64(trafficLimitConfig.DailyBytes())/(1<<30) {
untilDay = timeutil.Format("Ymd")
}
}
// monthly
if server.TrafficMonth == timeutil.Format("Ym") && trafficLimitConfig.MonthlyBytes() > 0 {
if server.TotalMonthlyTraffic >= float64(trafficLimitConfig.MonthlyBytes())/1024/1024/1024 {
if server.TotalMonthlyTraffic >= float64(trafficLimitConfig.MonthlyBytes())/(1<<30) {
untilDay = timeutil.Format("Ym32")
}
}
// totally
if trafficLimitConfig.TotalBytes() > 0 {
if server.TotalTraffic >= float64(trafficLimitConfig.TotalBytes())/1024/1024/1024 {
if server.TotalTraffic >= float64(trafficLimitConfig.TotalBytes())/(1<<30) {
untilDay = "30000101"
}
}
@@ -2408,7 +2435,7 @@ func (this *ServerDAO) UpdateServerTrafficLimitStatus(tx *dbs.Tx, trafficLimitCo
// IncreaseServerTotalTraffic 增加服务的总流量
func (this *ServerDAO) IncreaseServerTotalTraffic(tx *dbs.Tx, serverId int64, bytes int64) error {
var gb = float64(bytes) / 1024 / 1024 / 1024
var gb = float64(bytes) / (1 << 30)
var day = timeutil.Format("Ymd")
var month = timeutil.Format("Ym")
return this.Query(tx).
@@ -2602,11 +2629,29 @@ func (this *ServerDAO) UpdateServerBandwidth(tx *dbs.Tx, serverId int64, fullTim
if bandwidthBytes < 0 {
bandwidthBytes = 0
}
return this.Query(tx).
bandwidthTime, err := this.Query(tx).
Pk(serverId).
Set("bandwidthTime", fullTime).
Set("bandwidthBytes", bandwidthBytes).
UpdateQuickly()
Result("bandwidthTime").
FindStringCol("")
if err != nil {
return err
}
if bandwidthTime != fullTime {
return this.Query(tx).
Pk(serverId).
Set("bandwidthTime", fullTime).
Set("bandwidthBytes", bandwidthBytes).
UpdateQuickly()
} else {
return this.Query(tx).
Pk(serverId).
Set("bandwidthTime", fullTime).
Set("bandwidthBytes", dbs.SQL("bandwidthBytes+:bytes")).
Param("bytes", bandwidthBytes).
UpdateQuickly()
}
}
// NotifyUpdate 同步服务所在的集群
@@ -2619,7 +2664,7 @@ func (this *ServerDAO) NotifyUpdate(tx *dbs.Tx, serverId int64) error {
if clusterId == 0 {
return nil
}
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, serverId, NodeTaskTypeConfigChanged)
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, serverId, NodeTaskTypeConfigChanged)
}
// NotifyClusterUpdate 同步指定的集群
@@ -2627,7 +2672,7 @@ func (this *ServerDAO) NotifyClusterUpdate(tx *dbs.Tx, clusterId, serverId int64
if clusterId <= 0 {
return nil
}
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, serverId, NodeTaskTypeConfigChanged)
return SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, serverId, NodeTaskTypeConfigChanged)
}
// NotifyDNSUpdate 通知当前集群DNS更新

View File

@@ -13,6 +13,7 @@ import (
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"testing"
"time"
)
@@ -189,7 +190,7 @@ func TestServerDAO_FindAllEnabledServersWithNode_Cache(t *testing.T) {
t.Fatal(err)
}
for _, server := range servers {
_, _ = models.SharedServerDAO.ComposeServerConfig(nil, server, cacheMap, true)
_, _ = models.SharedServerDAO.ComposeServerConfig(nil, server, cacheMap, true, false)
}
}
@@ -200,7 +201,7 @@ func TestServerDAO_FindAllEnabledServersWithNode_Cache(t *testing.T) {
t.Fatal(err)
}
for _, server := range servers {
_, _ = models.SharedServerDAO.ComposeServerConfig(nil, server, cacheMap, true)
_, _ = models.SharedServerDAO.ComposeServerConfig(nil, server, cacheMap, true, false)
}
}
t.Log(time.Since(before).Seconds()*1000, "ms")
@@ -322,6 +323,15 @@ func TestServerDAO_FindBool(t *testing.T) {
}
}
func TestServerDAO_UpdateServerBandwidth(t *testing.T) {
var dao = models.NewServerDAO()
var tx *dbs.Tx
err := dao.UpdateServerBandwidth(tx, 1, timeutil.FormatTime("YmdHi", time.Now().Unix()/300*300), 1024)
if err != nil {
t.Fatal(err)
}
}
func BenchmarkServerDAO_CountAllEnabledServers(b *testing.B) {
models.SharedServerDAO = models.NewServerDAO()

View File

@@ -279,7 +279,7 @@ func (this *ServerGroupDAO) InitGroupWeb(tx *dbs.Tx, groupId int64) (int64, erro
}
// ComposeGroupConfig 组合配置
func (this *ServerGroupDAO) ComposeGroupConfig(tx *dbs.Tx, groupId int64, cacheMap *utils.CacheMap) (*serverconfigs.ServerGroupConfig, error) {
func (this *ServerGroupDAO) ComposeGroupConfig(tx *dbs.Tx, groupId int64, forList bool, cacheMap *utils.CacheMap) (*serverconfigs.ServerGroupConfig, error) {
if cacheMap == nil {
cacheMap = utils.NewCacheMap()
}
@@ -315,65 +315,67 @@ func (this *ServerGroupDAO) ComposeGroupConfig(tx *dbs.Tx, groupId int64, cacheM
IsOn: group.IsOn,
}
if IsNotNull(group.HttpReverseProxy) {
reverseProxyRef := &serverconfigs.ReverseProxyRef{}
err := json.Unmarshal(group.HttpReverseProxy, reverseProxyRef)
if err != nil {
return nil, err
}
config.HTTPReverseProxyRef = reverseProxyRef
if !forList {
if IsNotNull(group.HttpReverseProxy) {
reverseProxyRef := &serverconfigs.ReverseProxyRef{}
err := json.Unmarshal(group.HttpReverseProxy, reverseProxyRef)
if err != nil {
return nil, err
}
config.HTTPReverseProxyRef = reverseProxyRef
reverseProxyConfig, err := SharedReverseProxyDAO.ComposeReverseProxyConfig(tx, reverseProxyRef.ReverseProxyId, cacheMap)
if err != nil {
return nil, err
reverseProxyConfig, err := SharedReverseProxyDAO.ComposeReverseProxyConfig(tx, reverseProxyRef.ReverseProxyId, cacheMap)
if err != nil {
return nil, err
}
if reverseProxyConfig != nil {
config.HTTPReverseProxy = reverseProxyConfig
}
}
if reverseProxyConfig != nil {
config.HTTPReverseProxy = reverseProxyConfig
}
}
if IsNotNull(group.TcpReverseProxy) {
reverseProxyRef := &serverconfigs.ReverseProxyRef{}
err := json.Unmarshal(group.TcpReverseProxy, reverseProxyRef)
if err != nil {
return nil, err
}
config.TCPReverseProxyRef = reverseProxyRef
if IsNotNull(group.TcpReverseProxy) {
reverseProxyRef := &serverconfigs.ReverseProxyRef{}
err := json.Unmarshal(group.TcpReverseProxy, reverseProxyRef)
if err != nil {
return nil, err
}
config.TCPReverseProxyRef = reverseProxyRef
reverseProxyConfig, err := SharedReverseProxyDAO.ComposeReverseProxyConfig(tx, reverseProxyRef.ReverseProxyId, cacheMap)
if err != nil {
return nil, err
reverseProxyConfig, err := SharedReverseProxyDAO.ComposeReverseProxyConfig(tx, reverseProxyRef.ReverseProxyId, cacheMap)
if err != nil {
return nil, err
}
if reverseProxyConfig != nil {
config.TCPReverseProxy = reverseProxyConfig
}
}
if reverseProxyConfig != nil {
config.TCPReverseProxy = reverseProxyConfig
}
}
if IsNotNull(group.UdpReverseProxy) {
reverseProxyRef := &serverconfigs.ReverseProxyRef{}
err := json.Unmarshal(group.UdpReverseProxy, reverseProxyRef)
if err != nil {
return nil, err
}
config.UDPReverseProxyRef = reverseProxyRef
if IsNotNull(group.UdpReverseProxy) {
reverseProxyRef := &serverconfigs.ReverseProxyRef{}
err := json.Unmarshal(group.UdpReverseProxy, reverseProxyRef)
if err != nil {
return nil, err
}
config.UDPReverseProxyRef = reverseProxyRef
reverseProxyConfig, err := SharedReverseProxyDAO.ComposeReverseProxyConfig(tx, reverseProxyRef.ReverseProxyId, cacheMap)
if err != nil {
return nil, err
reverseProxyConfig, err := SharedReverseProxyDAO.ComposeReverseProxyConfig(tx, reverseProxyRef.ReverseProxyId, cacheMap)
if err != nil {
return nil, err
}
if reverseProxyConfig != nil {
config.UDPReverseProxy = reverseProxyConfig
}
}
if reverseProxyConfig != nil {
config.UDPReverseProxy = reverseProxyConfig
}
}
// web
if group.WebId > 0 {
webConfig, err := SharedHTTPWebDAO.ComposeWebConfig(tx, int64(group.WebId), cacheMap)
if err != nil {
return nil, err
}
if webConfig != nil {
config.Web = webConfig
// web
if group.WebId > 0 {
webConfig, err := SharedHTTPWebDAO.ComposeWebConfig(tx, int64(group.WebId), cacheMap)
if err != nil {
return nil, err
}
if webConfig != nil {
config.Web = webConfig
}
}
}

View File

@@ -125,3 +125,33 @@ func (this *Server) DecodeUDPPorts() (ports []int) {
}
return
}
// DecodeServerNames 获取域名
func (this *Server) DecodeServerNames() (serverNames []*serverconfigs.ServerNameConfig, count int) {
if len(this.ServerNames) == 0 {
return nil, 0
}
serverNames = []*serverconfigs.ServerNameConfig{}
err := json.Unmarshal(this.ServerNames, &serverNames)
if err != nil {
remotelogs.Error("Server/DecodeServerNames", "decode server names failed: "+err.Error())
return
}
for _, serverName := range serverNames {
count += serverName.Count()
}
return
}
// FirstServerName 获取第一个域名
func (this *Server) FirstServerName() string {
serverNames, _ := this.DecodeServerNames()
if len(serverNames) == 0 {
return ""
}
return serverNames[0].FirstName()
}

View File

@@ -141,3 +141,12 @@ func (this *SysLockerDAO) Increase(tx *dbs.Tx, key string, defaultValue int64) (
Result("version").
FindInt64Col(0)
}
// 读取当前版本号
func (this *SysLockerDAO) Read(tx *dbs.Tx, key string) (int64, error) {
return this.Query(tx).
Attr("key", key).
Result("version").
FindInt64Col(0)
}

View File

@@ -142,42 +142,6 @@ func (this *SysSettingDAO) ReadGlobalConfig(tx *dbs.Tx) (*serverconfigs.GlobalCo
return config, nil
}
// ReadUserServerConfig 读取用户服务配置
func (this *SysSettingDAO) ReadUserServerConfig(tx *dbs.Tx) (*userconfigs.UserServerConfig, error) {
valueJSON, err := this.ReadSetting(tx, systemconfigs.SettingCodeUserServerConfig)
if err != nil {
return nil, err
}
if len(valueJSON) == 0 {
return userconfigs.DefaultUserServerConfig(), nil
}
var config = userconfigs.DefaultUserServerConfig()
err = json.Unmarshal(valueJSON, config)
if err != nil {
return nil, err
}
return config, nil
}
// ReadUserFinanceConfig 读取用户服务配置
func (this *SysSettingDAO) ReadUserFinanceConfig(tx *dbs.Tx) (*userconfigs.UserFinanceConfig, error) {
valueJSON, err := this.ReadSetting(tx, systemconfigs.SettingCodeUserFinanceConfig)
if err != nil {
return nil, err
}
if len(valueJSON) == 0 {
return userconfigs.DefaultUserFinanceConfig(), nil
}
var config = userconfigs.DefaultUserFinanceConfig()
err = json.Unmarshal(valueJSON, config)
if err != nil {
return nil, err
}
return config, nil
}
// ReadAdminUIConfig 读取管理员界面配置
func (this *SysSettingDAO) ReadAdminUIConfig(tx *dbs.Tx, cacheMap *utils.CacheMap) (*systemconfigs.AdminUIConfig, error) {
var cacheKey = this.Table + ":ReadAdminUIConfig"
@@ -228,3 +192,21 @@ func (this *SysSettingDAO) NotifyUpdate(tx *dbs.Tx, code string) error {
}
return nil
}
// ReadUserServerConfig 读取用户服务配置
func (this *SysSettingDAO) ReadUserServerConfig(tx *dbs.Tx) (*userconfigs.UserServerConfig, error) {
valueJSON, err := this.ReadSetting(tx, systemconfigs.SettingCodeUserServerConfig)
if err != nil {
return nil, err
}
if len(valueJSON) == 0 {
return userconfigs.DefaultUserServerConfig(), nil
}
var config = userconfigs.DefaultUserServerConfig()
err = json.Unmarshal(valueJSON, config)
if err != nil {
return nil, err
}
return config, nil
}

View File

@@ -11,10 +11,10 @@ type SysSetting struct {
}
type SysSettingOperator struct {
Id interface{} // ID
UserId interface{} // 用户ID
Code interface{} // 代号
Value interface{} // 配置值
Id any // ID
UserId any // 用户ID
Code any // 代号
Value any // 配置值
}
func NewSysSettingOperator() *SysSettingOperator {

View File

@@ -0,0 +1,63 @@
package models
import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
)
const (
TrafficPackageStateEnabled = 1 // 已启用
TrafficPackageStateDisabled = 0 // 已禁用
)
type TrafficPackageDAO dbs.DAO
func NewTrafficPackageDAO() *TrafficPackageDAO {
return dbs.NewDAO(&TrafficPackageDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeTrafficPackages",
Model: new(TrafficPackage),
PkName: "id",
},
}).(*TrafficPackageDAO)
}
var SharedTrafficPackageDAO *TrafficPackageDAO
func init() {
dbs.OnReady(func() {
SharedTrafficPackageDAO = NewTrafficPackageDAO()
})
}
// EnableTrafficPackage 启用条目
func (this *TrafficPackageDAO) EnableTrafficPackage(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
Set("state", TrafficPackageStateEnabled).
Update()
return err
}
// DisableTrafficPackage 禁用条目
func (this *TrafficPackageDAO) DisableTrafficPackage(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
Set("state", TrafficPackageStateDisabled).
Update()
return err
}
// FindEnabledTrafficPackage 查找启用中的条目
func (this *TrafficPackageDAO) FindEnabledTrafficPackage(tx *dbs.Tx, id int64) (*TrafficPackage, error) {
result, err := this.Query(tx).
Pk(id).
State(TrafficPackageStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*TrafficPackage), err
}

View File

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

View File

@@ -0,0 +1,24 @@
package models
// TrafficPackage 流量包
type TrafficPackage struct {
Id uint32 `field:"id"` // ID
Size uint32 `field:"size"` // 尺寸
Unit string `field:"unit"` // 单位gb|tb等
Bytes uint64 `field:"bytes"` // 字节
IsOn bool `field:"isOn"` // 是否启用
State uint8 `field:"state"` // 状态
}
type TrafficPackageOperator struct {
Id any // ID
Size any // 尺寸
Unit any // 单位gb|tb等
Bytes any // 字节
IsOn any // 是否启用
State any // 状态
}
func NewTrafficPackageOperator() *TrafficPackageOperator {
return &TrafficPackageOperator{}
}

View File

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

View File

@@ -0,0 +1,63 @@
package models
import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
)
const (
TrafficPackagePeriodStateEnabled = 1 // 已启用
TrafficPackagePeriodStateDisabled = 0 // 已禁用
)
type TrafficPackagePeriodDAO dbs.DAO
func NewTrafficPackagePeriodDAO() *TrafficPackagePeriodDAO {
return dbs.NewDAO(&TrafficPackagePeriodDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeTrafficPackagePeriods",
Model: new(TrafficPackagePeriod),
PkName: "id",
},
}).(*TrafficPackagePeriodDAO)
}
var SharedTrafficPackagePeriodDAO *TrafficPackagePeriodDAO
func init() {
dbs.OnReady(func() {
SharedTrafficPackagePeriodDAO = NewTrafficPackagePeriodDAO()
})
}
// EnableTrafficPackagePeriod 启用条目
func (this *TrafficPackagePeriodDAO) EnableTrafficPackagePeriod(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
Set("state", TrafficPackagePeriodStateEnabled).
Update()
return err
}
// DisableTrafficPackagePeriod 禁用条目
func (this *TrafficPackagePeriodDAO) DisableTrafficPackagePeriod(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
Set("state", TrafficPackagePeriodStateDisabled).
Update()
return err
}
// FindEnabledTrafficPackagePeriod 查找启用中的条目
func (this *TrafficPackagePeriodDAO) FindEnabledTrafficPackagePeriod(tx *dbs.Tx, id int64) (*TrafficPackagePeriod, error) {
result, err := this.Query(tx).
Pk(id).
State(TrafficPackagePeriodStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*TrafficPackagePeriod), err
}

View File

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

View File

@@ -0,0 +1,24 @@
package models
// TrafficPackagePeriod 流量包有效期
type TrafficPackagePeriod struct {
Id uint32 `field:"id"` // ID
IsOn bool `field:"isOn"` // 是否启用
Count uint32 `field:"count"` // 数量
Unit string `field:"unit"` // 单位month, year
Months uint32 `field:"months"` // 月数
State uint8 `field:"state"` // 状态
}
type TrafficPackagePeriodOperator struct {
Id any // ID
IsOn any // 是否启用
Count any // 数量
Unit any // 单位month, year
Months any // 月数
State any // 状态
}
func NewTrafficPackagePeriodOperator() *TrafficPackagePeriodOperator {
return &TrafficPackagePeriodOperator{}
}

View File

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

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 TrafficPackagePriceDAO dbs.DAO
func NewTrafficPackagePriceDAO() *TrafficPackagePriceDAO {
return dbs.NewDAO(&TrafficPackagePriceDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeTrafficPackagePrices",
Model: new(TrafficPackagePrice),
PkName: "id",
},
}).(*TrafficPackagePriceDAO)
}
var SharedTrafficPackagePriceDAO *TrafficPackagePriceDAO
func init() {
dbs.OnReady(func() {
SharedTrafficPackagePriceDAO = NewTrafficPackagePriceDAO()
})
}

View File

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

View File

@@ -0,0 +1,24 @@
package models
// TrafficPackagePrice 流量包价格
type TrafficPackagePrice struct {
Id uint32 `field:"id"` // ID
PackageId uint32 `field:"packageId"` // 套餐ID
RegionId uint32 `field:"regionId"` // 区域ID
PeriodId uint32 `field:"periodId"` // 有效期ID
Price float64 `field:"price"` // 价格
DiscountPrice float64 `field:"discountPrice"` // 折后价格
}
type TrafficPackagePriceOperator struct {
Id any // ID
PackageId any // 套餐ID
RegionId any // 区域ID
PeriodId any // 有效期ID
Price any // 价格
DiscountPrice any // 折后价格
}
func NewTrafficPackagePriceOperator() *TrafficPackagePriceOperator {
return &TrafficPackagePriceOperator{}
}

View File

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

View File

@@ -1,8 +1,12 @@
package models
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeAPI/internal/utils/regexputils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
@@ -10,6 +14,8 @@ import (
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"math"
"strings"
"sync"
"time"
)
@@ -55,7 +61,7 @@ func init() {
}
// UpdateUserBandwidth 写入数据
func (this *UserBandwidthStatDAO) UpdateUserBandwidth(tx *dbs.Tx, userId int64, day string, timeAt string, bytes int64) error {
func (this *UserBandwidthStatDAO) UpdateUserBandwidth(tx *dbs.Tx, userId int64, regionId int64, day string, timeAt string, bytes int64) error {
if userId <= 0 {
// 如果用户ID不大于0则说明服务不属于任何用户此时不需要处理
return nil
@@ -65,10 +71,11 @@ func (this *UserBandwidthStatDAO) UpdateUserBandwidth(tx *dbs.Tx, userId int64,
Table(this.partialTable(userId)).
Param("bytes", bytes).
InsertOrUpdateQuickly(maps.Map{
"userId": userId,
"day": day,
"timeAt": timeAt,
"bytes": bytes,
"userId": userId,
"regionId": regionId,
"day": day,
"timeAt": timeAt,
"bytes": bytes,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
})
@@ -79,9 +86,12 @@ func (this *UserBandwidthStatDAO) UpdateUserBandwidth(tx *dbs.Tx, userId int64,
func (this *UserBandwidthStatDAO) FindUserPeekBandwidthInMonth(tx *dbs.Tx, userId int64, month string) (*UserBandwidthStat, error) {
one, err := this.Query(tx).
Table(this.partialTable(userId)).
Result("MIN(id) AS id", "MIN(userId) AS userId", "day", "timeAt", "SUM(bytes) AS bytes").
Attr("userId", userId).
Between("day", month+"01", month+"31").
Desc("bytes").
Group("day").
Group("timeAt").
Find()
if err != nil || one == nil {
return nil, err
@@ -89,14 +99,100 @@ func (this *UserBandwidthStatDAO) FindUserPeekBandwidthInMonth(tx *dbs.Tx, userI
return one.(*UserBandwidthStat), nil
}
// FindPercentileBetweenDays 获取日期段内内百分位
// regionId 如果为 -1 表示没有区域的带宽;如果为 0 表示所有区域的带宽
func (this *UserBandwidthStatDAO) FindPercentileBetweenDays(tx *dbs.Tx, userId int64, regionId int64, dayFrom string, dayTo string, percentile int32) (result *UserBandwidthStat, err error) {
if dayFrom > dayTo {
dayFrom, dayTo = dayTo, dayFrom
}
if percentile <= 0 {
percentile = 95
}
// 如果是100%以上,则快速返回
if percentile >= 100 {
var query = this.Query(tx).
Table(this.partialTable(userId))
if regionId > 0 {
query.Attr("regionId", regionId)
} else if regionId < 0 {
query.Attr("regionId", 0)
}
one, err := query.
Result("MIN(id) AS id", "MIN(userId) AS userId", "day", "timeAt", "SUM(bytes) AS bytes").
Attr("userId", userId).
Between("day", dayFrom, dayTo).
Desc("bytes").
Group("day").
Group("timeAt").
Find()
if err != nil || one == nil {
return nil, err
}
return one.(*UserBandwidthStat), nil
}
// 总数量
var totalQuery = this.Query(tx).
Table(this.partialTable(userId))
if regionId > 0 {
totalQuery.Attr("regionId", regionId)
} else if regionId < 0 {
totalQuery.Attr("regionId", 0)
}
total, err := totalQuery.
Attr("userId", userId).
Between("day", dayFrom, dayTo).
CountAttr("DISTINCT day, timeAt")
if err != nil {
return nil, err
}
if total == 0 {
return nil, nil
}
var offset int64
if total > 1 {
offset = int64(math.Ceil(float64(total) * float64(100-percentile) / 100))
}
// 查询 nth 位置
var query = this.Query(tx).
Table(this.partialTable(userId))
if regionId > 0 {
query.Attr("regionId", regionId)
} else if regionId < 0 {
query.Attr("regionId", 0)
}
one, err := query.
Result("MIN(id) AS id", "MIN(userId) AS userId", "day", "timeAt", "SUM(bytes) AS bytes").
Attr("userId", userId).
Between("day", dayFrom, dayTo).
Desc("bytes").
Group("day").
Group("timeAt").
Offset(offset).
Find()
if err != nil || one == nil {
return nil, err
}
return one.(*UserBandwidthStat), nil
}
// FindUserPeekBandwidthInDay 读取某日带宽峰值
// day YYYYMMDD
func (this *UserBandwidthStatDAO) FindUserPeekBandwidthInDay(tx *dbs.Tx, userId int64, day string) (*UserBandwidthStat, error) {
one, err := this.Query(tx).
Table(this.partialTable(userId)).
Result("MIN(id) AS id", "MIN(userId) AS userId", "MIN(day) AS day", "timeAt", "SUM(bytes) AS bytes").
Attr("userId", userId).
Attr("day", day).
Desc("bytes").
Group("timeAt").
Find()
if err != nil || one == nil {
return nil, err
@@ -104,6 +200,118 @@ func (this *UserBandwidthStatDAO) FindUserPeekBandwidthInDay(tx *dbs.Tx, userId
return one.(*UserBandwidthStat), nil
}
// FindUserBandwidthStatsBetweenDays 查找日期段内的带宽峰值
// dayFrom YYYYMMDD
// dayTo YYYYMMDD
func (this *UserBandwidthStatDAO) FindUserBandwidthStatsBetweenDays(tx *dbs.Tx, userId int64, regionId int64, dayFrom string, dayTo string) (result []*pb.FindDailyServerBandwidthStatsBetweenDaysResponse_Stat, err error) {
if userId <= 0 {
return nil, nil
}
if !regexputils.YYYYMMDD.MatchString(dayFrom) {
return nil, errors.New("invalid dayFrom '" + dayFrom + "'")
}
if !regexputils.YYYYMMDD.MatchString(dayTo) {
return nil, errors.New("invalid dayTo '" + dayTo + "'")
}
if dayFrom > dayTo {
dayFrom, dayTo = dayTo, dayFrom
}
var query = this.Query(tx).
Table(this.partialTable(userId))
if regionId > 0 {
query.Attr("regionId", regionId)
}
ones, _, err := query.
Result("SUM(bytes) AS bytes", "day", "timeAt").
Attr("userId", userId).
Between("day", dayFrom, dayTo).
Group("day").
Group("timeAt").
FindOnes()
if err != nil {
return nil, err
}
var m = map[string]*pb.FindDailyServerBandwidthStatsBetweenDaysResponse_Stat{}
for _, one := range ones {
var day = one.GetString("day")
var bytes = one.GetInt64("bytes")
var timeAt = one.GetString("timeAt")
var key = day + "@" + timeAt
m[key] = &pb.FindDailyServerBandwidthStatsBetweenDaysResponse_Stat{
Bytes: bytes,
Bits: bytes * 8,
Day: day,
TimeAt: timeAt,
}
}
allDays, err := utils.RangeDays(dayFrom, dayTo)
if err != nil {
return nil, err
}
dayTimes, err := utils.Range24HourTimes(5)
if err != nil {
return nil, err
}
// 截止到当前时间
var currentTime = timeutil.Format("Ymd@Hi")
for _, day := range allDays {
for _, timeAt := range dayTimes {
var key = day + "@" + timeAt
if key >= currentTime {
break
}
stat, ok := m[key]
if ok {
result = append(result, stat)
} else {
result = append(result, &pb.FindDailyServerBandwidthStatsBetweenDaysResponse_Stat{
Day: day,
TimeAt: timeAt,
})
}
}
}
return result, nil
}
// FindDistinctUserIds 获取所有有带宽的用户ID
// dayFrom YYYYMMDD
// dayTo YYYYMMDD
func (this *UserBandwidthStatDAO) FindDistinctUserIds(tx *dbs.Tx, dayFrom string, dayTo string) (userIds []int64, err error) {
dayFrom = strings.ReplaceAll(dayFrom, "-", "")
dayTo = strings.ReplaceAll(dayTo, "-", "")
err = this.runBatch(func(table string, locker *sync.Mutex) error {
ones, err := this.Query(tx).
Table(table).
Between("day", dayFrom, dayTo).
Result("DISTINCT userId").
FindAll()
if err != nil {
return err
}
for _, one := range ones {
locker.Lock()
userIds = append(userIds, int64(one.(*UserBandwidthStat).UserId))
locker.Unlock()
}
return nil
})
return
}
// Clean 清理过期数据
func (this *UserBandwidthStatDAO) Clean(tx *dbs.Tx) error {
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -100)) // 保留大约3个月的数据

View File

@@ -5,14 +5,38 @@ import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/logs"
timeutil "github.com/iwind/TeaGo/utils/time"
"testing"
"time"
)
func TestUserBandwidthStatDAO_FindUserPeekBandwidthInMonth(t *testing.T) {
var dao = models.NewUserBandwidthStatDAO()
var tx *dbs.Tx
stat, err := dao.FindUserPeekBandwidthInMonth(tx, 1, timeutil.Format("Ym"))
if err != nil {
t.Fatal(err)
}
logs.PrintAsJSON(stat, t)
}
func TestUserBandwidthStatDAO_FindUserPeekBandwidthInDay(t *testing.T) {
var dao = models.NewUserBandwidthStatDAO()
var tx *dbs.Tx
stat, err := dao.FindUserPeekBandwidthInDay(tx, 1, timeutil.Format("Ymd"))
if err != nil {
t.Fatal(err)
}
logs.PrintAsJSON(stat, t)
}
func TestUserBandwidthStatDAO_UpdateServerBandwidth(t *testing.T) {
var dao = models.NewUserBandwidthStatDAO()
var tx *dbs.Tx
err := dao.UpdateUserBandwidth(tx, 1, timeutil.Format("Ymd"), timeutil.Format("Hi"), 1024)
err := dao.UpdateUserBandwidth(tx, 1, 0, timeutil.Format("Ymd"), timeutil.Format("Hi"), 1024)
if err != nil {
t.Fatal(err)
}
@@ -28,3 +52,30 @@ func TestUserBandwidthStatDAO_Clean(t *testing.T) {
}
t.Log("ok")
}
func TestUserBandwidthStatDAO_FindBandwidthStatsBetweenDays(t *testing.T) {
var dao = models.NewUserBandwidthStatDAO()
var tx *dbs.Tx
stats, err := dao.FindUserBandwidthStatsBetweenDays(tx, 1, 0, timeutil.Format("Ymd", time.Now().AddDate(0, 0, -2)), timeutil.Format("Ymd"))
if err != nil {
t.Fatal(err)
}
var dataCount = 0
for _, stat := range stats {
t.Log(stat.Day, stat.TimeAt, "bytes:", stat.Bytes, "bits:", stat.Bits)
if stat.Bytes > 0 {
dataCount++
}
}
t.Log("data count:", dataCount)
}
func TestUserBandwidthStatDAO_FindPercentileBetweenDays(t *testing.T) {
var dao = models.NewUserBandwidthStatDAO()
var tx *dbs.Tx
stat, err := dao.FindPercentileBetweenDays(tx, 1, 0, timeutil.Format("Ymd"), timeutil.Format("Ymd"), 95)
if err != nil {
t.Fatal(err)
}
logs.PrintAsJSON(stat, t)
}

View File

@@ -2,19 +2,21 @@ package models
// UserBandwidthStat 用户月带宽峰值
type UserBandwidthStat struct {
Id uint64 `field:"id"` // ID
UserId uint64 `field:"userId"` // 用户ID
Day string `field:"day"` // 日期YYYYMMDD
TimeAt string `field:"timeAt"` // 时间点HHII
Bytes uint64 `field:"bytes"` // 带宽
Id uint64 `field:"id"` // ID
UserId uint64 `field:"userId"` // 用户ID
Day string `field:"day"` // 日期YYYYMMDD
TimeAt string `field:"timeAt"` // 时间点HHII
Bytes uint64 `field:"bytes"` // 带宽
RegionId uint32 `field:"regionId"` // 区域ID
}
type UserBandwidthStatOperator struct {
Id interface{} // ID
UserId interface{} // 用户ID
Day interface{} // 日期YYYYMMDD
TimeAt interface{} // 时间点HHII
Bytes interface{} // 带宽
Id any // ID
UserId any // 用户ID
Day any // 日期YYYYMMDD
TimeAt any // 时间点HHII
Bytes any // 带宽
RegionId any // 区域ID
}
func NewUserBandwidthStatOperator() *UserBandwidthStatOperator {

View File

@@ -1,50 +1,9 @@
package models
import (
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
func init() {
dbs.OnReadyDone(func() {
var generatedMonths = []string{}
goman.New(func() {
// 自动生成账单任务
var ticker = time.NewTicker(1 * time.Hour)
for range ticker.C {
// 是否已经生成了,如果已经生成了就跳过
var lastMonth = timeutil.Format("Ym", time.Now().AddDate(0, -1, 0))
if lists.ContainsString(generatedMonths, lastMonth) {
continue
}
err := SharedUserBillDAO.GenerateBills(nil, lastMonth)
if err != nil {
remotelogs.Error("UserBillDAO", "generate bills failed: "+err.Error())
} else {
generatedMonths = append(generatedMonths, lastMonth)
}
}
})
})
}
type BillType = string
const (
BillTypeTraffic BillType = "traffic" // 按流量计费
)
type UserBillDAO dbs.DAO
@@ -67,415 +26,3 @@ func init() {
SharedUserBillDAO = NewUserBillDAO()
})
}
// FindUserBill 查找单个账单
func (this *UserBillDAO) FindUserBill(tx *dbs.Tx, billId int64) (*UserBill, error) {
one, err := this.Query(tx).
Pk(billId).
Find()
if err != nil || one == nil {
return nil, err
}
return one.(*UserBill), nil
}
// CountAllUserBills 计算账单数量
func (this *UserBillDAO) CountAllUserBills(tx *dbs.Tx, isPaid int32, userId int64, month string) (int64, error) {
query := this.Query(tx)
if isPaid == 0 {
query.Attr("isPaid", 0)
} else if isPaid > 0 {
query.Attr("isPaid", 1)
}
if userId > 0 {
query.Attr("userId", userId)
}
if len(month) > 0 {
query.Attr("month", month)
}
return query.Count()
}
// ListUserBills 列出单页账单
func (this *UserBillDAO) ListUserBills(tx *dbs.Tx, isPaid int32, userId int64, month string, offset, size int64) (result []*UserBill, err error) {
query := this.Query(tx)
if isPaid == 0 {
query.Attr("isPaid", 0)
} else if isPaid > 0 {
query.Attr("isPaid", 1)
}
if userId > 0 {
query.Attr("userId", userId)
}
if len(month) > 0 {
query.Attr("month", month)
}
_, err = query.
Offset(offset).
Limit(size).
Slice(&result).
DescPk().
FindAll()
return
}
// FindUnpaidBills 查找未支付订单
func (this *UserBillDAO) FindUnpaidBills(tx *dbs.Tx, size int64) (result []*UserBill, err error) {
if size <= 0 {
size = 10000
}
_, err = this.Query(tx).
Attr("isPaid", false).
Lt("month", timeutil.Format("Ym")). //当月的不能支付,因为当月还没过完
Limit(size).
DescPk().
Slice(&result).
FindAll()
return
}
// CreateBill 创建账单
func (this *UserBillDAO) CreateBill(tx *dbs.Tx, userId int64, billType BillType, description string, amount float64, month string, canPay bool) error {
code, err := this.GenerateBillCode(tx)
if err != nil {
return err
}
return this.Query(tx).
InsertOrUpdateQuickly(maps.Map{
"userId": userId,
"type": billType,
"description": description,
"amount": amount,
"month": month,
"code": code,
"isPaid": amount == 0,
"canPay": canPay,
}, maps.Map{
"amount": amount,
"canPay": canPay,
})
}
// ExistBill 检查是否有当月账单
func (this *UserBillDAO) ExistBill(tx *dbs.Tx, userId int64, billType BillType, month string) (bool, error) {
return this.Query(tx).
Attr("userId", userId).
Attr("month", month).
Attr("type", billType).
Exist()
}
// GenerateBills 生成账单
// month 格式YYYYMM
func (this *UserBillDAO) GenerateBills(tx *dbs.Tx, month string) error {
// 区域价格
regions, err := SharedNodeRegionDAO.FindAllEnabledRegionPrices(tx)
if err != nil {
return err
}
var priceItems []*NodePriceItem
if len(regions) > 0 {
priceItems, err = SharedNodePriceItemDAO.FindAllEnabledRegionPrices(tx, NodePriceTypeTraffic)
if err != nil {
return err
}
}
// 默认计费方式
userFinanceConfig, err := SharedSysSettingDAO.ReadUserFinanceConfig(tx)
if err != nil {
return err
}
// 计算服务套餐费用
plans, err := SharedPlanDAO.FindAllEnabledPlans(tx)
if err != nil {
return err
}
var planMap = map[int64]*Plan{}
for _, plan := range plans {
planMap[int64(plan.Id)] = plan
}
var dayFrom = month + "01"
var dayTo = month + "32"
serverIds, err := SharedServerDailyStatDAO.FindDistinctServerIds(tx, dayFrom, dayTo)
if err != nil {
return err
}
var cacheMap = utils.NewCacheMap()
var userIds = []int64{}
for _, serverId := range serverIds {
// 套餐类型
userPlanId, userId, err := SharedServerDAO.FindServerLastUserPlanIdAndUserId(tx, serverId)
if err != nil {
return err
}
if userId == 0 {
continue
}
userIds = append(userIds, userId)
if userPlanId == 0 {
// 总流量
totalTrafficBytes, err := SharedServerDailyStatDAO.SumMonthlyBytes(tx, serverId, month)
if err != nil {
return err
}
// 默认计费方式
if userFinanceConfig != nil && userFinanceConfig.IsOn { // 默认计费方式
switch userFinanceConfig.PriceType {
case serverconfigs.PlanPriceTypeTraffic:
var config = userFinanceConfig.TrafficPriceConfig
var fee float64 = 0
if config != nil && config.Base > 0 {
fee = float64(totalTrafficBytes) / 1024 / 1024 / 1024 * float64(config.Base)
}
// 百分位
var percentile = 95
percentileBytes, err := SharedServerBandwidthStatDAO.FindMonthlyPercentile(tx, serverId, month, percentile)
if err != nil {
return err
}
err = SharedServerBillDAO.CreateOrUpdateServerBill(tx, userId, serverId, month, userPlanId, 0, totalTrafficBytes, percentileBytes, percentile, userFinanceConfig.PriceType, fee)
if err != nil {
return err
}
case serverconfigs.PlanPriceTypeBandwidth:
// 百分位
var percentile = 95
var config = userFinanceConfig.BandwidthPriceConfig
if config != nil {
percentile = config.Percentile
if percentile <= 0 {
percentile = 95
} else if percentile > 100 {
percentile = 100
}
}
percentileBytes, err := SharedServerBandwidthStatDAO.FindMonthlyPercentile(tx, serverId, month, percentile)
if err != nil {
return err
}
var mb = float32(percentileBytes) / 1024 / 1024
var price float32
if config != nil {
price = config.LookupPrice(mb)
}
var fee = float64(price)
err = SharedServerBillDAO.CreateOrUpdateServerBill(tx, userId, serverId, month, userPlanId, 0, totalTrafficBytes, percentileBytes, percentile, userFinanceConfig.PriceType, fee)
if err != nil {
return err
}
}
} else { // 区域流量计费
var fee float64
for _, region := range regions {
var regionId = int64(region.Id)
var pricesMap = region.DecodePriceMap()
if len(pricesMap) == 0 {
continue
}
trafficBytes, err := SharedServerDailyStatDAO.SumServerMonthlyWithRegion(tx, serverId, regionId, month)
if err != nil {
return err
}
if trafficBytes == 0 {
continue
}
var itemId = SharedNodePriceItemDAO.SearchItemsWithBytes(priceItems, trafficBytes)
if itemId == 0 {
continue
}
price, ok := pricesMap[itemId]
if !ok {
continue
}
if price <= 0 {
continue
}
var regionFee = float64(trafficBytes) / 1000 / 1000 / 1000 * 8 * price
fee += regionFee
}
// 百分位
var percentile = 95
percentileBytes, err := SharedServerBandwidthStatDAO.FindMonthlyPercentile(tx, serverId, month, percentile)
if err != nil {
return err
}
err = SharedServerBillDAO.CreateOrUpdateServerBill(tx, userId, serverId, month, userPlanId, 0, totalTrafficBytes, percentileBytes, percentile, "", fee)
if err != nil {
return err
}
}
} else {
userPlan, err := SharedUserPlanDAO.FindUserPlanWithoutState(tx, userPlanId, cacheMap)
if err != nil {
return err
}
if userPlan == nil {
continue
}
plan, ok := planMap[int64(userPlan.PlanId)]
if !ok {
continue
}
// 总流量
totalTrafficBytes, err := SharedServerDailyStatDAO.SumMonthlyBytes(tx, serverId, month)
if err != nil {
return err
}
switch plan.PriceType {
case serverconfigs.PlanPriceTypePeriod:
// 已经在购买套餐的时候付过费,这里不再重复计费
var fee float64 = 0
// 百分位
var percentile = 95
percentileBytes, err := SharedServerBandwidthStatDAO.FindMonthlyPercentile(tx, serverId, month, percentile)
if err != nil {
return err
}
err = SharedServerBillDAO.CreateOrUpdateServerBill(tx, int64(userPlan.UserId), serverId, month, userPlanId, int64(userPlan.PlanId), totalTrafficBytes, percentileBytes, percentile, plan.PriceType, fee)
if err != nil {
return err
}
case serverconfigs.PlanPriceTypeTraffic:
var config = plan.DecodeTrafficPrice()
var fee float64 = 0
if config != nil && config.Base > 0 {
fee = float64(totalTrafficBytes) / 1024 / 1024 / 1024 * float64(config.Base)
}
// 百分位
var percentile = 95
percentileBytes, err := SharedServerBandwidthStatDAO.FindMonthlyPercentile(tx, serverId, month, percentile)
if err != nil {
return err
}
err = SharedServerBillDAO.CreateOrUpdateServerBill(tx, int64(userPlan.UserId), serverId, month, userPlanId, int64(userPlan.PlanId), totalTrafficBytes, percentileBytes, percentile, plan.PriceType, fee)
if err != nil {
return err
}
case serverconfigs.PlanPriceTypeBandwidth:
// 百分位
var percentile = 95
var config = plan.DecodeBandwidthPrice()
if config != nil {
percentile = config.Percentile
if percentile <= 0 {
percentile = 95
} else if percentile > 100 {
percentile = 100
}
}
percentileBytes, err := SharedServerBandwidthStatDAO.FindMonthlyPercentile(tx, serverId, month, percentile)
if err != nil {
return err
}
var mb = float32(percentileBytes) / 1024 / 1024
var price float32
if config != nil {
price = config.LookupPrice(mb)
}
var fee = float64(price)
err = SharedServerBillDAO.CreateOrUpdateServerBill(tx, int64(userPlan.UserId), serverId, month, userPlanId, int64(userPlan.PlanId), totalTrafficBytes, percentileBytes, percentile, plan.PriceType, fee)
if err != nil {
return err
}
}
}
}
// 计算用户费用
for _, userId := range userIds {
if userId == 0 {
continue
}
amount, err := SharedServerBillDAO.SumUserMonthlyAmount(tx, userId, month)
if err != nil {
return err
}
err = SharedUserBillDAO.CreateBill(tx, userId, BillTypeTraffic, "流量带宽费用", amount, month, month < timeutil.Format("Ym"))
if err != nil {
return err
}
}
return nil
}
// UpdateUserBillIsPaid 设置账单已支付
func (this *UserBillDAO) UpdateUserBillIsPaid(tx *dbs.Tx, billId int64, isPaid bool) error {
return this.Query(tx).
Pk(billId).
Set("isPaid", isPaid).
UpdateQuickly()
}
// BillTypeName 获取账单类型名称
func (this *UserBillDAO) BillTypeName(billType BillType) string {
switch billType {
case BillTypeTraffic:
return "流量带宽"
}
return ""
}
// GenerateBillCode 生成账单编号
func (this *UserBillDAO) GenerateBillCode(tx *dbs.Tx) (string, error) {
var code = timeutil.Format("YmdHis") + types.String(rands.Int(100000, 999999))
exists, err := this.Query(tx).
Attr("code", code).
Exist()
if err != nil {
return "", err
}
if !exists {
return code, nil
}
return this.GenerateBillCode(tx)
}
// CheckUserBill 检查用户账单
func (this *UserBillDAO) CheckUserBill(tx *dbs.Tx, userId int64, billId int64) error {
if userId <= 0 || billId <= 0 {
return ErrNotFound
}
exists, err := this.Query(tx).
Pk(billId).
Attr("userId", userId).
Exist()
if err != nil {
return err
}
if !exists {
return ErrNotFound
}
return nil
}
// SumUnpaidUserBill 计算某个用户未支付总额
func (this *UserBillDAO) SumUnpaidUserBill(tx *dbs.Tx, userId int64) (float32, error) {
sum, err := this.Query(tx).
Attr("userId", userId).
Attr("isPaid", 0).
Sum("amount", 0)
if err != nil {
return 0, err
}
return float32(sum), nil
}

View File

@@ -1,20 +1,6 @@
package models
package models_test
import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/dbs"
timeutil "github.com/iwind/TeaGo/utils/time"
"testing"
_ "github.com/iwind/TeaGo/bootstrap"
)
func TestUserBillDAO_GenerateBills(t *testing.T) {
dbs.NotifyReady()
var tx *dbs.Tx
err := SharedUserBillDAO.GenerateBills(tx, timeutil.Format("Ym"))
if err != nil {
t.Fatal(err)
}
t.Log("ok")
}

View File

@@ -5,6 +5,7 @@ type UserBill struct {
Id uint64 `field:"id"` // ID
UserId uint32 `field:"userId"` // 用户ID
Type string `field:"type"` // 消费类型
PricePeriod string `field:"pricePeriod"` // 计费周期
Description string `field:"description"` // 描述
Amount float64 `field:"amount"` // 消费数额
DayFrom string `field:"dayFrom"` // YYYYMMDD
@@ -15,22 +16,27 @@ type UserBill struct {
PaidAt uint64 `field:"paidAt"` // 支付时间
Code string `field:"code"` // 账单编号
CreatedAt uint64 `field:"createdAt"` // 创建时间
CreatedDay string `field:"createdDay"` // 创建日期
State uint8 `field:"state"` // 状态
}
type UserBillOperator struct {
Id interface{} // ID
UserId interface{} // 用户ID
Type interface{} // 消费类型
Description interface{} // 描述
Amount interface{} // 消费数额
DayFrom interface{} // YYYYMMDD
DayTo interface{} // YYYYMMDD
Month interface{} // 帐期YYYYMM
CanPay interface{} // 是否可以支付
IsPaid interface{} // 是否支付
PaidAt interface{} // 支付时间
Code interface{} // 账单编号
CreatedAt interface{} // 创建时间
Id any // ID
UserId any // 用户ID
Type any // 消费类型
PricePeriod any // 计费周期
Description any // 描述
Amount any // 消费数额
DayFrom any // YYYYMMDD
DayTo any // YYYYMMDD
Month any // 帐期YYYYMM
CanPay any // 是否可以支付
IsPaid any // 是否已支付
PaidAt any // 支付时间
Code any // 账单编号
CreatedAt any // 创建时间
CreatedDay any // 创建日期
State any // 状态
}
func NewUserBillOperator() *UserBillOperator {

View File

@@ -5,6 +5,7 @@ import (
dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
_ "github.com/go-sql-driver/mysql"
@@ -52,7 +53,11 @@ func (this *UserDAO) EnableUser(tx *dbs.Tx, userId int64) error {
Pk(userId).
Set("state", UserStateEnabled).
Update()
return err
if err != nil {
return err
}
return this.NotifyUpdate(tx, userId)
}
// DisableUser 禁用条目
@@ -65,7 +70,11 @@ func (this *UserDAO) DisableUser(tx *dbs.Tx, userId int64) error {
Pk(userId).
Set("state", UserStateDisabled).
Update()
return err
if err != nil {
return err
}
return this.NotifyUpdate(tx, userId)
}
// FindEnabledUser 查找启用的用户
@@ -190,15 +199,6 @@ func (this *UserDAO) UpdateUser(tx *dbs.Tx, userId int64, username string, passw
return errors.New("invalid userId")
}
// 是否启用变化
oldIsOn, err := this.Query(tx).
Pk(userId).
Result("isOn").
FindBoolCol()
if err != nil {
return err
}
var op = NewUserOperator()
op.Id = userId
op.Username = username
@@ -212,16 +212,12 @@ func (this *UserDAO) UpdateUser(tx *dbs.Tx, userId int64, username string, passw
op.Remark = remark
op.ClusterId = nodeClusterId
op.IsOn = isOn
err = this.Save(tx, op)
err := this.Save(tx, op)
if err != nil {
return err
}
if oldIsOn != isOn {
return SharedServerDAO.NotifyUserClustersChange(tx, userId)
}
return nil
return this.NotifyUpdate(tx, userId)
}
// UpdateUserInfo 修改用户基本信息
@@ -529,5 +525,65 @@ func (this *UserDAO) UpdateUserIsVerified(tx *dbs.Tx, userId int64, isRejected b
op.IsRejected = isRejected
op.RejectReason = rejectReason
op.IsVerified = true
return this.Save(tx, op)
err := this.Save(tx, op)
if err != nil {
return err
}
return this.NotifyUpdate(tx, userId)
}
// RenewUserServersState 更新用户服务状态
func (this *UserDAO) RenewUserServersState(tx *dbs.Tx, userId int64) (bool, error) {
oldServersEnabled, err := this.Query(tx).
Pk(userId).
Result("serversEnabled").
FindBoolCol()
if err != nil {
return false, err
}
newServersEnabled, err := this.CheckUserServersEnabled(tx, userId)
if err != nil {
return false, err
}
if oldServersEnabled != newServersEnabled {
err = this.Query(tx).
Pk(userId).
Set("serversEnabled", newServersEnabled).
UpdateQuickly()
if err != nil {
return false, err
}
// 创建变更通知
clusterIds, err := SharedServerDAO.FindUserServerClusterIds(tx, userId)
if err != nil {
return false, err
}
for _, clusterId := range clusterIds {
err = SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, userId, 0, NodeTaskTypeUserServersStateChanged)
if err != nil {
return false, err
}
}
}
return newServersEnabled, nil
}
// NotifyUpdate 用户变更通知
func (this *UserDAO) NotifyUpdate(tx *dbs.Tx, userId int64) error {
if userId <= 0 {
return nil
}
// 更新用户服务状态
_, err := this.RenewUserServersState(tx, userId)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,27 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package models
import "github.com/iwind/TeaGo/dbs"
// CheckUserServersEnabled 判断用户是否可用服务功能
func (this *UserDAO) CheckUserServersEnabled(tx *dbs.Tx, userId int64) (isEnabled bool, err error) {
// 是否已删除、未启用、已拒绝
one, err := this.Query(tx).
Result("id", "isRejected", "state", "isOn").
Pk(userId).
Find()
if err != nil {
return false, err
}
if one == nil {
return false, nil
}
var user = one.(*User)
if user.State != UserStateEnabled || !user.IsOn || user.IsRejected {
return false, nil
}
return true, nil
}

View File

@@ -1,6 +1,7 @@
package models
package models_test
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
@@ -9,7 +10,7 @@ import (
)
func TestUserDAO_UpdateUserFeatures(t *testing.T) {
var dao = NewUserDAO()
var dao = models.NewUserDAO()
var tx *dbs.Tx
err := dao.UpdateUsersFeatures(tx, []string{
userconfigs.UserFeatureCodeServerACME,
@@ -18,3 +19,17 @@ func TestUserDAO_UpdateUserFeatures(t *testing.T) {
t.Fatal(err)
}
}
func TestUserDAO_CheckUserServersEnabled(t *testing.T) {
dbs.NotifyReady()
var dao = models.NewUserDAO()
var tx *dbs.Tx
for _, userId := range []int64{1, 2, 1000000} {
isEnabled, err := dao.CheckUserServersEnabled(tx, userId)
if err != nil {
t.Fatal(err)
}
t.Log("user:", userId, "isEnabled:", isEnabled)
}
}

View File

@@ -28,6 +28,9 @@ type User struct {
IsVerified bool `field:"isVerified"` // 是否验证通过
RequirePlans uint8 `field:"requirePlans"` // 是否需要购买套餐
Modules dbs.JSON `field:"modules"` // 用户模块
PriceType string `field:"priceType"` // 计费类型traffic|bandwidth
PricePeriod string `field:"pricePeriod"` // 结算周期
ServersEnabled uint8 `field:"serversEnabled"` // 是否禁用所有服务
}
type UserOperator struct {
@@ -55,6 +58,9 @@ type UserOperator struct {
IsVerified any // 是否验证通过
RequirePlans any // 是否需要购买套餐
Modules any // 用户模块
PriceType any // 计费类型traffic|bandwidth
PricePeriod any // 结算周期
ServersEnabled any // 是否禁用所有服务
}
func NewUserOperator() *UserOperator {

View File

@@ -300,7 +300,7 @@ func (this *UserNodeDAO) CountAllEnabledUserNodesWithSSLPolicyIds(tx *dbs.Tx, ss
if len(sslPolicyIds) == 0 {
return
}
policyStringIds := []string{}
var policyStringIds = []string{}
for _, policyId := range sslPolicyIds {
policyStringIds = append(policyStringIds, strconv.FormatInt(policyId, 10))
}
@@ -310,3 +310,21 @@ func (this *UserNodeDAO) CountAllEnabledUserNodesWithSSLPolicyIds(tx *dbs.Tx, ss
Param("policyIds", strings.Join(policyStringIds, ",")).
Count()
}
// FindUserNodeAccessAddr 获取用户节点访问地址
func (this *UserNodeDAO) FindUserNodeAccessAddr(tx *dbs.Tx) (string, error) {
nodes, err := this.ListEnabledUserNodes(tx, 0, 100)
if err != nil {
return "", err
}
for _, node := range nodes {
addrs, err := node.DecodeAccessAddrStrings()
if err != nil {
continue
}
if len(addrs) > 0 {
return addrs[0], nil
}
}
return "", nil
}

View File

@@ -22,21 +22,21 @@ type UserNode struct {
}
type UserNodeOperator struct {
Id interface{} // ID
IsOn interface{} // 是否启用
UniqueId interface{} // 唯一ID
Secret interface{} // 密钥
Name interface{} // 名称
Description interface{} // 描述
Http interface{} // 监听的HTTP配置
Https interface{} // 监听的HTTPS配置
AccessAddrs interface{} // 外部访问地址
Order interface{} // 排序
State interface{} // 状态
CreatedAt interface{} // 创建时间
AdminId interface{} // 管理员ID
Weight interface{} // 权重
Status interface{} // 运行状态
Id any // ID
IsOn any // 是否启用
UniqueId any // 唯一ID
Secret any // 密钥
Name any // 名称
Description any // 描述
Http any // 监听的HTTP配置
Https any // 监听的HTTPS配置
AccessAddrs any // 外部访问地址
Order any // 排序
State any // 状态
CreatedAt any // 创建时间
AdminId any // 管理员ID
Weight any // 权重
Status any // 运行状态
}
func NewUserNodeOperator() *UserNodeOperator {

View File

@@ -1,3 +1,5 @@
//go:build !plus
package models
import (
@@ -7,8 +9,6 @@ import (
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
const (
@@ -112,59 +112,11 @@ func (this *UserPlanDAO) FindUserPlanWithoutState(tx *dbs.Tx, userPlanId int64,
// CountAllEnabledUserPlans 计算套餐数量
func (this *UserPlanDAO) CountAllEnabledUserPlans(tx *dbs.Tx, userId int64, isAvailable bool, isExpired bool, expiringDays int32) (int64, error) {
var query = this.Query(tx).
State(UserPlanStateEnabled).
Where("planId IN (SELECT id FROM " + SharedPlanDAO.Table + " WHERE state=1)")
if userId > 0 {
query.Attr("userId", userId)
} else {
query.Where("userId IN (SELECT id FROM " + SharedUserDAO.Table + " WHERE state=1)")
}
var today = timeutil.Format("Y-m-d")
if isAvailable {
query.Gte("dayTo", today)
}
if isExpired {
query.Lt("dayTo", today)
}
if expiringDays > 0 {
var expiringDay = timeutil.Format("Y-m-d", time.Now().AddDate(0, 0, int(expiringDays)))
query.Gte("dayTo", today)
query.Lte("dayTo", expiringDay)
}
return query.Count()
return 0, nil
}
// ListEnabledUserPlans 列出单页套餐
func (this *UserPlanDAO) ListEnabledUserPlans(tx *dbs.Tx, userId int64, isAvailable bool, isExpired bool, expiringDays int32, offset int64, size int64) (result []*UserPlan, err error) {
var query = this.Query(tx).
State(UserPlanStateEnabled).
Where("planId IN (SELECT id FROM " + SharedPlanDAO.Table + " WHERE state=1)")
if userId > 0 {
query.Attr("userId", userId)
} else {
query.Where("userId IN (SELECT id FROM " + SharedUserDAO.Table + " WHERE state=1)")
}
var today = timeutil.Format("Y-m-d")
if isAvailable {
query.Gte("dayTo", today)
}
if isExpired {
query.Lt("dayTo", today)
}
if expiringDays > 0 {
var expiringDay = timeutil.Format("Y-m-d", time.Now().AddDate(0, 0, int(expiringDays)))
query.Gte("dayTo", today)
query.Lte("dayTo", expiringDay)
}
_, err = query.
DescPk().
Offset(offset).
Limit(size).
Slice(&result).
FindAll()
return
}
@@ -215,20 +167,6 @@ func (this *UserPlanDAO) UpdateUserPlanDayTo(tx *dbs.Tx, userPlanId int64, dayTo
// FindAllEnabledPlansForServer 列出服务可用的套餐
func (this *UserPlanDAO) FindAllEnabledPlansForServer(tx *dbs.Tx, userId int64, serverId int64) (result []*UserPlan, err error) {
var query = this.Query(tx).
State(UserPlanStateEnabled).
Attr("userId", userId).
Where("planId IN (SELECT id FROM " + SharedPlanDAO.Table + " WHERE state=1)")
if serverId > 0 {
query.Where("id NOT IN (SELECT userPlanId FROM " + SharedServerDAO.Table + " WHERE state=1 AND id!=:serverId)")
query.Param("serverId", serverId)
} else {
query.Where("id NOT IN (SELECT userPlanId FROM " + SharedServerDAO.Table + " WHERE state=1)")
}
_, err = query.
DescPk().
Slice(&result).
FindAll()
return
}

View File

@@ -0,0 +1,38 @@
package models
import "github.com/iwind/TeaGo/dbs"
// UserTrafficBill 用户流量/带宽账单
type UserTrafficBill struct {
Id uint64 `field:"id"` // ID
BillId uint64 `field:"billId"` // 主账单ID
RegionId uint32 `field:"regionId"` // 区域ID
Amount float64 `field:"amount"` // 金额
BandwidthMB float64 `field:"bandwidthMB"` // 带宽MB
BandwidthPercentile uint8 `field:"bandwidthPercentile"` // 带宽百分位
TrafficGB float64 `field:"trafficGB"` // 流量GB
TrafficPackageGB float64 `field:"trafficPackageGB"` // 使用的流量包GB
UserTrafficPackageIds dbs.JSON `field:"userTrafficPackageIds"` // 使用的流量包ID
PricePerUnit float64 `field:"pricePerUnit"` // 单位价格
PriceType string `field:"priceType"` // 计费方式traffic|bandwidth
State uint8 `field:"state"` // 状态
}
type UserTrafficBillOperator struct {
Id any // ID
BillId any // 主账单ID
RegionId any // 区域ID
Amount any // 金额
BandwidthMB any // 带宽MB
BandwidthPercentile any // 带宽百分位
TrafficGB any // 流量GB
TrafficPackageGB any // 使用的流量包GB
UserTrafficPackageIds any // 使用的流量包ID
PricePerUnit any // 单位价格
PriceType any // 计费方式traffic|bandwidth
State any // 状态
}
func NewUserTrafficBillOperator() *UserTrafficBillOperator {
return &UserTrafficBillOperator{}
}

View File

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

View File

@@ -0,0 +1,63 @@
package models
import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
)
const (
UserTrafficPackageStateEnabled = 1 // 已启用
UserTrafficPackageStateDisabled = 0 // 已禁用
)
type UserTrafficPackageDAO dbs.DAO
func NewUserTrafficPackageDAO() *UserTrafficPackageDAO {
return dbs.NewDAO(&UserTrafficPackageDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeUserTrafficPackages",
Model: new(UserTrafficPackage),
PkName: "id",
},
}).(*UserTrafficPackageDAO)
}
var SharedUserTrafficPackageDAO *UserTrafficPackageDAO
func init() {
dbs.OnReady(func() {
SharedUserTrafficPackageDAO = NewUserTrafficPackageDAO()
})
}
// EnableUserTrafficPackage 启用条目
func (this *UserTrafficPackageDAO) EnableUserTrafficPackage(tx *dbs.Tx, id uint64) error {
_, err := this.Query(tx).
Pk(id).
Set("state", UserTrafficPackageStateEnabled).
Update()
return err
}
// DisableUserTrafficPackage 禁用条目
func (this *UserTrafficPackageDAO) DisableUserTrafficPackage(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
Set("state", UserTrafficPackageStateDisabled).
Update()
return err
}
// FindEnabledUserTrafficPackage 查找启用中的条目
func (this *UserTrafficPackageDAO) FindEnabledUserTrafficPackage(tx *dbs.Tx, id int64) (*UserTrafficPackage, error) {
result, err := this.Query(tx).
Pk(id).
State(UserTrafficPackageStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*UserTrafficPackage), err
}

View File

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

View File

@@ -0,0 +1,40 @@
package models
// UserTrafficPackage 用户购买的流量包
type UserTrafficPackage struct {
Id uint64 `field:"id"` // ID
AdminId uint32 `field:"adminId"` // 管理员ID
UserId uint64 `field:"userId"` // 用户ID
PackageId uint32 `field:"packageId"` // 流量包ID
TotalBytes uint64 `field:"totalBytes"` // 总字节数
UsedBytes uint64 `field:"usedBytes"` // 已使用字节数
RegionId uint32 `field:"regionId"` // 区域ID
PeriodId uint32 `field:"periodId"` // 有效期ID
PeriodCount uint32 `field:"periodCount"` // 有效期数量
PeriodUnit string `field:"periodUnit"` // 有效期单位
DayFrom string `field:"dayFrom"` // 开始日期
DayTo string `field:"dayTo"` // 结束日期
CreatedAt uint64 `field:"createdAt"` // 创建时间
State uint8 `field:"state"` // 状态
}
type UserTrafficPackageOperator struct {
Id any // ID
AdminId any // 管理员ID
UserId any // 用户ID
PackageId any // 流量包ID
TotalBytes any // 总字节数
UsedBytes any // 已使用字节数
RegionId any // 区域ID
PeriodId any // 有效期ID
PeriodCount any // 有效期数量
PeriodUnit any // 有效期单位
DayFrom any // 开始日期
DayTo any // 结束日期
CreatedAt any // 创建时间
State any // 状态
}
func NewUserTrafficPackageOperator() *UserTrafficPackageOperator {
return &UserTrafficPackageOperator{}
}

View File

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

73
internal/db/utils/disk.go Normal file
View File

@@ -0,0 +1,73 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package dbutils
import (
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/dbs"
"golang.org/x/sys/unix"
"time"
)
const minFreeSpaceGB = 3
var HasFreeSpace = true
var IsLocalDatabase = false
var LocalDatabaseDataDir = ""
func init() {
var ticker = time.NewTicker(5 * time.Minute)
dbs.OnReadyDone(func() {
goman.New(func() {
for range ticker.C {
HasFreeSpace = CheckHasFreeSpace()
}
})
})
}
// CheckHasFreeSpace 检查当前数据库是否有剩余空间
func CheckHasFreeSpace() bool {
db, _ := dbs.Default()
if db == nil {
return false
}
config, _ := db.Config()
if config == nil {
return false
}
dsnConfig, _ := mysql.ParseDSN(config.Dsn)
if dsnConfig == nil {
return false
}
if IsLocalAddr(dsnConfig.Addr) {
IsLocalDatabase = true
// only for local database
one, err := db.FindOne("SHOW VARIABLES WHERE variable_name='datadir'")
if err != nil || len(one) == 0 {
return true
}
var dir = one.GetString("Value")
if len(dir) == 0 {
return true
}
LocalDatabaseDataDir = dir
var stat unix.Statfs_t
err = unix.Statfs(dir, &stat)
if err != nil {
return true
}
var availableSpace = (stat.Bavail * uint64(stat.Bsize)) / (1 << 30) // GB
return availableSpace > minFreeSpaceGB
}
return true
}

View File

@@ -0,0 +1,14 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package dbutils_test
import (
dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils"
_ "github.com/iwind/TeaGo/bootstrap"
"testing"
)
func TestHasFreeSpace(t *testing.T) {
t.Log(dbutils.CheckHasFreeSpace())
t.Log(dbutils.LocalDatabaseDataDir)
}

View File

@@ -2,6 +2,7 @@ package dbutils
import (
"github.com/iwind/TeaGo/dbs"
"net"
"strings"
)
@@ -69,3 +70,26 @@ func SetGlobalVarMax(db *dbs.DB, variableName string, maxValue int) error {
}
return nil
}
// IsLocalAddr 是否为本地数据库
func IsLocalAddr(addr string) bool {
var host = addr
if strings.Contains(addr, ":") {
host, _, _ = net.SplitHostPort(addr)
if len(host) == 0 {
host = addr
}
}
if host == "127.0.0.1" || host == "::1" || host == "localhost" {
return true
}
interfaceAddrs, _ := net.InterfaceAddrs()
for _, interfaceAddr := range interfaceAddrs {
if strings.HasPrefix(interfaceAddr.String(), host+"/") {
return true
}
}
return false
}

View File

@@ -4,6 +4,7 @@ package dbutils_test
import (
dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils"
"github.com/iwind/TeaGo/assert"
"testing"
)
@@ -12,3 +13,13 @@ func TestQuoteLike(t *testing.T) {
t.Log(s + " => " + dbutils.QuoteLike(s))
}
}
func TestIsLocalAddr(t *testing.T) {
var a = assert.NewAssertion(t)
a.IsTrue(dbutils.IsLocalAddr("127.0.0.1"))
a.IsTrue(dbutils.IsLocalAddr("localhost"))
a.IsTrue(dbutils.IsLocalAddr("::1"))
a.IsTrue(dbutils.IsLocalAddr("127.0.0.1:3306"))
a.IsFalse(dbutils.IsLocalAddr("192.168.2.200"))
a.IsFalse(dbutils.IsLocalAddr("192.168.2.200:3306"))
}

View File

@@ -0,0 +1,18 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package dnspod
type BaseResponse struct {
Status struct {
Code string `json:"code"`
Message string `json:"message"`
} `json:"status"`
}
func (this *BaseResponse) IsOk() bool {
return this.Status.Code == "1"
}
func (this *BaseResponse) LastError() (code string, message string) {
return this.Status.Code, this.Status.Message
}

View File

@@ -0,0 +1,13 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package dnspod
type DomainInfoResponse struct {
BaseResponse
Domain struct {
Id any `json:"id"`
Name string `json:"name"`
Grade string `json:"grade"`
} `json:"domain"`
}

View File

@@ -0,0 +1,17 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package dnspod
type DomainListResponse struct {
BaseResponse
Info struct {
DomainTotal int `json:"domain_total"`
AllTotal int `json:"all_total"`
MineTotal int `json:"mine_total"`
} `json:"info"`
Domains []struct {
Name string `json:"name"`
} `json:"domains"`
}

View File

@@ -0,0 +1,8 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package dnspod
type ResponseInterface interface {
IsOk() bool
LastError() (code string, message string)
}

View File

@@ -0,0 +1,13 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package dnspod
type RecordCreateResponse struct {
BaseResponse
Record struct {
Id any `json:"id"`
Name string `json:"name"`
Status string `json:"status"`
} `json:"record"`
}

View File

@@ -0,0 +1,9 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package dnspod
type RecordLineResponse struct {
BaseResponse
Lines []string `json:"lines"`
}

View File

@@ -0,0 +1,23 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package dnspod
type RecordListResponse struct {
BaseResponse
Info struct {
SubDomains string `json:"sub_domains"`
RecordTotal string `json:"record_total"`
RecordsNum string `json:"records_num"`
} `json:"info"`
Records []struct {
Id any `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Value string `json:"value"`
Line string `json:"line"`
LineId string `json:"line_id"`
TTL string `json:"ttl"`
} `json:"records"`
}

View File

@@ -0,0 +1,14 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package dnspod
type RecordModifyResponse struct {
BaseResponse
Record struct {
Id any `json:"id"`
Name string `json:"name"`
Value string `json:"value"`
Status string `json:"status"`
} `json:"record"`
}

View File

@@ -0,0 +1,7 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package dnspod
type RecordRemoveResponse struct {
BaseResponse
}

View File

@@ -17,3 +17,26 @@ type Record struct {
Route string `json:"route"`
TTL int32 `json:"ttl"`
}
func (this *Record) Clone() *Record {
return &Record{
Id: this.Id,
Name: this.Name,
Type: this.Type,
Value: this.Value,
Route: this.Route,
TTL: this.TTL,
}
}
func (this *Record) Copy(anotherRecord *Record) {
if anotherRecord == nil {
return
}
this.Id = anotherRecord.Id
this.Name = anotherRecord.Name
this.Type = anotherRecord.Type
this.Value = anotherRecord.Value
this.Route = anotherRecord.Route
this.TTL = anotherRecord.TTL
}

View File

@@ -0,0 +1,201 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package dnsclients
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/dnstypes"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/types"
"sync"
"time"
)
func init() {
dbs.OnReadyDone(func() {
go func() {
var ticker = time.NewTicker(1 * time.Hour)
for range ticker.C {
sharedDomainRecordsCache.Clean()
}
}()
})
}
type recordList struct {
version int64
updatedAt int64
records []*dnstypes.Record
}
var sharedDomainRecordsCache = NewDomainRecordsCache()
// DomainRecordsCache 域名记录缓存
type DomainRecordsCache struct {
domainRecordsMap map[string]*recordList // domain@providerId => record
locker sync.Mutex
}
func NewDomainRecordsCache() *DomainRecordsCache {
return &DomainRecordsCache{
domainRecordsMap: map[string]*recordList{},
}
}
// WriteDomainRecords 写入域名记录缓存
func (this *DomainRecordsCache) WriteDomainRecords(providerId int64, domain string, records []*dnstypes.Record) {
if providerId <= 0 || len(domain) == 0 {
return
}
domain = types.String(providerId) + "@" + domain
this.locker.Lock()
defer this.locker.Unlock()
// 版本号
var key = "DomainRecordsCache" + "@" + types.String(providerId) + "@" + domain
version, err := models.SharedSysLockerDAO.Increase(nil, key, 1)
if err != nil {
remotelogs.Error("dnsclients.BaseProvider", "WriteDomainRecordsCache: "+err.Error())
return
}
var clonedRecords = []*dnstypes.Record{}
for _, record := range records {
clonedRecords = append(clonedRecords, record)
}
this.domainRecordsMap[domain] = &recordList{
version: version,
updatedAt: time.Now().Unix(),
records: clonedRecords,
}
}
// QueryDomainRecord 从缓存中读取域名记录
func (this *DomainRecordsCache) QueryDomainRecord(providerId int64, domain string, recordName string, recordType string) (record *dnstypes.Record, hasRecords bool, ok bool) {
if providerId <= 0 || len(domain) == 0 {
return
}
domain = types.String(providerId) + "@" + domain
this.locker.Lock()
defer this.locker.Unlock()
// check version
var key = "DomainRecordsCache" + "@" + types.String(providerId) + "@" + domain
version, err := models.SharedSysLockerDAO.Read(nil, key)
if err != nil {
remotelogs.Error("dnsclients.BaseProvider", "ReadDomainRecordsCache: "+err.Error())
return
}
// find list
list, recordsOk := this.domainRecordsMap[domain]
if !recordsOk {
return
}
if version != list.version {
delete(this.domainRecordsMap, domain)
return
}
// check timestamp
if list.updatedAt < time.Now().Unix()-86400 /** 缓存有效期为一天 **/ {
delete(this.domainRecordsMap, domain)
return
}
hasRecords = true
for _, r := range list.records {
if r.Name == recordName && r.Type == recordType {
return r, true, true
}
}
return
}
// DeleteDomainRecord 删除域名记录缓存
func (this *DomainRecordsCache) DeleteDomainRecord(providerId int64, domain string, recordId string) {
if providerId <= 0 || len(domain) == 0 || len(recordId) == 0 {
return
}
domain = types.String(providerId) + "@" + domain
this.locker.Lock()
defer this.locker.Unlock()
list, ok := this.domainRecordsMap[domain]
if !ok {
return
}
var found = false
var newRecords = []*dnstypes.Record{}
for _, record := range list.records {
if record.Id == recordId {
found = true
continue
}
newRecords = append(newRecords, record)
}
if found {
list.records = newRecords
}
}
// AddDomainRecord 添加域名记录缓存
func (this *DomainRecordsCache) AddDomainRecord(providerId int64, domain string, record *dnstypes.Record) {
if providerId <= 0 || len(domain) == 0 || record == nil || len(record.Id) == 0 {
return
}
domain = types.String(providerId) + "@" + domain
this.locker.Lock()
defer this.locker.Unlock()
list, ok := this.domainRecordsMap[domain]
if ok {
list.records = append(list.records, record.Clone())
}
// 如果完全没有记录,则不保存
}
// UpdateDomainRecord 修改域名记录缓存
func (this *DomainRecordsCache) UpdateDomainRecord(providerId int64, domain string, record *dnstypes.Record) {
if providerId <= 0 || len(domain) == 0 || record == nil || len(record.Id) == 0 {
return
}
domain = types.String(providerId) + "@" + domain
this.locker.Lock()
defer this.locker.Unlock()
list, ok := this.domainRecordsMap[domain]
if !ok {
return
}
for _, r := range list.records {
if r.Id == record.Id {
r.Copy(record)
break
}
}
}
// Clean 清除过期缓存
func (this *DomainRecordsCache) Clean() {
this.locker.Lock()
defer this.locker.Unlock()
for domain, list := range this.domainRecordsMap {
if list.updatedAt < time.Now().Unix()-86400 {
delete(this.domainRecordsMap, domain)
}
}
}

View File

@@ -0,0 +1,58 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package dnsclients_test
import (
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients"
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/dnstypes"
"github.com/iwind/TeaGo/dbs"
"testing"
)
func TestDomainRecordsCache_WriteDomainRecords(t *testing.T) {
dbs.NotifyReady()
var cache = dnsclients.NewDomainRecordsCache()
cache.WriteDomainRecords(1, "a", []*dnstypes.Record{
{
Id: "1",
Name: "hello",
Type: "A",
Value: "192.168.1.100",
},
})
//time.Sleep(30 * time.Second)
{
t.Log(cache.QueryDomainRecord(1, "a", "hello", "A"))
}
{
t.Log(cache.QueryDomainRecord(1, "a", "hello", "AAAA"))
}
{
t.Log(cache.QueryDomainRecord(1, "a", "hello2", "A"))
}
t.Log("======")
cache.DeleteDomainRecord(1, "a", "2")
cache.UpdateDomainRecord(1, "a", &dnstypes.Record{
Id: "1",
Name: "hello2",
Type: "A",
Value: "192.168.1.200",
})
{
t.Log(cache.QueryDomainRecord(1, "a", "hello2", "A"))
}
t.Log("======")
cache.AddDomainRecord(1, "a", &dnstypes.Record{
Id: "2",
Name: "hello",
Type: "AAAA",
Value: "::1",
})
{
t.Log(cache.QueryDomainRecord(1, "a", "hello", "AAAA"))
}
}

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