Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6906b3094b | ||
|
|
aec28b5087 | ||
|
|
b59ed1f73e | ||
|
|
2a83f61bdd | ||
|
|
4f21d60ca4 | ||
|
|
0a6111b2e5 | ||
|
|
4b425e1698 | ||
|
|
bee7da807b | ||
|
|
a906a7db06 | ||
|
|
ea62cf0ff7 | ||
|
|
72e0c55f5d | ||
|
|
3a88f23181 | ||
|
|
967c9080fb | ||
|
|
f44b9434ad | ||
|
|
e21a3c5f8c | ||
|
|
88dae56b6c | ||
|
|
40c3475306 | ||
|
|
318c8dd566 | ||
|
|
a5f30b1573 | ||
|
|
a8ec959c70 | ||
|
|
c393b2f480 | ||
|
|
548f56f8f0 | ||
|
|
9aa71365b9 | ||
|
|
292fb72a26 | ||
|
|
e5315c3b8d | ||
|
|
a4dd7bb75a | ||
|
|
53ef0f3fb2 | ||
|
|
6af8bff802 | ||
|
|
d849f7440a | ||
|
|
425c0ec44c | ||
|
|
aad0b01581 | ||
|
|
bc2c3dfa0b | ||
|
|
0fe76430c6 | ||
|
|
979ff4c44e | ||
|
|
b0b6b5984f | ||
|
|
5d4da6cccb | ||
|
|
912ffa062f | ||
|
|
1db4661c75 | ||
|
|
f7dd9e3f39 | ||
|
|
0cc74b920e | ||
|
|
51c3807d01 | ||
|
|
c2c42ca2b7 | ||
|
|
2a6db6ebfe | ||
|
|
30d8edbdcf | ||
|
|
177afafe12 | ||
|
|
98765b6e2a | ||
|
|
e4e0aab010 | ||
|
|
ed87b4e2a9 | ||
|
|
337eb36d25 | ||
|
|
c44e40d72d | ||
|
|
2e8ba831a1 | ||
|
|
a706c2a5a5 |
@@ -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"})
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
13
go.mod
@@ -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
32
go.sum
@@ -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=
|
||||
|
||||
@@ -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 {
|
||||
|
||||
24
internal/acme/providers_ext.go
Normal file
24
internal/acme/providers_ext.go
Normal 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\"按钮生成。",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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 + "'")
|
||||
}
|
||||
|
||||
@@ -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 + ")")
|
||||
}
|
||||
}
|
||||
|
||||
// 开始事务
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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更新
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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"` // 唯一ID:nodeId@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{} // 唯一ID:nodeId@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 // 唯一ID:nodeId@type
|
||||
UpdatedAt any // 修改时间
|
||||
IsDone any // 是否已完成
|
||||
IsOk any // 是否已完成
|
||||
Error any // 错误信息
|
||||
IsNotified any // 是否已通知更新
|
||||
Version any // 版本
|
||||
}
|
||||
|
||||
func NewNodeTaskOperator() *NodeTaskOperator {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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个月的数据
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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更新
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
63
internal/db/models/traffic_package_dao.go
Normal file
63
internal/db/models/traffic_package_dao.go
Normal 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
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package accounts
|
||||
package models_test
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
24
internal/db/models/traffic_package_model.go
Normal file
24
internal/db/models/traffic_package_model.go
Normal 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{}
|
||||
}
|
||||
1
internal/db/models/traffic_package_model_ext.go
Normal file
1
internal/db/models/traffic_package_model_ext.go
Normal file
@@ -0,0 +1 @@
|
||||
package models
|
||||
63
internal/db/models/traffic_package_period_dao.go
Normal file
63
internal/db/models/traffic_package_period_dao.go
Normal 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
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package accounts
|
||||
package models_test
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
24
internal/db/models/traffic_package_period_model.go
Normal file
24
internal/db/models/traffic_package_period_model.go
Normal 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{}
|
||||
}
|
||||
1
internal/db/models/traffic_package_period_model_ext.go
Normal file
1
internal/db/models/traffic_package_period_model_ext.go
Normal file
@@ -0,0 +1 @@
|
||||
package models
|
||||
28
internal/db/models/traffic_package_price_dao.go
Normal file
28
internal/db/models/traffic_package_price_dao.go
Normal 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()
|
||||
})
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package models
|
||||
package models_test
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
24
internal/db/models/traffic_package_price_model.go
Normal file
24
internal/db/models/traffic_package_price_model.go
Normal 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{}
|
||||
}
|
||||
1
internal/db/models/traffic_package_price_model_ext.go
Normal file
1
internal/db/models/traffic_package_price_model_ext.go
Normal file
@@ -0,0 +1 @@
|
||||
package models
|
||||
@@ -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个月的数据
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
27
internal/db/models/user_dao_ext.go
Normal file
27
internal/db/models/user_dao_ext.go
Normal 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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
38
internal/db/models/user_traffic_bill_model.go
Normal file
38
internal/db/models/user_traffic_bill_model.go
Normal 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{}
|
||||
}
|
||||
1
internal/db/models/user_traffic_bill_model_ext.go
Normal file
1
internal/db/models/user_traffic_bill_model_ext.go
Normal file
@@ -0,0 +1 @@
|
||||
package models
|
||||
63
internal/db/models/user_traffic_package_dao.go
Normal file
63
internal/db/models/user_traffic_package_dao.go
Normal 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
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package models
|
||||
package models_test
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
40
internal/db/models/user_traffic_package_model.go
Normal file
40
internal/db/models/user_traffic_package_model.go
Normal 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{}
|
||||
}
|
||||
1
internal/db/models/user_traffic_package_model_ext.go
Normal file
1
internal/db/models/user_traffic_package_model_ext.go
Normal file
@@ -0,0 +1 @@
|
||||
package models
|
||||
73
internal/db/utils/disk.go
Normal file
73
internal/db/utils/disk.go
Normal 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
|
||||
}
|
||||
14
internal/db/utils/disk_test.go
Normal file
14
internal/db/utils/disk_test.go
Normal 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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"))
|
||||
}
|
||||
|
||||
18
internal/dnsclients/dnspod/response_base.go
Normal file
18
internal/dnsclients/dnspod/response_base.go
Normal 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
|
||||
}
|
||||
13
internal/dnsclients/dnspod/response_domain_info.go
Normal file
13
internal/dnsclients/dnspod/response_domain_info.go
Normal 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"`
|
||||
}
|
||||
17
internal/dnsclients/dnspod/response_domain_list.go
Normal file
17
internal/dnsclients/dnspod/response_domain_list.go
Normal 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"`
|
||||
}
|
||||
8
internal/dnsclients/dnspod/response_interface.go
Normal file
8
internal/dnsclients/dnspod/response_interface.go
Normal 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)
|
||||
}
|
||||
13
internal/dnsclients/dnspod/response_record_create.go
Normal file
13
internal/dnsclients/dnspod/response_record_create.go
Normal 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"`
|
||||
}
|
||||
9
internal/dnsclients/dnspod/response_record_line.go
Normal file
9
internal/dnsclients/dnspod/response_record_line.go
Normal 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"`
|
||||
}
|
||||
23
internal/dnsclients/dnspod/response_record_list.go
Normal file
23
internal/dnsclients/dnspod/response_record_list.go
Normal 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"`
|
||||
}
|
||||
14
internal/dnsclients/dnspod/response_record_modify.go
Normal file
14
internal/dnsclients/dnspod/response_record_modify.go
Normal 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"`
|
||||
}
|
||||
7
internal/dnsclients/dnspod/response_record_remove.go
Normal file
7
internal/dnsclients/dnspod/response_record_remove.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
201
internal/dnsclients/domain_records_cache.go
Normal file
201
internal/dnsclients/domain_records_cache.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
58
internal/dnsclients/domain_records_cahce_test.go
Normal file
58
internal/dnsclients/domain_records_cahce_test.go
Normal 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
Reference in New Issue
Block a user