Compare commits
429 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b1a9f9a45 | ||
|
|
7a86ecb44b | ||
|
|
89a113431a | ||
|
|
ce62d0769b | ||
|
|
a72dc2e011 | ||
|
|
91ca2d6b6b | ||
|
|
0de6fa5ce8 | ||
|
|
84e2628769 | ||
|
|
fc28798c9f | ||
|
|
24c21c5513 | ||
|
|
8445e811a5 | ||
|
|
d54621d500 | ||
|
|
ef045e90f2 | ||
|
|
5205136809 | ||
|
|
179a7760fa | ||
|
|
640e69524c | ||
|
|
3620ab3dc6 | ||
|
|
5789b1afb9 | ||
|
|
6f9f9dfb6f | ||
|
|
171bafce6a | ||
|
|
7c7fecab26 | ||
|
|
7afdf5a2d9 | ||
|
|
ee9718ac77 | ||
|
|
15e10182da | ||
|
|
4341c5b738 | ||
|
|
cf9b31b8eb | ||
|
|
564d440cd1 | ||
|
|
bad9231ab3 | ||
|
|
a78333c490 | ||
|
|
ace44173ec | ||
|
|
5155ce97af | ||
|
|
e2bf3ba1a4 | ||
|
|
9abc65bd2b | ||
|
|
a99156ea49 | ||
|
|
d35db163ae | ||
|
|
e7b0f0df90 | ||
|
|
eab09fa37a | ||
|
|
ec3f89ecf5 | ||
|
|
d58106c7ca | ||
|
|
5da924118d | ||
|
|
51e37f0c52 | ||
|
|
7e9e764322 | ||
|
|
75e41fff4d | ||
|
|
8a9842edaf | ||
|
|
4e8629d74a | ||
|
|
2e02aa556e | ||
|
|
974c95c20a | ||
|
|
3c0e6a900b | ||
|
|
fb420f8fce | ||
|
|
705e80f6c7 | ||
|
|
9a4be1f5a7 | ||
|
|
7d82c3abf6 | ||
|
|
55034efa87 | ||
|
|
3ff45d6f49 | ||
|
|
2b2c285c90 | ||
|
|
29ad1bc741 | ||
|
|
a263ab5938 | ||
|
|
6b34d32a8f | ||
|
|
3ff6ca5905 | ||
|
|
59ce71f72e | ||
|
|
49da653ed8 | ||
|
|
12db6910dc | ||
|
|
3ce1aa14b5 | ||
|
|
c67fbfa849 | ||
|
|
72b94c5035 | ||
|
|
01ea13d283 | ||
|
|
91378b26dd | ||
|
|
46dc74195b | ||
|
|
63316b6b23 | ||
|
|
1b8600e04d | ||
|
|
11141d022f | ||
|
|
8fd29bd81c | ||
|
|
820779e614 | ||
|
|
d45dc35ea6 | ||
|
|
b3280ee290 | ||
|
|
ff0e89aa5f | ||
|
|
6ff5ba67e8 | ||
|
|
e7474523ba | ||
|
|
0b928aed14 | ||
|
|
52dcd3da1f | ||
|
|
0260d6f735 | ||
|
|
364636ea61 | ||
|
|
12f816cb5e | ||
|
|
ef70d3465d | ||
|
|
a3a9e726cb | ||
|
|
0c65774c82 | ||
|
|
fa31e547a2 | ||
|
|
c406536511 | ||
|
|
35f48e4f5d | ||
|
|
851e982ab9 | ||
|
|
956186613f | ||
|
|
aeb2a0c4f9 | ||
|
|
4f05671af2 | ||
|
|
6b6170b183 | ||
|
|
29f2aa2654 | ||
|
|
ed234b58bc | ||
|
|
cfcad531f4 | ||
|
|
12ffdfd849 | ||
|
|
c2be7d70b8 | ||
|
|
efe723bc51 | ||
|
|
501559e3b8 | ||
|
|
de8b3c2195 | ||
|
|
31aeb21e09 | ||
|
|
f1cf89a78c | ||
|
|
d6df5e1c48 | ||
|
|
049b9b52dd | ||
|
|
c53259008e | ||
|
|
6e0a0a099d | ||
|
|
f4313de55f | ||
|
|
fd3823255c | ||
|
|
79823f4317 | ||
|
|
99fd015066 | ||
|
|
f63c2d867d | ||
|
|
eefb497c20 | ||
|
|
1c8564d817 | ||
|
|
1359997f48 | ||
|
|
4a52d43273 | ||
|
|
fb68de23c1 | ||
|
|
c1b7cf11c3 | ||
|
|
311751596c | ||
|
|
4132dee4bc | ||
|
|
8e886cd410 | ||
|
|
d6f896298d | ||
|
|
2ca8aa4b44 | ||
|
|
783052a0b4 | ||
|
|
5bda9e4b51 | ||
|
|
f7cbf051bd | ||
|
|
4d7e82d0a2 | ||
|
|
bf2ffeb15c | ||
|
|
7b3290800d | ||
|
|
eaebb6df5a | ||
|
|
acdddf5e12 | ||
|
|
1224758b5b | ||
|
|
5b32343a2c | ||
|
|
8afd21f2f4 | ||
|
|
3e76bf6e5a | ||
|
|
ed74adebac | ||
|
|
1400dff676 | ||
|
|
8e3360b368 | ||
|
|
e80441639c | ||
|
|
07bf994b68 | ||
|
|
344ef4d597 | ||
|
|
144b9b9519 | ||
|
|
38841d6d75 | ||
|
|
6d1603e253 | ||
|
|
6ce23c9913 | ||
|
|
fd8c9adbf7 | ||
|
|
ea40dc7534 | ||
|
|
37c8a8c955 | ||
|
|
2a0e4bba4b | ||
|
|
dd252b7b87 | ||
|
|
2de63863a7 | ||
|
|
5d00d53ea5 | ||
|
|
dcf04d64bd | ||
|
|
d36f7d01df | ||
|
|
a0ff24adb6 | ||
|
|
5597a0af6c | ||
|
|
c196a85a59 | ||
|
|
ba638d4e1d | ||
|
|
7f8abccd2a | ||
|
|
c0f540cc2c | ||
|
|
bd905ff1a9 | ||
|
|
3b8d1b4cd8 | ||
|
|
f86180b93c | ||
|
|
cfb1864fd2 | ||
|
|
1d0a66a156 | ||
|
|
b4d4f6460e | ||
|
|
e2de9799c0 | ||
|
|
9b9c6471f7 | ||
|
|
d30b10baee | ||
|
|
55f7189a1c | ||
|
|
0ff6fb002d | ||
|
|
a5d34565c5 | ||
|
|
3c17ba0a8b | ||
|
|
a8c0c64071 | ||
|
|
82ed22a464 | ||
|
|
b190479d44 | ||
|
|
ed7b586137 | ||
|
|
ea19635fe5 | ||
|
|
29cd7da6e4 | ||
|
|
4dd9c6398e | ||
|
|
3aae6b6b89 | ||
|
|
a91f0ac206 | ||
|
|
f60745794c | ||
|
|
da408bfe8e | ||
|
|
9a40100fd7 | ||
|
|
42c53b47dc | ||
|
|
9855829a3c | ||
|
|
5cf1f9bf33 | ||
|
|
7b1efe65d5 | ||
|
|
53c371f8d1 | ||
|
|
82d30ca958 | ||
|
|
eeec60c543 | ||
|
|
7b9e6fe5fb | ||
|
|
b14ab9ecd8 | ||
|
|
2854e1cd7c | ||
|
|
a11b3c0d8d | ||
|
|
724a8e99ee | ||
|
|
fc77a2d7ed | ||
|
|
97e7165dec | ||
|
|
06a27e9bbc | ||
|
|
121605324d | ||
|
|
6c088c304b | ||
|
|
fc86111039 | ||
|
|
77fde956ee | ||
|
|
6b81ffd074 | ||
|
|
eff3c77551 | ||
|
|
2089bac52f | ||
|
|
67760a53ba | ||
|
|
d4d7b1fff7 | ||
|
|
d121bc86a0 | ||
|
|
7a1bd29f6f | ||
|
|
f1af151080 | ||
|
|
f39d106bef | ||
|
|
d9c092cd31 | ||
|
|
7d1d138e42 | ||
|
|
89bd70819f | ||
|
|
15156b68e3 | ||
|
|
92a3b8f375 | ||
|
|
6a152b7775 | ||
|
|
ea915582c1 | ||
|
|
51b938b9c7 | ||
|
|
c2f559d48e | ||
|
|
a96c1434b6 | ||
|
|
d135442a52 | ||
|
|
0b8501a724 | ||
|
|
7fcc2b7dba | ||
|
|
1ea7fe0213 | ||
|
|
838d33648f | ||
|
|
6cf4a8ea3e | ||
|
|
15c40d6c96 | ||
|
|
6dfecd69b4 | ||
|
|
dbc97bc8de | ||
|
|
8308e2e83d | ||
|
|
8227138168 | ||
|
|
2227a14ba4 | ||
|
|
6137b44408 | ||
|
|
f654c65626 | ||
|
|
674574a6af | ||
|
|
f02ab1aae2 | ||
|
|
55527bba09 | ||
|
|
821b607ef2 | ||
|
|
afeee89a88 | ||
|
|
a983615464 | ||
|
|
bd596816d5 | ||
|
|
ca233c3573 | ||
|
|
fd1f990a0e | ||
|
|
1796bb8f96 | ||
|
|
3f5e4babc7 | ||
|
|
f508c16f92 | ||
|
|
ac0bbd0b99 | ||
|
|
9017176efb | ||
|
|
8b40634e74 | ||
|
|
cf476f79d6 | ||
|
|
fc38a6ab7e | ||
|
|
e4f0dafc1a | ||
|
|
b7fda0b9cc | ||
|
|
f4cc5aa087 | ||
|
|
f58724065d | ||
|
|
1e6b42c00c | ||
|
|
8d759a104b | ||
|
|
72d7ceb94e | ||
|
|
53f7a0b77e | ||
|
|
56000a8b8a | ||
|
|
ab7b2fee3a | ||
|
|
d768d46854 | ||
|
|
b86c9aad6f | ||
|
|
df667c6ee6 | ||
|
|
70331805d7 | ||
|
|
71dbf86572 | ||
|
|
0df358d70d | ||
|
|
93a17ced7c | ||
|
|
580d09ef99 | ||
|
|
fed725d45c | ||
|
|
350b514fc7 | ||
|
|
71d2671c04 | ||
|
|
5a22146309 | ||
|
|
ac2fb4c84b | ||
|
|
c25e3f18e0 | ||
|
|
d3e4f28c69 | ||
|
|
363892efb2 | ||
|
|
cf36559eea | ||
|
|
fa3e0ca6ab | ||
|
|
7229b0db34 | ||
|
|
23871804b1 | ||
|
|
5e00bfa4c1 | ||
|
|
473a2db335 | ||
|
|
c893de8af7 | ||
|
|
a8cf04d178 | ||
|
|
e94a7f9a77 | ||
|
|
ccf435ee8e | ||
|
|
7dc5c5f349 | ||
|
|
566c04f080 | ||
|
|
5a13c7663c | ||
|
|
1df6d579d7 | ||
|
|
20110495ab | ||
|
|
ce8d656d65 | ||
|
|
186fe3c365 | ||
|
|
a9ce2f45df | ||
|
|
5105af9918 | ||
|
|
378c485219 | ||
|
|
2465993e2c | ||
|
|
818c1c25a7 | ||
|
|
77c67ccd3f | ||
|
|
d855fdedde | ||
|
|
95c734c87a | ||
|
|
89ff1927b7 | ||
|
|
1a3aaf2846 | ||
|
|
7d25019abb | ||
|
|
4cfc7d5387 | ||
|
|
9245cf9cdb | ||
|
|
9e1e57dfd8 | ||
|
|
87f032bebd | ||
|
|
bda407fc3f | ||
|
|
3b2f6060b8 | ||
|
|
a15ad7dd04 | ||
|
|
a415ef6070 | ||
|
|
d94a822f52 | ||
|
|
9e3fb9cf66 | ||
|
|
84a9916d3e | ||
|
|
7d1ae0d242 | ||
|
|
3fbad22218 | ||
|
|
5fffc8a833 | ||
|
|
2c72d28c48 | ||
|
|
45ea803e7a | ||
|
|
edd4b59207 | ||
|
|
38ad11fda0 | ||
|
|
961d9b4dbc | ||
|
|
6436e83d9e | ||
|
|
b2d3d483e8 | ||
|
|
89ec81f6b1 | ||
|
|
5124169e28 | ||
|
|
fe4eb3928e | ||
|
|
514d968b20 | ||
|
|
3cce13e671 | ||
|
|
6d359b09f2 | ||
|
|
316b3485ca | ||
|
|
31c20122b4 | ||
|
|
7343d4bfac | ||
|
|
330d5a3d21 | ||
|
|
709845064f | ||
|
|
be5a89e513 | ||
|
|
80328b9b5f | ||
|
|
c6a09e131b | ||
|
|
f52ea4fa4f | ||
|
|
6cbda588f7 | ||
|
|
f9e7c3a2e0 | ||
|
|
c370dada11 | ||
|
|
8d71afc176 | ||
|
|
a39a114316 | ||
|
|
3f5c3568db | ||
|
|
f50ff00f0b | ||
|
|
7caa5bee75 | ||
|
|
bf5368929b | ||
|
|
ff6d8d9ba3 | ||
|
|
877d42a271 | ||
|
|
21c51cbdc8 | ||
|
|
45e4eb72ac | ||
|
|
11c344eef4 | ||
|
|
17008d6b03 | ||
|
|
5512efbb70 | ||
|
|
1e0a7612df | ||
|
|
8589e0d373 | ||
|
|
c5edaef356 | ||
|
|
692eb04c93 | ||
|
|
83d9a17c2b | ||
|
|
a4422ef7bd | ||
|
|
e6e9fcc3c3 | ||
|
|
7484243dff | ||
|
|
907676d688 | ||
|
|
54dbe1f3e4 | ||
|
|
98a2d61fd1 | ||
|
|
e544e088be | ||
|
|
86c33256ca | ||
|
|
91ff73e4e4 | ||
|
|
e68473a13f | ||
|
|
5774beda6f | ||
|
|
4869c11d60 | ||
|
|
4bc6f93902 | ||
|
|
ce4e079752 | ||
|
|
488df3d150 | ||
|
|
109e129cc5 | ||
|
|
447f89399f | ||
|
|
206c12c746 | ||
|
|
3c14310e3a | ||
|
|
50055c1045 | ||
|
|
cca97f5c51 | ||
|
|
1bfaf041ea | ||
|
|
927b5b6fd3 | ||
|
|
1afaa0bc8d | ||
|
|
7312a7aa94 | ||
|
|
fd18739b0a | ||
|
|
d92f56e7fe | ||
|
|
a1a7ec8c93 | ||
|
|
951fdf0927 | ||
|
|
7d3328f75b | ||
|
|
8db39a6b36 | ||
|
|
71681b1c5d | ||
|
|
6b2a015015 | ||
|
|
9e74baf37b | ||
|
|
b651d94bf5 | ||
|
|
b61433c5f8 | ||
|
|
c4d2191110 | ||
|
|
f6159c646c | ||
|
|
9cc7d54b64 | ||
|
|
05ce1d327b | ||
|
|
111df18204 | ||
|
|
bbcdf167b1 | ||
|
|
d62a44b1ac | ||
|
|
969bb2bcda | ||
|
|
6dcd3f2a98 | ||
|
|
7936dc85a9 | ||
|
|
94c4098de5 | ||
|
|
5b0aef8d4a | ||
|
|
4571b95e4a | ||
|
|
b9cba4c7df | ||
|
|
b46b5dc05f | ||
|
|
ef5630ba4a | ||
|
|
fe6c610d56 | ||
|
|
442dd195ca | ||
|
|
e9e4abff03 | ||
|
|
2a3fbd080e | ||
|
|
9d7f1a2702 | ||
|
|
57dcbce775 | ||
|
|
a71bf9ed79 | ||
|
|
725692ce94 | ||
|
|
ea15096dc3 | ||
|
|
a07dbb718f | ||
|
|
18381b76fb |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*_plus.go
|
||||
*-plus.sh
|
||||
@@ -6,6 +6,8 @@ function build() {
|
||||
DIST=$ROOT/"../dist/${NAME}"
|
||||
OS=${1}
|
||||
ARCH=${2}
|
||||
TAG=${3}
|
||||
NODE_ARCHITECTS=("amd64" "386" "arm64" "mips64" "mips64le")
|
||||
|
||||
if [ -z $OS ]; then
|
||||
echo "usage: build.sh OS ARCH"
|
||||
@@ -15,11 +17,14 @@ function build() {
|
||||
echo "usage: build.sh OS ARCH"
|
||||
exit
|
||||
fi
|
||||
if [ -z $TAG ]; then
|
||||
TAG="community"
|
||||
fi
|
||||
|
||||
VERSION=$(lookup-version $ROOT/../internal/const/const.go)
|
||||
ZIP="${NAME}-${OS}-${ARCH}-v${VERSION}.zip"
|
||||
ZIP="${NAME}-${OS}-${ARCH}-${TAG}-v${VERSION}.zip"
|
||||
|
||||
# check edge-node
|
||||
# build edge-node
|
||||
NodeVersion=$(lookup-version $ROOT"/../../EdgeNode/internal/const/const.go")
|
||||
echo "building edge-node v${NodeVersion} ..."
|
||||
EDGE_NODE_BUILD_SCRIPT=$ROOT"/../../EdgeNode/build/build.sh"
|
||||
@@ -29,21 +34,52 @@ function build() {
|
||||
fi
|
||||
cd $ROOT"/../../EdgeNode/build"
|
||||
echo "=============================="
|
||||
architects=("amd64" "386" "arm64" "mips64" "mips64le")
|
||||
for arch in "${architects[@]}"; do
|
||||
./build.sh linux $arch
|
||||
for arch in "${NODE_ARCHITECTS[@]}"; do
|
||||
if [ ! -f $ROOT"/../../EdgeNode/dist/edge-node-linux-${arch}-${TAG}-v${NodeVersion}.zip" ]; then
|
||||
./build.sh linux $arch $TAG
|
||||
else
|
||||
echo "use built node linux/$arch/v${NodeVersion}"
|
||||
fi
|
||||
done
|
||||
echo "=============================="
|
||||
cd -
|
||||
|
||||
rm -f $ROOT/deploy/*.zip
|
||||
for arch in "${architects[@]}"; do
|
||||
cp $ROOT"/../../EdgeNode/dist/edge-node-linux-${arch}-v${NodeVersion}.zip" $ROOT/deploy/
|
||||
for arch in "${NODE_ARCHITECTS[@]}"; do
|
||||
cp $ROOT"/../../EdgeNode/dist/edge-node-linux-${arch}-${TAG}-v${NodeVersion}.zip" $ROOT/deploy/edge-node-linux-${arch}-v${NodeVersion}.zip
|
||||
done
|
||||
|
||||
# build edge-dns
|
||||
if [ "$TAG" = "plus" ]; then
|
||||
DNS_ROOT=$ROOT"/../../EdgeDNS"
|
||||
if [ -d $DNS_ROOT ]; then
|
||||
DNSNodeVersion=$(lookup-version $ROOT"/../../EdgeDNS/internal/const/const.go")
|
||||
echo "building edge-dns ${DNSNodeVersion} ..."
|
||||
EDGE_DNS_NODE_BUILD_SCRIPT=$ROOT"/../../EdgeDNS/build/build.sh"
|
||||
if [ ! -f $EDGE_DNS_NODE_BUILD_SCRIPT ]; then
|
||||
echo "unable to find edge-dns build script 'EdgeDNS/build/build.sh'"
|
||||
exit
|
||||
fi
|
||||
cd $ROOT"/../../EdgeDNS/build"
|
||||
echo "=============================="
|
||||
architects=("amd64")
|
||||
for arch in "${architects[@]}"; do
|
||||
./build.sh linux $arch $TAG
|
||||
done
|
||||
echo "=============================="
|
||||
cd -
|
||||
|
||||
for arch in "${architects[@]}"; do
|
||||
cp $ROOT"/../../EdgeDNS/dist/edge-dns-linux-${arch}-v${DNSNodeVersion}.zip" $ROOT/deploy/edge-dns-linux-${arch}-v${DNSNodeVersion}.zip
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
# build sql
|
||||
echo "building sql ..."
|
||||
${ROOT}/sql.sh
|
||||
if [ $TAG = "plus" ]; then
|
||||
echo "building sql ..."
|
||||
${ROOT}/sql.sh
|
||||
fi
|
||||
|
||||
# copy files
|
||||
echo "copying ..."
|
||||
@@ -62,16 +98,24 @@ function build() {
|
||||
rm -f $DIST/resources/ipdata/ip2region/global_region.csv
|
||||
rm -f $DIST/resources/ipdata/ip2region/ip.merge.txt
|
||||
|
||||
# building installer
|
||||
echo "building installer ..."
|
||||
architects=("amd64" "386")
|
||||
# building edge installer
|
||||
echo "building node installer ..."
|
||||
architects=("amd64" "386" "arm64")
|
||||
for arch in "${architects[@]}"; do
|
||||
# TODO support arm, mips ...
|
||||
env GOOS=linux GOARCH=${arch} go build --ldflags="-s -w" -o $ROOT/installers/edge-installer-helper-linux-${arch} $ROOT/../cmd/installer-helper/main.go
|
||||
env GOOS=linux GOARCH=${arch} go build -tags $TAG --ldflags="-s -w" -o $ROOT/installers/edge-installer-helper-linux-${arch} $ROOT/../cmd/installer-helper/main.go
|
||||
done
|
||||
|
||||
# building edge dns installer
|
||||
echo "building dns node installer ..."
|
||||
architects=("amd64" "386" "arm64")
|
||||
for arch in "${architects[@]}"; do
|
||||
# TODO support arm, mips ...
|
||||
env GOOS=linux GOARCH=${arch} go build -tags $TAG --ldflags="-s -w" -o $ROOT/installers/edge-installer-dns-helper-linux-${arch} $ROOT/../cmd/installer-dns-helper/main.go
|
||||
done
|
||||
|
||||
# building api node
|
||||
env GOOS=$OS GOARCH=$ARCH go build --ldflags="-s -w" -o $DIST/bin/edge-api $ROOT/../cmd/edge-api/main.go
|
||||
env GOOS=$OS GOARCH=$ARCH go build -tags $TAG --ldflags="-s -w" -o $DIST/bin/edge-api $ROOT/../cmd/edge-api/main.go
|
||||
|
||||
# delete hidden files
|
||||
find $DIST -name ".DS_Store" -delete
|
||||
@@ -102,4 +146,4 @@ function lookup-version() {
|
||||
fi
|
||||
}
|
||||
|
||||
build $1 $2
|
||||
build $1 $2 $3
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/gosock/pkg/gosock"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
@@ -44,12 +45,13 @@ func main() {
|
||||
_, _ = os.Stdout.Write(resultJSON)
|
||||
})
|
||||
app.On("upgrade", func() {
|
||||
fmt.Println("start ...")
|
||||
executor, err := setup.NewSQLExecutorFromCmd()
|
||||
if err != nil {
|
||||
fmt.Println("ERROR: " + err.Error())
|
||||
return
|
||||
}
|
||||
err = executor.Run()
|
||||
err = executor.Run(true)
|
||||
if err != nil {
|
||||
fmt.Println("ERROR: " + err.Error())
|
||||
return
|
||||
@@ -67,6 +69,34 @@ func main() {
|
||||
}
|
||||
fmt.Println("done")
|
||||
})
|
||||
app.On("goman", func() {
|
||||
var sock = gosock.NewTmpSock(teaconst.ProcessName)
|
||||
reply, err := sock.Send(&gosock.Command{Code: "goman"})
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]" + err.Error())
|
||||
} else {
|
||||
instancesJSON, err := json.MarshalIndent(reply.Params, "", " ")
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]" + err.Error())
|
||||
} else {
|
||||
fmt.Println(string(instancesJSON))
|
||||
}
|
||||
}
|
||||
})
|
||||
app.On("debug", func() {
|
||||
var sock = gosock.NewTmpSock(teaconst.ProcessName)
|
||||
reply, err := sock.Send(&gosock.Command{Code: "debug"})
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]" + err.Error())
|
||||
} else {
|
||||
var isDebug = maps.NewMap(reply.Params).GetBool("debug")
|
||||
if isDebug {
|
||||
fmt.Println("debug on")
|
||||
} else {
|
||||
fmt.Println("debug off")
|
||||
}
|
||||
}
|
||||
})
|
||||
app.Run(func() {
|
||||
nodes.NewAPINode().Start()
|
||||
})
|
||||
|
||||
73
cmd/installer-dns-helper/main.go
Normal file
73
cmd/installer-dns-helper/main.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
"github.com/iwind/gosock/pkg/gosock"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd := ""
|
||||
flag.StringVar(&cmd, "cmd", "", "command name: [unzip]")
|
||||
|
||||
// unzip
|
||||
zipPath := ""
|
||||
targetPath := ""
|
||||
flag.StringVar(&zipPath, "zip", "", "zip path")
|
||||
flag.StringVar(&targetPath, "target", "", "target dir")
|
||||
|
||||
// parse
|
||||
flag.Parse()
|
||||
|
||||
if len(cmd) == 0 {
|
||||
stderr("need '-cmd=COMMAND' argument")
|
||||
} else if cmd == "test" {
|
||||
// 检查是否正在运行
|
||||
var sock = gosock.NewTmpSock("edge-dns")
|
||||
if sock.IsListening() {
|
||||
// 从systemd中停止
|
||||
systemctl, _ := exec.LookPath("systemctl")
|
||||
if len(systemctl) > 0 {
|
||||
systemctlCmd := exec.Command(systemctl, "stop", "edge-dns")
|
||||
_ = systemctlCmd.Run()
|
||||
}
|
||||
|
||||
// 从进程中停止
|
||||
if sock.IsListening() {
|
||||
_, _ = sock.Send(&gosock.Command{
|
||||
Code: "stop",
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if cmd == "unzip" { // 解压
|
||||
if len(zipPath) == 0 {
|
||||
stderr("ERROR: need '-zip=PATH' argument")
|
||||
return
|
||||
}
|
||||
if len(targetPath) == 0 {
|
||||
stderr("ERROR: need '-target=TARGET' argument")
|
||||
return
|
||||
}
|
||||
|
||||
unzip := utils.NewUnzip(zipPath, targetPath)
|
||||
err := unzip.Run()
|
||||
if err != nil {
|
||||
stderr("ERROR: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
stdout("ok")
|
||||
} else {
|
||||
stderr("ERROR: not recognized command '" + cmd + "'")
|
||||
}
|
||||
}
|
||||
|
||||
func stdout(s string) {
|
||||
_, _ = os.Stdout.WriteString(s + "\n")
|
||||
}
|
||||
|
||||
func stderr(s string) {
|
||||
_, _ = os.Stderr.WriteString(s + "\n")
|
||||
}
|
||||
@@ -3,8 +3,9 @@ package main
|
||||
import (
|
||||
"flag"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
"net"
|
||||
"github.com/iwind/gosock/pkg/gosock"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -24,11 +25,21 @@ func main() {
|
||||
stderr("need '-cmd=COMMAND' argument")
|
||||
} else if cmd == "test" {
|
||||
// 检查是否正在运行
|
||||
path := os.TempDir() + "/edge-node.sock"
|
||||
conn, err := net.Dial("unix", path)
|
||||
if err == nil {
|
||||
_ = conn.Close()
|
||||
stderr("test node status: edge node is running now, can not install again")
|
||||
var sock = gosock.NewTmpSock("edge-node")
|
||||
if sock.IsListening() {
|
||||
// 从systemd中停止
|
||||
systemctl, _ := exec.LookPath("systemctl")
|
||||
if len(systemctl) > 0 {
|
||||
systemctlCmd := exec.Command(systemctl, "stop", "edge-node")
|
||||
_ = systemctlCmd.Run()
|
||||
}
|
||||
|
||||
// 从进程中停止
|
||||
if sock.IsListening() {
|
||||
_, _ = sock.Send(&gosock.Command{
|
||||
Code: "stop",
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if cmd == "unzip" { // 解压
|
||||
if len(zipPath) == 0 {
|
||||
|
||||
24
go.mod
24
go.mod
@@ -7,21 +7,25 @@ replace github.com/TeaOSLab/EdgeCommon => ../EdgeCommon
|
||||
require (
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
|
||||
github.com/TeaOSLab/EdgeCommon v0.0.0-00010101000000-000000000000
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.641
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183
|
||||
github.com/andybalholm/brotli v1.0.4
|
||||
github.com/cespare/xxhash/v2 v2.1.1
|
||||
github.com/go-acme/lego/v4 v4.1.2
|
||||
github.com/go-acme/lego/v4 v4.5.2
|
||||
github.com/go-ole/go-ole v1.2.4 // indirect
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/go-yaml/yaml v2.1.0+incompatible
|
||||
github.com/golang/protobuf v1.4.2
|
||||
github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/iwind/TeaGo v0.0.0-20211026123858-7de7a21cad24
|
||||
github.com/iwind/gosock v0.0.0-20210722083328-12b2d66abec3
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/lionsoul2014/ip2region v2.2.0-release+incompatible
|
||||
github.com/mozillazg/go-pinyin v0.18.0
|
||||
github.com/pkg/sftp v1.12.0
|
||||
github.com/shirou/gopsutil v2.20.9+incompatible
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299
|
||||
google.golang.org/grpc v1.32.0
|
||||
google.golang.org/protobuf v1.25.0
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
github.com/shirou/gopsutil v3.21.5+incompatible
|
||||
github.com/tklauser/go-sysconf v0.3.6 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22
|
||||
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced // indirect
|
||||
google.golang.org/grpc v1.38.0
|
||||
google.golang.org/protobuf v1.26.0
|
||||
)
|
||||
|
||||
423
go.sum
423
go.sum
@@ -14,83 +14,106 @@ cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNF
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg=
|
||||
github.com/Azure/go-autorest/autorest v0.5.0/go.mod h1:9HLKlQjVBH6U3oDfsXOeVc56THsLPw1L03yban4xThw=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.2.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.1.0/go.mod h1:Gf7/i2FUpyb/sGBLIFxTBzrNzBo7aPXXE3ZVeDRwdpM=
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.1.0/go.mod h1:Dk8CUAt/b/PzkfeRsWzVG9Yj3ps8mS8ECztu43rdU8U=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
|
||||
github.com/Azure/go-autorest/autorest v0.11.19/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.8/go.mod h1:kxyKZTSfKh8OVFWPAgOgQ/frrJgeYQJPyR5fLFmXko4=
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM=
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
|
||||
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.18/go.mod h1:L+HB2uBoDgi3+r1pJEJcbGwyyHhd2QXaGsKLbDwtm8Q=
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.458/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.641 h1:X/Ik2DvrwICTd5hbRPjB7+s/61pk/b40HJ6XHAg2LSc=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.641/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/aws/aws-sdk-go v1.30.20/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183 h1:dkj8/dxOQ4L1XpwCzRLqukvUBbxuNdz3FeyvHFnRjmo=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aws/aws-sdk-go v1.39.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/cenkalti/backoff/v4 v4.0.2 h1:JIufpQLbh4DkbQoii76ItQIUFzevQSqOLZca4eamEDs=
|
||||
github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw=
|
||||
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=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/cloudflare-go v0.13.2/go.mod h1:27kfc1apuifUmJhp069y0+hwlKDg4bd8LWlu7oKeZvM=
|
||||
github.com/cloudflare/cloudflare-go v0.20.0/go.mod h1:sPWL/lIC6biLEdyGZwBQ1rGQKF1FhM7N60fuNiFdYTI=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cpu/goacmedns v0.0.3/go.mod h1:4MipLkI+qScwqtVxcNO6okBhbgRrr7/tKXUSgSL0teQ=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpu/goacmedns v0.1.1/go.mod h1:MuaouqEhPAHxsbqjgnck5zeghuwBP1dLnPoobeGqugQ=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deepmap/oapi-codegen v1.6.1/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200624174652-8d2f3be8b2d9/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||
github.com/dnsimple/dnsimple-go v0.63.0/go.mod h1:O5TJ0/U6r7AfT8niYNlmohpLbCSG+c71tQlGr9SeGrg=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
|
||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/dnsimple/dnsimple-go v0.70.1/go.mod h1:F9WHww9cC76hrnwGFfAfrqdW99j3MOYasQcIwTS/aUk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/exoscale/egoscale v0.23.0/go.mod h1:hRo78jkjkCDKpivQdRBEpNYF5+cVpCJCPDg2/r45KaY=
|
||||
github.com/exoscale/egoscale v0.67.0/go.mod h1:wi0myUxPsV8SdEtdJHQJxFLL/wEw9fiw9Gs1PWRkvkM=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-acme/lego/v4 v4.1.2 h1:1zROppXkTbAIh7J7AydGD3dFICLIocucJY1NTH/wB64=
|
||||
github.com/go-acme/lego/v4 v4.1.2/go.mod h1:pIFm5tWkXSgiAEfJ/XQCQIvX1cEvHFwbgLZyx8OVSUE=
|
||||
github.com/go-acme/lego/v4 v4.5.2 h1:Gg6jta10furQZ+DRknspdFjzboBQ132RmjSgd4CJuH0=
|
||||
github.com/go-acme/lego/v4 v4.5.2/go.mod h1:mL1DY809LzjvRuaxINNxsI26f5oStVhBGTpJMiinkZM=
|
||||
github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
|
||||
github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
@@ -101,18 +124,23 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
|
||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-redis/redis/v8 v8.0.0-beta.7/go.mod h1:FGJAWDWFht1sQ4qxyJHZZbVyvnVcKQN0E3u5/5lRz+g=
|
||||
github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
|
||||
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
|
||||
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -132,18 +160,24 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
@@ -153,49 +187,80 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gophercloud/gophercloud v0.6.1-0.20191122030953-d8ac278c1c9d/go.mod h1:ozGNgr9KYOVATV5jsgHl/ceCDXGuguqOZAzoQ/2vcNM=
|
||||
github.com/gophercloud/gophercloud v0.7.0/go.mod h1:gmC5oQqMDOMO1t1gq5DquX/yAU808e/4mzjjDA76+Ss=
|
||||
github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c/go.mod h1:ehWUbLQJPqS0Ep+CxeD559hsm9pthPXadJNKwZkp43w=
|
||||
github.com/gophercloud/gophercloud v0.15.1-0.20210202035223-633d73521055/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4=
|
||||
github.com/gophercloud/gophercloud v0.16.0/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4=
|
||||
github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae/go.mod h1:wx8HMD8oQD0Ryhz6+6ykq75PJ79iPyEqYHfwZ4l7OsA=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.7/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4=
|
||||
github.com/iwind/TeaGo v0.0.0-20200923021120-f5d76441fe9e/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||
github.com/iwind/TeaGo v0.0.0-20210125103732-4d79fc3c3d0b h1:niQL2t//041GUaqDPM5D+ldyr0Ng2WKwZJHPRLQhQtk=
|
||||
github.com/iwind/TeaGo v0.0.0-20210125103732-4d79fc3c3d0b/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||
github.com/iwind/TeaGo v0.0.0-20210302120856-7588e79bdbe3 h1:k9K3HHMmkF7HYyIHz21AtmYH4Zdk/8OI98a8P0O8o1I=
|
||||
github.com/iwind/TeaGo v0.0.0-20210302120856-7588e79bdbe3/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||
github.com/iwind/TeaGo v0.0.0-20210325033016-3279bdaa087d h1:FQTYJmZeCMdwM0Bz+C4h31SDBt04ap6A4JOjm+FfYwk=
|
||||
github.com/iwind/TeaGo v0.0.0-20210325033016-3279bdaa087d/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||
github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f h1:r2O8PONj/KiuZjJHVHn7KlCePUIjNtgAmvLfgRafQ8o=
|
||||
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-20210628135026-38575a4ab060/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||
github.com/iwind/TeaGo v0.0.0-20210831140440-a2a442471b13 h1:HuEJ5xJfujW1Q6rNDhOu5LQXEBB2qLPah3jYslT8Gz4=
|
||||
github.com/iwind/TeaGo v0.0.0-20210831140440-a2a442471b13/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||
github.com/iwind/TeaGo v0.0.0-20211026123858-7de7a21cad24 h1:1cGulkD2SNJJRok5OKwyhP/Ddm+PgSWKOupn0cR36/A=
|
||||
github.com/iwind/TeaGo v0.0.0-20211026123858-7de7a21cad24/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||
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/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||
github.com/jarcoal/httpmock v1.0.6/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
|
||||
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
@@ -209,26 +274,58 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA=
|
||||
github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w=
|
||||
github.com/linode/linodego v0.21.0/go.mod h1:UTpq1JUZD0CZsJ8rt+0CRkqbzrp1MbGakVPt2DXY5Mk=
|
||||
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
|
||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||
github.com/linode/linodego v0.31.1/go.mod h1:BR0gVkCJffEdIGJSl6bHR80Ty+Uvg/2jkjmrWaFectM=
|
||||
github.com/lionsoul2014/ip2region v2.2.0-release+incompatible h1:1qp9iks+69h7IGLazAplzS9Ca14HAxuD5c0rbFdPGy4=
|
||||
github.com/lionsoul2014/ip2region v2.2.0-release+incompatible/go.mod h1:+ZBN7PBoh5gG6/y0ZQ85vJDBe21WnfbRrQQwTfliJJI=
|
||||
github.com/liquidweb/liquidweb-go v1.6.1/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVLEIG/i5J9cyixzQ=
|
||||
github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
|
||||
github.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
|
||||
github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ=
|
||||
github.com/liquidweb/liquidweb-go v1.6.3/go.mod h1:SuXXp+thr28LnjEw18AYtWwIbWMHSUiajPQs8T9c/Rc=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
||||
github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.1.31 h1:sJFOl9BgwbYAWOGEwr61FU28pqsBNdpRBnhGXtO06Oo=
|
||||
github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
|
||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mozillazg/go-pinyin v0.18.0 h1:hQompXO23/0ohH8YNjvfsAITnCQImCiR/Fny8EhIeW0=
|
||||
github.com/mozillazg/go-pinyin v0.18.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
@@ -237,102 +334,156 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nrdcg/auroradns v1.0.1/go.mod h1:y4pc0i9QXYlFCWrhWrUSIETnZgrf4KuwjDIWmmXo3JI=
|
||||
github.com/nrdcg/desec v0.5.0/go.mod h1:2ejvMazkav1VdDbv2HeQO7w+Ta1CGHqzQr27ZBYTuEQ=
|
||||
github.com/nrdcg/desec v0.6.0/go.mod h1:wybWg5cRrNmtXLYpUCPCLvz4jfFNEGZQEnoUiX9WqcY=
|
||||
github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ=
|
||||
github.com/nrdcg/freemyip v0.2.0/go.mod h1:HjF0Yz0lSb37HD2ihIyGz9esyGcxbCrrGFLPpKevbx4=
|
||||
github.com/nrdcg/goinwx v0.8.1/go.mod h1:tILVc10gieBp/5PMvbcYeXM6pVQ+c9jxDZnpaR1UW7c=
|
||||
github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw=
|
||||
github.com/nrdcg/porkbun v0.1.1/go.mod h1:JWl/WKnguWos4mjfp4YizvvToigk9qpQwrodOk+CPoA=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
|
||||
github.com/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/oracle/oci-go-sdk v24.2.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
||||
github.com/ovh/go-ovh v1.1.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
github.com/pkg/sftp v1.12.0 h1:/f3b24xrDhkhddlaobPe2JgBqfdt+gC/NYl0QY9IOuI=
|
||||
github.com/pkg/sftp v1.12.0/go.mod h1:fUqqXB5vEgVCZ131L+9say31RAri6aF6KDViawhxKK8=
|
||||
github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sacloud/libsacloud v1.36.2/go.mod h1:P7YAOVmnIn3DKHqCZcUKYUXmSwGBm3yS7IBEjKVSrjg=
|
||||
github.com/shirou/gopsutil v2.20.9+incompatible h1:msXs2frUV+O/JLva9EDLpuJ84PrFsdCTCQex8PUdtkQ=
|
||||
github.com/shirou/gopsutil v2.20.9+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shirou/gopsutil v3.21.5+incompatible h1:OloQyEerMi7JUrXiNzy8wQ5XN+baemxSl12QgIzt0jc=
|
||||
github.com/shirou/gopsutil v3.21.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
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/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
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=
|
||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/gunit v1.0.4/go.mod h1:EH5qMBab2UclzXUcpR8b93eHsIlp9u+pDQIRp5DZNzQ=
|
||||
github.com/softlayer/softlayer-go v1.0.3/go.mod h1:6HepcfAXROz0Rf63krk5hPZyHT6qyx2MNvYyHof7ik4=
|
||||
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e/go.mod h1:fKZCUVdirrxrBpwd9wb+lSoVixvpwAu8eHzbQB2tums=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/transip/gotransip/v6 v6.2.0/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tklauser/go-sysconf v0.3.6 h1:oc1sJWvKkmvIxhDHeKWvZS4f6AW+YcoguSfRF2/Hmo4=
|
||||
github.com/tklauser/go-sysconf v0.3.6/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
|
||||
github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA=
|
||||
github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/transip/gotransip/v6 v6.6.1/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g=
|
||||
github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g=
|
||||
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/vultr/govultr v0.5.0/go.mod h1:wZZXZbYbqyY1n3AldoeYNZK4Wnmmoq6dNFkvd5TV3ss=
|
||||
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14/go.mod h1:RWc47jtnVuQv6+lY3c768WtXCas/Xi+U5UFc5xULmYg=
|
||||
github.com/vultr/govultr/v2 v2.7.1/go.mod h1:BvOhVe6/ZpjwcoL6/unkdQshmbS9VGbowI4QT+3DGVU=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opentelemetry.io/otel v0.7.0/go.mod h1:aZMyHG5TqDOXEgH2tyLiXSUKly1jT3yqE9PmrzIeCdo=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
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-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
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=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
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 h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
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=
|
||||
@@ -356,6 +507,7 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
@@ -363,13 +515,16 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
@@ -381,7 +536,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -389,8 +543,16 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
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 h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
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=
|
||||
@@ -401,18 +563,21 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -421,34 +586,60 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201110211018-35f3e6cf4a65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/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 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
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=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
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 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
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=
|
||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
@@ -465,13 +656,12 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
@@ -482,11 +672,14 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapK
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
@@ -521,11 +714,10 @@ google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced h1:c5geK1iMU3cDKtFrCVQIcjR3W+JOZMuhIyICMCTbtus=
|
||||
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
@@ -534,8 +726,9 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0=
|
||||
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@@ -544,8 +737,10 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -556,17 +751,20 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
|
||||
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ns1/ns1-go.v2 v2.4.2/go.mod h1:GMnKY+ZuoJ+lVLL+78uSTjwTz2jMazq6AfGKQOYhsPk=
|
||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ns1/ns1-go.v2 v2.6.2/go.mod h1:GMnKY+ZuoJ+lVLL+78uSTjwTz2jMazq6AfGKQOYhsPk=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@@ -574,9 +772,8 @@ 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-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
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=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
66
internal/accesslogs/storage_base.go
Normal file
66
internal/accesslogs/storage_base.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package accesslogs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type BaseStorage struct {
|
||||
isOk bool
|
||||
version int
|
||||
}
|
||||
|
||||
func (this *BaseStorage) SetVersion(version int) {
|
||||
this.version = version
|
||||
}
|
||||
|
||||
func (this *BaseStorage) Version() int {
|
||||
return this.version
|
||||
}
|
||||
|
||||
func (this *BaseStorage) IsOk() bool {
|
||||
return this.isOk
|
||||
}
|
||||
|
||||
func (this *BaseStorage) SetOk(isOk bool) {
|
||||
this.isOk = isOk
|
||||
}
|
||||
|
||||
// Marshal 对日志进行编码
|
||||
func (this *BaseStorage) Marshal(accessLog *pb.HTTPAccessLog) ([]byte, error) {
|
||||
return json.Marshal(accessLog)
|
||||
}
|
||||
|
||||
// FormatVariables 格式化字符串中的变量
|
||||
func (this *BaseStorage) FormatVariables(s string) string {
|
||||
now := time.Now()
|
||||
return configutils.ParseVariables(s, func(varName string) (value string) {
|
||||
switch varName {
|
||||
case "year":
|
||||
return strconv.Itoa(now.Year())
|
||||
case "month":
|
||||
return fmt.Sprintf("%02d", now.Month())
|
||||
case "week":
|
||||
_, week := now.ISOWeek()
|
||||
return fmt.Sprintf("%02d", week)
|
||||
case "day":
|
||||
return fmt.Sprintf("%02d", now.Day())
|
||||
case "hour":
|
||||
return fmt.Sprintf("%02d", now.Hour())
|
||||
case "minute":
|
||||
return fmt.Sprintf("%02d", now.Minute())
|
||||
case "second":
|
||||
return fmt.Sprintf("%02d", now.Second())
|
||||
case "date":
|
||||
return fmt.Sprintf("%d%02d%02d", now.Year(), now.Month(), now.Day())
|
||||
}
|
||||
|
||||
return varName
|
||||
})
|
||||
}
|
||||
95
internal/accesslogs/storage_command.go
Normal file
95
internal/accesslogs/storage_command.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package accesslogs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"os/exec"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// CommandStorage 通过命令行存储
|
||||
type CommandStorage struct {
|
||||
BaseStorage
|
||||
|
||||
config *serverconfigs.AccessLogCommandStorageConfig
|
||||
|
||||
writeLocker sync.Mutex
|
||||
}
|
||||
|
||||
func NewCommandStorage(config *serverconfigs.AccessLogCommandStorageConfig) *CommandStorage {
|
||||
return &CommandStorage{config: config}
|
||||
}
|
||||
|
||||
func (this *CommandStorage) Config() interface{} {
|
||||
return this.config
|
||||
}
|
||||
|
||||
// Start 启动
|
||||
func (this *CommandStorage) Start() error {
|
||||
if len(this.config.Command) == 0 {
|
||||
return errors.New("'command' should not be empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 写入日志
|
||||
func (this *CommandStorage) Write(accessLogs []*pb.HTTPAccessLog) error {
|
||||
if len(accessLogs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
this.writeLocker.Lock()
|
||||
defer this.writeLocker.Unlock()
|
||||
|
||||
cmd := exec.Command(this.config.Command, this.config.Args...)
|
||||
if len(this.config.Dir) > 0 {
|
||||
cmd.Dir = this.config.Dir
|
||||
}
|
||||
|
||||
stdout := bytes.NewBuffer([]byte{})
|
||||
cmd.Stdout = stdout
|
||||
|
||||
w, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, accessLog := range accessLogs {
|
||||
data, err := this.Marshal(accessLog)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
continue
|
||||
}
|
||||
_, err = w.Write(data)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
}
|
||||
|
||||
_, err = w.Write([]byte("\n"))
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
}
|
||||
}
|
||||
_ = w.Close()
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
|
||||
if stdout.Len() > 0 {
|
||||
logs.Error(errors.New(string(stdout.Bytes())))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close 关闭
|
||||
func (this *CommandStorage) Close() error {
|
||||
return nil
|
||||
}
|
||||
63
internal/accesslogs/storage_command_test.go
Normal file
63
internal/accesslogs/storage_command_test.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package accesslogs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCommandStorage_Write(t *testing.T) {
|
||||
php, err := exec.LookPath("php")
|
||||
if err != nil { // not found php, so we can not test
|
||||
t.Log("php:", err)
|
||||
return
|
||||
}
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
before := time.Now()
|
||||
|
||||
storage := NewCommandStorage(&serverconfigs.AccessLogCommandStorageConfig{
|
||||
Command: php,
|
||||
Args: []string{cwd + "/tests/command_storage.php"},
|
||||
})
|
||||
err = storage.Start()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = storage.Write([]*pb.HTTPAccessLog{
|
||||
{
|
||||
RequestMethod: "GET",
|
||||
RequestPath: "/hello",
|
||||
},
|
||||
{
|
||||
RequestMethod: "GET",
|
||||
RequestPath: "/world",
|
||||
},
|
||||
{
|
||||
RequestMethod: "GET",
|
||||
RequestPath: "/lu",
|
||||
},
|
||||
{
|
||||
RequestMethod: "GET",
|
||||
RequestPath: "/ping",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = storage.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(time.Since(before).Seconds(), "seconds")
|
||||
}
|
||||
127
internal/accesslogs/storage_es.go
Normal file
127
internal/accesslogs/storage_es.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package accesslogs
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ESStorage ElasticSearch存储策略
|
||||
type ESStorage struct {
|
||||
BaseStorage
|
||||
|
||||
config *serverconfigs.AccessLogESStorageConfig
|
||||
}
|
||||
|
||||
func NewESStorage(config *serverconfigs.AccessLogESStorageConfig) *ESStorage {
|
||||
return &ESStorage{config: config}
|
||||
}
|
||||
|
||||
func (this *ESStorage) Config() interface{} {
|
||||
return this.config
|
||||
}
|
||||
|
||||
// Start 开启
|
||||
func (this *ESStorage) Start() error {
|
||||
if len(this.config.Endpoint) == 0 {
|
||||
return errors.New("'endpoint' should not be nil")
|
||||
}
|
||||
if !regexp.MustCompile(`(?i)^(http|https)://`).MatchString(this.config.Endpoint) {
|
||||
this.config.Endpoint = "http://" + this.config.Endpoint
|
||||
}
|
||||
if len(this.config.Index) == 0 {
|
||||
return errors.New("'index' should not be nil")
|
||||
}
|
||||
if len(this.config.MappingType) == 0 {
|
||||
return errors.New("'mappingType' should not be nil")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 写入日志
|
||||
func (this *ESStorage) Write(accessLogs []*pb.HTTPAccessLog) error {
|
||||
if len(accessLogs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
bulk := &strings.Builder{}
|
||||
indexName := this.FormatVariables(this.config.Index)
|
||||
typeName := this.FormatVariables(this.config.MappingType)
|
||||
for _, accessLog := range accessLogs {
|
||||
if len(accessLog.RequestId) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
opData, err := json.Marshal(map[string]interface{}{
|
||||
"index": map[string]interface{}{
|
||||
"_index": indexName,
|
||||
"_type": typeName,
|
||||
"_id": accessLog.RequestId,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
remotelogs.Error("ACCESS_LOG_ES_STORAGE", "write failed: "+err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
data, err := this.Marshal(accessLog)
|
||||
if err != nil {
|
||||
remotelogs.Error("ACCESS_LOG_ES_STORAGE", "marshal data failed: "+err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
bulk.Write(opData)
|
||||
bulk.WriteString("\n")
|
||||
bulk.Write(data)
|
||||
bulk.WriteString("\n")
|
||||
}
|
||||
|
||||
if bulk.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, this.config.Endpoint+"/_bulk", strings.NewReader(bulk.String()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("User-Agent", strings.ReplaceAll(teaconst.ProductName, " ", "-")+"/"+teaconst.Version)
|
||||
if len(this.config.Username) > 0 || len(this.config.Password) > 0 {
|
||||
req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(this.config.Username+":"+this.config.Password)))
|
||||
}
|
||||
client := utils.SharedHttpClient(10 * time.Second)
|
||||
defer func() {
|
||||
_ = req.Body.Close()
|
||||
}()
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
bodyData, _ := ioutil.ReadAll(resp.Body)
|
||||
return errors.New("ElasticSearch response status code: " + fmt.Sprintf("%d", resp.StatusCode) + " content: " + string(bodyData))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close 关闭
|
||||
func (this *ESStorage) Close() error {
|
||||
return nil
|
||||
}
|
||||
53
internal/accesslogs/storage_es_test.go
Normal file
53
internal/accesslogs/storage_es_test.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package accesslogs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestESStorage_Write(t *testing.T) {
|
||||
storage := NewESStorage(&serverconfigs.AccessLogESStorageConfig{
|
||||
Endpoint: "http://127.0.0.1:9200",
|
||||
Index: "logs",
|
||||
MappingType: "accessLogs",
|
||||
Username: "hello",
|
||||
Password: "world",
|
||||
})
|
||||
err := storage.Start()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
{
|
||||
err = storage.Write([]*pb.HTTPAccessLog{
|
||||
{
|
||||
RequestMethod: "POST",
|
||||
RequestPath: "/1",
|
||||
TimeLocal: time.Now().Format("2/Jan/2006:15:04:05 -0700"),
|
||||
TimeISO8601: "2018-07-23T22:23:35+08:00",
|
||||
Header: map[string]*pb.Strings{
|
||||
"Content-Type": {Values: []string{"text/html"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
RequestMethod: "GET",
|
||||
RequestPath: "/2",
|
||||
TimeLocal: time.Now().Format("2/Jan/2006:15:04:05 -0700"),
|
||||
TimeISO8601: "2018-07-23T22:23:35+08:00",
|
||||
Header: map[string]*pb.Strings{
|
||||
"Content-Type": {Values: []string{"text/css"}},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
err = storage.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
127
internal/accesslogs/storage_file.go
Normal file
127
internal/accesslogs/storage_file.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package accesslogs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// FileStorage 文件存储策略
|
||||
type FileStorage struct {
|
||||
BaseStorage
|
||||
|
||||
config *serverconfigs.AccessLogFileStorageConfig
|
||||
|
||||
writeLocker sync.Mutex
|
||||
|
||||
files map[string]*os.File // path => *File
|
||||
filesLocker sync.Mutex
|
||||
}
|
||||
|
||||
func NewFileStorage(config *serverconfigs.AccessLogFileStorageConfig) *FileStorage {
|
||||
return &FileStorage{
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *FileStorage) Config() interface{} {
|
||||
return this.config
|
||||
}
|
||||
|
||||
// Start 开启
|
||||
func (this *FileStorage) Start() error {
|
||||
if len(this.config.Path) == 0 {
|
||||
return errors.New("'path' should not be empty")
|
||||
}
|
||||
|
||||
this.files = map[string]*os.File{}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write 写入日志
|
||||
func (this *FileStorage) Write(accessLogs []*pb.HTTPAccessLog) error {
|
||||
if len(accessLogs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
fp := this.fp()
|
||||
if fp == nil {
|
||||
return errors.New("file pointer should not be nil")
|
||||
}
|
||||
this.writeLocker.Lock()
|
||||
defer this.writeLocker.Unlock()
|
||||
|
||||
for _, accessLog := range accessLogs {
|
||||
data, err := this.Marshal(accessLog)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
continue
|
||||
}
|
||||
_, err = fp.Write(data)
|
||||
if err != nil {
|
||||
_ = this.Close()
|
||||
break
|
||||
}
|
||||
_, _ = fp.WriteString("\n")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close 关闭
|
||||
func (this *FileStorage) Close() error {
|
||||
this.filesLocker.Lock()
|
||||
defer this.filesLocker.Unlock()
|
||||
|
||||
var resultErr error
|
||||
for _, f := range this.files {
|
||||
err := f.Close()
|
||||
if err != nil {
|
||||
resultErr = err
|
||||
}
|
||||
}
|
||||
return resultErr
|
||||
}
|
||||
|
||||
func (this *FileStorage) fp() *os.File {
|
||||
path := this.FormatVariables(this.config.Path)
|
||||
|
||||
this.filesLocker.Lock()
|
||||
defer this.filesLocker.Unlock()
|
||||
fp, ok := this.files[path]
|
||||
if ok {
|
||||
return fp
|
||||
}
|
||||
|
||||
// 关闭其他的文件
|
||||
for _, f := range this.files {
|
||||
_ = f.Close()
|
||||
}
|
||||
|
||||
// 是否创建文件目录
|
||||
if this.config.AutoCreate {
|
||||
dir := filepath.Dir(path)
|
||||
_, err := os.Stat(dir)
|
||||
if os.IsNotExist(err) {
|
||||
err = os.MkdirAll(dir, 0777)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 打开新文件
|
||||
fp, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
return nil
|
||||
}
|
||||
this.files[path] = fp
|
||||
|
||||
return fp
|
||||
}
|
||||
70
internal/accesslogs/storage_file_test.go
Normal file
70
internal/accesslogs/storage_file_test.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package accesslogs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestFileStorage_Write(t *testing.T) {
|
||||
storage := NewFileStorage(&serverconfigs.AccessLogFileStorageConfig{
|
||||
Path: Tea.Root + "/logs/access-${date}.log",
|
||||
})
|
||||
err := storage.Start()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
{
|
||||
err = storage.Write([]*pb.HTTPAccessLog{
|
||||
{
|
||||
RequestPath: "/hello",
|
||||
},
|
||||
{
|
||||
RequestPath: "/world",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
err = storage.Write([]*pb.HTTPAccessLog{
|
||||
{
|
||||
RequestPath: "/1",
|
||||
},
|
||||
{
|
||||
RequestPath: "/2",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
err = storage.Write([]*pb.HTTPAccessLog{
|
||||
{
|
||||
RequestMethod: "POST",
|
||||
RequestPath: "/1",
|
||||
TimeLocal: time.Now().Format("2/Jan/2006:15:04:05 -0700"),
|
||||
},
|
||||
{
|
||||
RequestMethod: "GET",
|
||||
RequestPath: "/2",
|
||||
TimeLocal: time.Now().Format("2/Jan/2006:15:04:05 -0700"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
err = storage.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
30
internal/accesslogs/storage_interface.go
Normal file
30
internal/accesslogs/storage_interface.go
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package accesslogs
|
||||
|
||||
import "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
|
||||
// StorageInterface 日志存储接口
|
||||
type StorageInterface interface {
|
||||
// Version 获取版本
|
||||
Version() int
|
||||
|
||||
// SetVersion 设置版本
|
||||
SetVersion(version int)
|
||||
|
||||
IsOk() bool
|
||||
|
||||
SetOk(ok bool)
|
||||
|
||||
// Config 获取配置
|
||||
Config() interface{}
|
||||
|
||||
// Start 开启
|
||||
Start() error
|
||||
|
||||
// Write 写入日志
|
||||
Write(accessLogs []*pb.HTTPAccessLog) error
|
||||
|
||||
// Close 关闭
|
||||
Close() error
|
||||
}
|
||||
183
internal/accesslogs/storage_manager.go
Normal file
183
internal/accesslogs/storage_manager.go
Normal file
@@ -0,0 +1,183 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package accesslogs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var SharedStorageManager = NewStorageManager()
|
||||
|
||||
type StorageManager struct {
|
||||
storageMap map[int64]StorageInterface // policyId => Storage
|
||||
|
||||
locker sync.Mutex
|
||||
}
|
||||
|
||||
func NewStorageManager() *StorageManager {
|
||||
return &StorageManager{
|
||||
storageMap: map[int64]StorageInterface{},
|
||||
}
|
||||
}
|
||||
|
||||
func (this *StorageManager) Start() {
|
||||
var ticker = time.NewTicker(1 * time.Minute)
|
||||
if Tea.IsTesting() {
|
||||
ticker = time.NewTicker(5 * time.Second)
|
||||
}
|
||||
|
||||
// 启动时执行一次
|
||||
var err = this.Loop()
|
||||
if err != nil {
|
||||
remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "update error: "+err.Error())
|
||||
}
|
||||
|
||||
// 循环执行
|
||||
for range ticker.C {
|
||||
err := this.Loop()
|
||||
if err != nil {
|
||||
remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "update error: "+err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop 更新
|
||||
func (this *StorageManager) Loop() error {
|
||||
policies, err := models.SharedHTTPAccessLogPolicyDAO.FindAllEnabledAndOnPolicies(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var policyIds = []int64{}
|
||||
for _, policy := range policies {
|
||||
if policy.IsOn == 1 {
|
||||
policyIds = append(policyIds, int64(policy.Id))
|
||||
}
|
||||
}
|
||||
|
||||
this.locker.Lock()
|
||||
defer this.locker.Unlock()
|
||||
|
||||
// 关闭不用的
|
||||
for policyId, storage := range this.storageMap {
|
||||
if !lists.ContainsInt64(policyIds, policyId) {
|
||||
err := storage.Close()
|
||||
if err != nil {
|
||||
remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "close '"+types.String(policyId)+"' failed: "+err.Error())
|
||||
}
|
||||
delete(this.storageMap, policyId)
|
||||
remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "remove '"+types.String(policyId)+"'")
|
||||
}
|
||||
}
|
||||
|
||||
for _, policy := range policies {
|
||||
var policyId = int64(policy.Id)
|
||||
storage, ok := this.storageMap[policyId]
|
||||
if ok {
|
||||
// 检查配置是否有变更
|
||||
if types.Int(policy.Version) != storage.Version() {
|
||||
err = storage.Close()
|
||||
if err != nil {
|
||||
remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "close policy '"+types.String(policyId)+"' failed: "+err.Error())
|
||||
|
||||
// 继续往下执行
|
||||
}
|
||||
|
||||
if len(policy.Options) > 0 {
|
||||
err = json.Unmarshal([]byte(policy.Options), storage.Config())
|
||||
if err != nil {
|
||||
remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "unmarshal policy '"+types.String(policyId)+"' config failed: "+err.Error())
|
||||
storage.SetOk(false)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
storage.SetVersion(types.Int(policy.Version))
|
||||
err := storage.Start()
|
||||
if err != nil {
|
||||
remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "start policy '"+types.String(policyId)+"' failed: "+err.Error())
|
||||
continue
|
||||
}
|
||||
storage.SetOk(true)
|
||||
remotelogs.Println("ACCESS_LOG_STORAGE_MANAGER", "restart policy '"+types.String(policyId)+"'")
|
||||
}
|
||||
} else {
|
||||
storage, err := this.createStorage(policy.Type, []byte(policy.Options))
|
||||
if err != nil {
|
||||
remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "create policy '"+types.String(policyId)+"' failed: "+err.Error())
|
||||
continue
|
||||
}
|
||||
storage.SetVersion(types.Int(policy.Version))
|
||||
this.storageMap[policyId] = storage
|
||||
err = storage.Start()
|
||||
if err != nil {
|
||||
remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "start policy '"+types.String(policyId)+"' failed: "+err.Error())
|
||||
continue
|
||||
}
|
||||
storage.SetOk(true)
|
||||
remotelogs.Println("ACCESS_LOG_STORAGE_MANAGER", "start policy '"+types.String(policyId)+"'")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *StorageManager) createStorage(storageType string, optionsJSON []byte) (StorageInterface, error) {
|
||||
switch storageType {
|
||||
case serverconfigs.AccessLogStorageTypeFile:
|
||||
var config = &serverconfigs.AccessLogFileStorageConfig{}
|
||||
if len(optionsJSON) > 0 {
|
||||
err := json.Unmarshal(optionsJSON, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return NewFileStorage(config), nil
|
||||
case serverconfigs.AccessLogStorageTypeES:
|
||||
var config = &serverconfigs.AccessLogESStorageConfig{}
|
||||
if len(optionsJSON) > 0 {
|
||||
err := json.Unmarshal(optionsJSON, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return NewESStorage(config), nil
|
||||
case serverconfigs.AccessLogStorageTypeTCP:
|
||||
var config = &serverconfigs.AccessLogTCPStorageConfig{}
|
||||
if len(optionsJSON) > 0 {
|
||||
err := json.Unmarshal(optionsJSON, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return NewTCPStorage(config), nil
|
||||
case serverconfigs.AccessLogStorageTypeSyslog:
|
||||
var config = &serverconfigs.AccessLogSyslogStorageConfig{}
|
||||
if len(optionsJSON) > 0 {
|
||||
err := json.Unmarshal(optionsJSON, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return NewSyslogStorage(config), nil
|
||||
case serverconfigs.AccessLogStorageTypeCommand:
|
||||
var config = &serverconfigs.AccessLogCommandStorageConfig{}
|
||||
if len(optionsJSON) > 0 {
|
||||
err := json.Unmarshal(optionsJSON, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return NewCommandStorage(config), nil
|
||||
}
|
||||
|
||||
return nil, errors.New("invalid policy type '" + storageType + "'")
|
||||
}
|
||||
17
internal/accesslogs/storage_manager_test.go
Normal file
17
internal/accesslogs/storage_manager_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package accesslogs
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStorageManager_Loop(t *testing.T) {
|
||||
dbs.NotifyReady()
|
||||
|
||||
var storage = NewStorageManager()
|
||||
err := storage.Loop()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(storage.storageMap)
|
||||
}
|
||||
15
internal/accesslogs/storage_manager_write.go
Normal file
15
internal/accesslogs/storage_manager_write.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
//go:build !plus
|
||||
// +build !plus
|
||||
|
||||
package accesslogs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
// 写入日志
|
||||
func (this *StorageManager) Write(policyId int64, accessLogs []*pb.HTTPAccessLog) error {
|
||||
return nil
|
||||
}
|
||||
137
internal/accesslogs/storage_syslog.go
Normal file
137
internal/accesslogs/storage_syslog.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package accesslogs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type SyslogStorageProtocol = string
|
||||
|
||||
const (
|
||||
SyslogStorageProtocolTCP SyslogStorageProtocol = "tcp"
|
||||
SyslogStorageProtocolUDP SyslogStorageProtocol = "udp"
|
||||
SyslogStorageProtocolNone SyslogStorageProtocol = "none"
|
||||
SyslogStorageProtocolSocket SyslogStorageProtocol = "socket"
|
||||
)
|
||||
|
||||
type SyslogStoragePriority = int
|
||||
|
||||
// SyslogStorage syslog存储策略
|
||||
type SyslogStorage struct {
|
||||
BaseStorage
|
||||
|
||||
config *serverconfigs.AccessLogSyslogStorageConfig
|
||||
|
||||
exe string
|
||||
}
|
||||
|
||||
func NewSyslogStorage(config *serverconfigs.AccessLogSyslogStorageConfig) *SyslogStorage {
|
||||
return &SyslogStorage{config: config}
|
||||
}
|
||||
|
||||
func (this *SyslogStorage) Config() interface{} {
|
||||
return this.config
|
||||
}
|
||||
|
||||
// Start 开启
|
||||
func (this *SyslogStorage) Start() error {
|
||||
if runtime.GOOS != "linux" {
|
||||
return errors.New("'syslog' storage only works on linux")
|
||||
}
|
||||
|
||||
exe, err := exec.LookPath("logger")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
this.exe = exe
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 写入日志
|
||||
func (this *SyslogStorage) Write(accessLogs []*pb.HTTPAccessLog) error {
|
||||
if len(accessLogs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
args := []string{}
|
||||
if len(this.config.Tag) > 0 {
|
||||
args = append(args, "-t", this.config.Tag)
|
||||
}
|
||||
|
||||
if this.config.Priority >= 0 {
|
||||
args = append(args, "-p", strconv.Itoa(this.config.Priority))
|
||||
}
|
||||
|
||||
switch this.config.Protocol {
|
||||
case SyslogStorageProtocolTCP:
|
||||
args = append(args, "-T")
|
||||
if len(this.config.ServerAddr) > 0 {
|
||||
args = append(args, "-n", this.config.ServerAddr)
|
||||
}
|
||||
if this.config.ServerPort > 0 {
|
||||
args = append(args, "-P", strconv.Itoa(this.config.ServerPort))
|
||||
}
|
||||
case SyslogStorageProtocolUDP:
|
||||
args = append(args, "-d")
|
||||
if len(this.config.ServerAddr) > 0 {
|
||||
args = append(args, "-n", this.config.ServerAddr)
|
||||
}
|
||||
if this.config.ServerPort > 0 {
|
||||
args = append(args, "-P", strconv.Itoa(this.config.ServerPort))
|
||||
}
|
||||
case SyslogStorageProtocolSocket:
|
||||
args = append(args, "-u")
|
||||
args = append(args, this.config.Socket)
|
||||
case SyslogStorageProtocolNone:
|
||||
// do nothing
|
||||
}
|
||||
|
||||
args = append(args, "-S", "10240")
|
||||
|
||||
cmd := exec.Command(this.exe, args...)
|
||||
w, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, accessLog := range accessLogs {
|
||||
data, err := this.Marshal(accessLog)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
continue
|
||||
}
|
||||
_, err = w.Write(data)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
}
|
||||
|
||||
_, err = w.Write([]byte("\n"))
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
_ = w.Close()
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close 关闭
|
||||
func (this *SyslogStorage) Close() error {
|
||||
return nil
|
||||
}
|
||||
111
internal/accesslogs/storage_tcp.go
Normal file
111
internal/accesslogs/storage_tcp.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package accesslogs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// TCPStorage TCP存储策略
|
||||
type TCPStorage struct {
|
||||
BaseStorage
|
||||
|
||||
config *serverconfigs.AccessLogTCPStorageConfig
|
||||
|
||||
writeLocker sync.Mutex
|
||||
|
||||
connLocker sync.Mutex
|
||||
conn net.Conn
|
||||
}
|
||||
|
||||
func NewTCPStorage(config *serverconfigs.AccessLogTCPStorageConfig) *TCPStorage {
|
||||
return &TCPStorage{config: config}
|
||||
}
|
||||
|
||||
func (this *TCPStorage) Config() interface{} {
|
||||
return this.config
|
||||
}
|
||||
|
||||
// Start 开启
|
||||
func (this *TCPStorage) Start() error {
|
||||
if len(this.config.Network) == 0 {
|
||||
return errors.New("'network' should not be empty")
|
||||
}
|
||||
if len(this.config.Addr) == 0 {
|
||||
return errors.New("'addr' should not be empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 写入日志
|
||||
func (this *TCPStorage) Write(accessLogs []*pb.HTTPAccessLog) error {
|
||||
if len(accessLogs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := this.connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn := this.conn
|
||||
if conn == nil {
|
||||
return errors.New("connection should not be nil")
|
||||
}
|
||||
|
||||
this.writeLocker.Lock()
|
||||
defer this.writeLocker.Unlock()
|
||||
|
||||
for _, accessLog := range accessLogs {
|
||||
data, err := this.Marshal(accessLog)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
continue
|
||||
}
|
||||
_, err = conn.Write(data)
|
||||
if err != nil {
|
||||
_ = this.Close()
|
||||
break
|
||||
}
|
||||
_, err = conn.Write([]byte("\n"))
|
||||
if err != nil {
|
||||
_ = this.Close()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close 关闭
|
||||
func (this *TCPStorage) Close() error {
|
||||
this.connLocker.Lock()
|
||||
defer this.connLocker.Unlock()
|
||||
|
||||
if this.conn != nil {
|
||||
err := this.conn.Close()
|
||||
this.conn = nil
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *TCPStorage) connect() error {
|
||||
this.connLocker.Lock()
|
||||
defer this.connLocker.Unlock()
|
||||
|
||||
if this.conn != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
conn, err := net.Dial(this.config.Network, this.config.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
this.conn = conn
|
||||
|
||||
return nil
|
||||
}
|
||||
72
internal/accesslogs/storage_tcp_test.go
Normal file
72
internal/accesslogs/storage_tcp_test.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package accesslogs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestTCPStorage_Write(t *testing.T) {
|
||||
go func() {
|
||||
server, err := net.Listen("tcp", "127.0.0.1:9981")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
for {
|
||||
conn, err := server.Accept()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
n, err := conn.Read(buf)
|
||||
if n > 0 {
|
||||
t.Log(string(buf[:n]))
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
_ = server.Close()
|
||||
}()
|
||||
|
||||
storage := NewTCPStorage(&serverconfigs.AccessLogTCPStorageConfig{
|
||||
Network: "tcp",
|
||||
Addr: "127.0.0.1:9981",
|
||||
})
|
||||
err := storage.Start()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
{
|
||||
err = storage.Write([]*pb.HTTPAccessLog{
|
||||
{
|
||||
RequestMethod: "POST",
|
||||
RequestPath: "/1",
|
||||
TimeLocal: time.Now().Format("2/Jan/2006:15:04:05 -0700"),
|
||||
},
|
||||
{
|
||||
RequestMethod: "GET",
|
||||
RequestPath: "/2",
|
||||
TimeLocal: time.Now().Format("2/Jan/2006:15:04:05 -0700"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
err = storage.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
24
internal/accesslogs/tests/command_storage.php
Normal file
24
internal/accesslogs/tests/command_storage.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
// test command storage
|
||||
|
||||
// open access log file
|
||||
$fp = fopen("/tmp/goedge-command-storage.log", "a+");
|
||||
|
||||
// read access logs from stdin
|
||||
$stdin = fopen("php://stdin", "r");
|
||||
while(true) {
|
||||
if (feof($stdin)) {
|
||||
break;
|
||||
}
|
||||
$line = fgets($stdin);
|
||||
|
||||
// write to access log file
|
||||
fwrite($fp, $line);
|
||||
}
|
||||
|
||||
// close file pointers
|
||||
fclose($fp);
|
||||
fclose($stdin);
|
||||
|
||||
?>
|
||||
8
internal/acme/account.go
Normal file
8
internal/acme/account.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package acme
|
||||
|
||||
type Account struct {
|
||||
EABKid string
|
||||
EABKey string
|
||||
}
|
||||
@@ -63,6 +63,7 @@ func TestGenerate(t *testing.T) {
|
||||
}
|
||||
|
||||
config := lego.NewConfig(myUser)
|
||||
config.CADirURL = "https://acme.zerossl.com/v2/DV90"
|
||||
config.Certificate.KeyType = certcrypto.RSA2048
|
||||
|
||||
client, err := lego.NewClient(config)
|
||||
@@ -91,3 +92,53 @@ func TestGenerate(t *testing.T) {
|
||||
}
|
||||
t.Log(certificates)
|
||||
}
|
||||
|
||||
func TestGenerate_EAB(t *testing.T) {
|
||||
acmelog.Logger = log.New(ioutil.Discard, "", log.LstdFlags)
|
||||
|
||||
// 生成私钥
|
||||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
myUser := &MyUser{
|
||||
Email: "test1@teaos.cn",
|
||||
key: privateKey,
|
||||
}
|
||||
|
||||
config := lego.NewConfig(myUser)
|
||||
config.CADirURL = "https://acme.zerossl.com/v2/DV90"
|
||||
config.Certificate.KeyType = certcrypto.RSA2048
|
||||
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = client.Challenge.SetDNS01Provider(&MyProvider{t: t})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// New users will need to register
|
||||
var reg *registration.Resource
|
||||
if client.GetExternalAccountRequired() {
|
||||
reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
|
||||
TermsOfServiceAgreed: true,
|
||||
Kid: "KID",
|
||||
HmacEncoded: "HAMC KEY",
|
||||
})
|
||||
} else {
|
||||
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
}
|
||||
myUser.Registration = reg
|
||||
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: []string{"teaos.cn"},
|
||||
Bundle: true,
|
||||
}
|
||||
certificates, err := client.Certificate.Obtain(request)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(certificates)
|
||||
}
|
||||
|
||||
@@ -2,37 +2,42 @@ package acme
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/dnstypes"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type DNSProvider struct {
|
||||
raw dnsclients.ProviderInterface
|
||||
raw dnsclients.ProviderInterface
|
||||
dnsDomain string
|
||||
}
|
||||
|
||||
func NewDNSProvider(raw dnsclients.ProviderInterface) *DNSProvider {
|
||||
return &DNSProvider{raw: raw}
|
||||
func NewDNSProvider(raw dnsclients.ProviderInterface, dnsDomain string) *DNSProvider {
|
||||
return &DNSProvider{
|
||||
raw: raw,
|
||||
dnsDomain: dnsDomain,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value := dns01.GetRecord(domain, keyAuth)
|
||||
|
||||
// 设置记录
|
||||
index := strings.Index(fqdn, "."+domain)
|
||||
index := strings.Index(fqdn, "."+this.dnsDomain)
|
||||
if index < 0 {
|
||||
return errors.New("invalid fqdn value")
|
||||
}
|
||||
recordName := fqdn[:index]
|
||||
record, err := this.raw.QueryRecord(domain, recordName, dnsclients.RecordTypeTXT)
|
||||
record, err := this.raw.QueryRecord(this.dnsDomain, recordName, dnstypes.RecordTypeTXT)
|
||||
if err != nil {
|
||||
return errors.New("query DNS record failed: " + err.Error())
|
||||
}
|
||||
if record == nil {
|
||||
err = this.raw.AddRecord(domain, &dnsclients.Record{
|
||||
err = this.raw.AddRecord(this.dnsDomain, &dnstypes.Record{
|
||||
Id: "",
|
||||
Name: recordName,
|
||||
Type: dnsclients.RecordTypeTXT,
|
||||
Type: dnstypes.RecordTypeTXT,
|
||||
Value: value,
|
||||
Route: this.raw.DefaultRoute(),
|
||||
})
|
||||
@@ -40,9 +45,9 @@ func (this *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
return errors.New("create DNS record failed: " + err.Error())
|
||||
}
|
||||
} else {
|
||||
err = this.raw.UpdateRecord(domain, record, &dnsclients.Record{
|
||||
err = this.raw.UpdateRecord(this.dnsDomain, record, &dnstypes.Record{
|
||||
Name: recordName,
|
||||
Type: dnsclients.RecordTypeTXT,
|
||||
Type: dnstypes.RecordTypeTXT,
|
||||
Value: value,
|
||||
Route: this.raw.DefaultRoute(),
|
||||
})
|
||||
|
||||
43
internal/acme/providers.go
Normal file
43
internal/acme/providers.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package acme
|
||||
|
||||
const DefaultProviderCode = "letsencrypt"
|
||||
|
||||
type Provider struct {
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
Description string `json:"description"`
|
||||
APIURL string `json:"apiURL"`
|
||||
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 {
|
||||
return provider
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
@@ -33,6 +34,14 @@ func (this *Request) OnAuth(onAuth AuthCallback) {
|
||||
}
|
||||
|
||||
func (this *Request) Run() (certData []byte, keyData []byte, err error) {
|
||||
if this.task.Provider == nil {
|
||||
err = errors.New("provider should not be nil")
|
||||
return
|
||||
}
|
||||
if this.task.Provider.RequireEAB && this.task.Account == nil {
|
||||
err = errors.New("account should not be nil when provider require EAB")
|
||||
}
|
||||
|
||||
switch this.task.AuthType {
|
||||
case AuthTypeDNS:
|
||||
return this.runDNS()
|
||||
@@ -68,6 +77,8 @@ func (this *Request) runDNS() (certData []byte, keyData []byte, err error) {
|
||||
|
||||
config := lego.NewConfig(this.task.User)
|
||||
config.Certificate.KeyType = certcrypto.RSA2048
|
||||
config.CADirURL = this.task.Provider.APIURL
|
||||
config.UserAgent = teaconst.ProductName + "/" + teaconst.Version
|
||||
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
@@ -82,17 +93,32 @@ func (this *Request) runDNS() (certData []byte, keyData []byte, err error) {
|
||||
return nil, nil, err
|
||||
}
|
||||
} else {
|
||||
resource, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
err = this.task.User.Register(resource)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
if this.task.Provider.RequireEAB {
|
||||
resource, err := client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
|
||||
TermsOfServiceAgreed: true,
|
||||
Kid: this.task.Account.EABKid,
|
||||
HmacEncoded: this.task.Account.EABKey,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, errors.New("register user failed: " + err.Error())
|
||||
}
|
||||
err = this.task.User.Register(resource)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
} else {
|
||||
resource, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
err = this.task.User.Register(resource)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = client.Challenge.SetDNS01Provider(NewDNSProvider(this.task.DNSProvider))
|
||||
err = client.Challenge.SetDNS01Provider(NewDNSProvider(this.task.DNSProvider, this.task.DNSDomain))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -104,7 +130,7 @@ func (this *Request) runDNS() (certData []byte, keyData []byte, err error) {
|
||||
}
|
||||
certResource, err := client.Certificate.Obtain(request)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, errors.New("obtain cert failed: " + err.Error())
|
||||
}
|
||||
|
||||
return certResource.Certificate, certResource.PrivateKey, nil
|
||||
@@ -122,6 +148,8 @@ func (this *Request) runHTTP() (certData []byte, keyData []byte, err error) {
|
||||
|
||||
config := lego.NewConfig(this.task.User)
|
||||
config.Certificate.KeyType = certcrypto.RSA2048
|
||||
config.CADirURL = this.task.Provider.APIURL
|
||||
config.UserAgent = teaconst.ProductName + "/" + teaconst.Version
|
||||
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
@@ -136,13 +164,28 @@ func (this *Request) runHTTP() (certData []byte, keyData []byte, err error) {
|
||||
return nil, nil, err
|
||||
}
|
||||
} else {
|
||||
resource, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
err = this.task.User.Register(resource)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
if this.task.Provider.RequireEAB {
|
||||
resource, err := client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
|
||||
TermsOfServiceAgreed: true,
|
||||
Kid: this.task.Account.EABKid,
|
||||
HmacEncoded: this.task.Account.EABKey,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, errors.New("register user failed: " + err.Error())
|
||||
}
|
||||
err = this.task.User.Register(resource)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
} else {
|
||||
resource, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
err = this.task.User.Register(resource)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,10 +39,10 @@ func TestRequest_Run_DNS(t *testing.T) {
|
||||
|
||||
req := NewRequest(&Task{
|
||||
User: user,
|
||||
Type: TaskTypeDNS,
|
||||
AuthType: AuthTypeDNS,
|
||||
DNSProvider: dnsProvider,
|
||||
DNSDomain: "yun4s.cn",
|
||||
Domains: []string{"yun4s.cn"},
|
||||
Domains: []string{"www.yun4s.cn"},
|
||||
})
|
||||
certData, keyData, err := req.Run()
|
||||
if err != nil {
|
||||
@@ -74,9 +74,9 @@ func TestRequest_Run_HTTP(t *testing.T) {
|
||||
}
|
||||
|
||||
req := NewRequest(&Task{
|
||||
User: user,
|
||||
Type: TaskTypeHTTP,
|
||||
Domains: []string{"teaos.cn", "www.teaos.cn", "meloy.cn"},
|
||||
User: user,
|
||||
AuthType: AuthTypeHTTP,
|
||||
Domains: []string{"teaos.cn", "www.teaos.cn", "meloy.cn"},
|
||||
})
|
||||
certData, keyData, err := req.runHTTP()
|
||||
if err != nil {
|
||||
|
||||
@@ -10,6 +10,8 @@ const (
|
||||
)
|
||||
|
||||
type Task struct {
|
||||
Provider *Provider
|
||||
Account *Account
|
||||
User *User
|
||||
AuthType AuthType
|
||||
Domains []string
|
||||
|
||||
@@ -2,8 +2,11 @@ package apps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"github.com/iwind/gosock/pkg/gosock"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
@@ -11,7 +14,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// App命令帮助
|
||||
// AppCmd App命令帮助
|
||||
type AppCmd struct {
|
||||
product string
|
||||
version string
|
||||
@@ -20,10 +23,14 @@ type AppCmd struct {
|
||||
appendStrings []string
|
||||
|
||||
directives []*Directive
|
||||
|
||||
sock *gosock.Sock
|
||||
}
|
||||
|
||||
func NewAppCmd() *AppCmd {
|
||||
return &AppCmd{}
|
||||
return &AppCmd{
|
||||
sock: gosock.NewTmpSock(teaconst.ProcessName),
|
||||
}
|
||||
}
|
||||
|
||||
type CommandHelpOption struct {
|
||||
@@ -31,25 +38,25 @@ type CommandHelpOption struct {
|
||||
Description string
|
||||
}
|
||||
|
||||
// 产品
|
||||
// Product 产品
|
||||
func (this *AppCmd) Product(product string) *AppCmd {
|
||||
this.product = product
|
||||
return this
|
||||
}
|
||||
|
||||
// 版本
|
||||
// Version 版本
|
||||
func (this *AppCmd) Version(version string) *AppCmd {
|
||||
this.version = version
|
||||
return this
|
||||
}
|
||||
|
||||
// 使用方法
|
||||
// Usage 使用方法
|
||||
func (this *AppCmd) Usage(usage string) *AppCmd {
|
||||
this.usage = usage
|
||||
return this
|
||||
}
|
||||
|
||||
// 选项
|
||||
// Option 选项
|
||||
func (this *AppCmd) Option(code string, description string) *AppCmd {
|
||||
this.options = append(this.options, &CommandHelpOption{
|
||||
Code: code,
|
||||
@@ -58,13 +65,13 @@ func (this *AppCmd) Option(code string, description string) *AppCmd {
|
||||
return this
|
||||
}
|
||||
|
||||
// 附加内容
|
||||
// Append 附加内容
|
||||
func (this *AppCmd) Append(appendString string) *AppCmd {
|
||||
this.appendStrings = append(this.appendStrings, appendString)
|
||||
return this
|
||||
}
|
||||
|
||||
// 打印
|
||||
// Print 打印
|
||||
func (this *AppCmd) Print() {
|
||||
fmt.Println(this.product + " v" + this.version)
|
||||
|
||||
@@ -103,7 +110,7 @@ func (this *AppCmd) Print() {
|
||||
}
|
||||
}
|
||||
|
||||
// 添加指令
|
||||
// On 添加指令
|
||||
func (this *AppCmd) On(arg string, callback func()) {
|
||||
this.directives = append(this.directives, &Directive{
|
||||
Arg: arg,
|
||||
@@ -111,7 +118,7 @@ func (this *AppCmd) On(arg string, callback func()) {
|
||||
})
|
||||
}
|
||||
|
||||
// 运行
|
||||
// Run 运行
|
||||
func (this *AppCmd) Run(main func()) {
|
||||
// 获取参数
|
||||
args := os.Args[1:]
|
||||
@@ -150,9 +157,6 @@ func (this *AppCmd) Run(main func()) {
|
||||
return
|
||||
}
|
||||
|
||||
// 记录PID
|
||||
_ = this.writePid()
|
||||
|
||||
// 日志
|
||||
writer := new(LogWriter)
|
||||
writer.Init()
|
||||
@@ -164,7 +168,7 @@ func (this *AppCmd) Run(main func()) {
|
||||
|
||||
// 版本号
|
||||
func (this *AppCmd) runVersion() {
|
||||
fmt.Println(this.product+" v"+this.version, "(build: "+runtime.Version(), runtime.GOOS, runtime.GOARCH+")")
|
||||
fmt.Println(this.product+" v"+this.version, "(build: "+runtime.Version(), runtime.GOOS, runtime.GOARCH, teaconst.Tag+")")
|
||||
}
|
||||
|
||||
// 帮助
|
||||
@@ -174,9 +178,9 @@ func (this *AppCmd) runHelp() {
|
||||
|
||||
// 启动
|
||||
func (this *AppCmd) runStart() {
|
||||
proc := this.checkPid()
|
||||
if proc != nil {
|
||||
fmt.Println(this.product+" already started, pid:", proc.Pid)
|
||||
var pid = this.getPID()
|
||||
if pid > 0 {
|
||||
fmt.Println(this.product+" already started, pid:", pid)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -192,18 +196,15 @@ func (this *AppCmd) runStart() {
|
||||
|
||||
// 停止
|
||||
func (this *AppCmd) runStop() {
|
||||
proc := this.checkPid()
|
||||
if proc == nil {
|
||||
var pid = this.getPID()
|
||||
if pid == 0 {
|
||||
fmt.Println(this.product + " not started yet")
|
||||
return
|
||||
}
|
||||
|
||||
// 停止进程
|
||||
_ = proc.Kill()
|
||||
_, _ = this.sock.Send(&gosock.Command{Code: "stop"})
|
||||
|
||||
// 在Windows上经常不能及时释放资源
|
||||
_ = DeletePid(Tea.Root + "/bin/pid")
|
||||
fmt.Println(this.product+" stopped ok, pid:", proc.Pid)
|
||||
fmt.Println(this.product+" stopped ok, pid:", types.String(pid))
|
||||
}
|
||||
|
||||
// 重启
|
||||
@@ -215,20 +216,24 @@ func (this *AppCmd) runRestart() {
|
||||
|
||||
// 状态
|
||||
func (this *AppCmd) runStatus() {
|
||||
proc := this.checkPid()
|
||||
if proc == nil {
|
||||
var pid = this.getPID()
|
||||
if pid == 0 {
|
||||
fmt.Println(this.product + " not started yet")
|
||||
} else {
|
||||
fmt.Println(this.product + " is running, pid: " + fmt.Sprintf("%d", proc.Pid))
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(this.product + " is running, pid: " + types.String(pid))
|
||||
}
|
||||
|
||||
// 检查PID
|
||||
func (this *AppCmd) checkPid() *os.Process {
|
||||
return CheckPid(Tea.Root + "/bin/pid")
|
||||
}
|
||||
// 获取当前的PID
|
||||
func (this *AppCmd) getPID() int {
|
||||
if !this.sock.IsListening() {
|
||||
return 0
|
||||
}
|
||||
|
||||
// 写入PID
|
||||
func (this *AppCmd) writePid() error {
|
||||
return WritePid(Tea.Root + "/bin/pid")
|
||||
reply, err := this.sock.Send(&gosock.Command{Code: "pid"})
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return maps.NewMap(reply.Params).GetInt("pid")
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package apps
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// lock file
|
||||
func LockFile(fp *os.File) error {
|
||||
return syscall.Flock(int(fp.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)
|
||||
}
|
||||
|
||||
func UnlockFile(fp *os.File) error {
|
||||
return syscall.Flock(int(fp.Fd()), syscall.LOCK_UN)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package apps
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
// lock file
|
||||
func LockFile(fp *os.File) error {
|
||||
return errors.New("not implemented on windows")
|
||||
}
|
||||
|
||||
func UnlockFile(fp *os.File) error {
|
||||
return errors.New("not implemented on windows")
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var pidFileList = []*os.File{}
|
||||
|
||||
// 检查Pid
|
||||
func CheckPid(path string) *os.Process {
|
||||
// windows上打开的文件是不能删除的
|
||||
if runtime.GOOS == "windows" {
|
||||
if os.Remove(path) == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
|
||||
// 是否能取得Lock
|
||||
err = LockFile(file)
|
||||
if err == nil {
|
||||
_ = UnlockFile(file)
|
||||
return nil
|
||||
}
|
||||
|
||||
pidBytes, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
pid := types.Int(string(pidBytes))
|
||||
|
||||
if pid <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
proc, _ := os.FindProcess(pid)
|
||||
return proc
|
||||
}
|
||||
|
||||
// 写入Pid
|
||||
func WritePid(path string) error {
|
||||
fp, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_RDONLY, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
err = LockFile(fp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
pidFileList = append(pidFileList, fp) // hold the file pointers
|
||||
|
||||
_, err = fp.WriteString(fmt.Sprintf("%d", os.Getpid()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 写入Ppid
|
||||
func WritePpid(path string) error {
|
||||
fp, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_RDONLY, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
err = LockFile(fp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
pidFileList = append(pidFileList, fp) // hold the file pointers
|
||||
|
||||
_, err = fp.WriteString(fmt.Sprintf("%d", os.Getppid()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 删除Pid
|
||||
func DeletePid(path string) error {
|
||||
_, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
for _, fp := range pidFileList {
|
||||
_ = UnlockFile(fp)
|
||||
_ = fp.Close()
|
||||
}
|
||||
return os.Remove(path)
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package configs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||||
"github.com/go-yaml/yaml"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
@@ -11,9 +10,8 @@ import (
|
||||
)
|
||||
|
||||
var sharedAPIConfig *APIConfig = nil
|
||||
var PaddingId string
|
||||
|
||||
// API节点配置
|
||||
// APIConfig API节点配置
|
||||
type APIConfig struct {
|
||||
NodeId string `yaml:"nodeId" json:"nodeId"`
|
||||
Secret string `yaml:"secret" json:"secret"`
|
||||
@@ -21,7 +19,7 @@ type APIConfig struct {
|
||||
numberId int64 // 数字ID
|
||||
}
|
||||
|
||||
// 获取共享配置
|
||||
// SharedAPIConfig 获取共享配置
|
||||
func SharedAPIConfig() (*APIConfig, error) {
|
||||
sharedLocker.Lock()
|
||||
defer sharedLocker.Unlock()
|
||||
@@ -72,7 +70,7 @@ func SharedAPIConfig() (*APIConfig, error) {
|
||||
{
|
||||
dbConfigFile := Tea.ConfigFile("db.yaml")
|
||||
_, err := os.Stat(dbConfigFile)
|
||||
if err == nil {
|
||||
if err != nil {
|
||||
paths := []string{}
|
||||
homeDir, homeErr := os.UserHomeDir()
|
||||
if homeErr == nil {
|
||||
@@ -96,18 +94,18 @@ func SharedAPIConfig() (*APIConfig, error) {
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// 设置数字ID
|
||||
// SetNumberId 设置数字ID
|
||||
func (this *APIConfig) SetNumberId(numberId int64) {
|
||||
this.numberId = numberId
|
||||
PaddingId = fmt.Sprintf("%08d", numberId)
|
||||
teaconst.NodeId = numberId
|
||||
}
|
||||
|
||||
// 获取数字ID
|
||||
// NumberId 获取数字ID
|
||||
func (this *APIConfig) NumberId() int64 {
|
||||
return this.numberId
|
||||
}
|
||||
|
||||
// 保存到文件
|
||||
// WriteFile 保存到文件
|
||||
func (this *APIConfig) WriteFile(path string) error {
|
||||
data, err := yaml.Marshal(this)
|
||||
if err != nil {
|
||||
|
||||
9
internal/const/build.go
Normal file
9
internal/const/build.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
//go:build !plus
|
||||
// +build !plus
|
||||
|
||||
package teaconst
|
||||
|
||||
const BuildCommunity = true
|
||||
const BuildPlus = false
|
||||
const Tag = "community"
|
||||
@@ -1,7 +1,7 @@
|
||||
package teaconst
|
||||
|
||||
const (
|
||||
Version = "0.1.1"
|
||||
Version = "0.4.1"
|
||||
|
||||
ProductName = "Edge API"
|
||||
ProcessName = "edge-api"
|
||||
@@ -18,9 +18,10 @@ const (
|
||||
|
||||
// 其他节点版本号,用来检测是否有需要升级的节点
|
||||
|
||||
NodeVersion = "0.1.1"
|
||||
UserNodeVersion = "0.0.7"
|
||||
AuthorityNodeVersion = "0.0.1"
|
||||
MonitorNodeVersion = "0.0.1"
|
||||
DNSNodeVersion = "0.0.1"
|
||||
NodeVersion = "0.4.1"
|
||||
UserNodeVersion = "0.3.1"
|
||||
AuthorityNodeVersion = "0.0.2"
|
||||
MonitorNodeVersion = "0.0.3"
|
||||
DNSNodeVersion = "0.2.1"
|
||||
ReportNodeVersion = "0.1.0"
|
||||
)
|
||||
|
||||
10
internal/const/vars.go
Normal file
10
internal/const/vars.go
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package teaconst
|
||||
|
||||
var (
|
||||
IsPlus = false
|
||||
MaxNodes int32 = 0
|
||||
NodeId int64 = 0
|
||||
Debug = false
|
||||
)
|
||||
@@ -33,7 +33,7 @@ func TestDB_Instance(t *testing.T) {
|
||||
if err == driver.ErrBadConn {
|
||||
return
|
||||
}
|
||||
t.Fatal(i, "exec:", err)
|
||||
t.Error(i, "exec:", err)
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
80
internal/db/models/accounts/user_account_daily_stat_dao.go
Normal file
80
internal/db/models/accounts/user_account_daily_stat_dao.go
Normal file
@@ -0,0 +1,80 @@
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package accounts
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
)
|
||||
22
internal/db/models/accounts/user_account_daily_stat_model.go
Normal file
22
internal/db/models/accounts/user_account_daily_stat_model.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package accounts
|
||||
|
||||
// UserAccountDailyStat 账户每日统计
|
||||
type UserAccountDailyStat struct {
|
||||
Id uint32 `field:"id"` // ID
|
||||
Day string `field:"day"` // YYYYMMDD
|
||||
Month string `field:"month"` // YYYYMM
|
||||
Income float64 `field:"income"` // 收入
|
||||
Expense float64 `field:"expense"` // 支出
|
||||
}
|
||||
|
||||
type UserAccountDailyStatOperator struct {
|
||||
Id interface{} // ID
|
||||
Day interface{} // YYYYMMDD
|
||||
Month interface{} // YYYYMM
|
||||
Income interface{} // 收入
|
||||
Expense interface{} // 支出
|
||||
}
|
||||
|
||||
func NewUserAccountDailyStatOperator() *UserAccountDailyStatOperator {
|
||||
return &UserAccountDailyStatOperator{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package accounts
|
||||
253
internal/db/models/accounts/user_account_dao.go
Normal file
253
internal/db/models/accounts/user_account_dao.go
Normal file
@@ -0,0 +1,253 @@
|
||||
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
|
||||
}
|
||||
18
internal/db/models/accounts/user_account_dao_test.go
Normal file
18
internal/db/models/accounts/user_account_dao_test.go
Normal file
@@ -0,0 +1,18 @@
|
||||
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")
|
||||
}
|
||||
128
internal/db/models/accounts/user_account_log_dao.go
Normal file
128
internal/db/models/accounts/user_account_log_dao.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"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", "%"+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", "%"+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
|
||||
}
|
||||
6
internal/db/models/accounts/user_account_log_dao_test.go
Normal file
6
internal/db/models/accounts/user_account_log_dao_test.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package accounts
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
)
|
||||
36
internal/db/models/accounts/user_account_log_model.go
Normal file
36
internal/db/models/accounts/user_account_log_model.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package accounts
|
||||
|
||||
// UserAccountLog 用户账户日志
|
||||
type UserAccountLog struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
UserId uint64 `field:"userId"` // 用户ID
|
||||
AccountId uint64 `field:"accountId"` // 账户ID
|
||||
Delta float64 `field:"delta"` // 操作余额的数量(可为负)
|
||||
DeltaFrozen float64 `field:"deltaFrozen"` // 操作冻结的数量(可为负)
|
||||
Total float64 `field:"total"` // 操作后余额
|
||||
TotalFrozen float64 `field:"totalFrozen"` // 操作后冻结余额
|
||||
EventType string `field:"eventType"` // 类型
|
||||
Description string `field:"description"` // 描述文字
|
||||
Day string `field:"day"` // YYYYMMDD
|
||||
CreatedAt uint64 `field:"createdAt"` // 时间
|
||||
Params string `field:"params"` // 参数
|
||||
}
|
||||
|
||||
type UserAccountLogOperator struct {
|
||||
Id interface{} // ID
|
||||
UserId interface{} // 用户ID
|
||||
AccountId interface{} // 账户ID
|
||||
Delta interface{} // 操作余额的数量(可为负)
|
||||
DeltaFrozen interface{} // 操作冻结的数量(可为负)
|
||||
Total interface{} // 操作后余额
|
||||
TotalFrozen interface{} // 操作后冻结余额
|
||||
EventType interface{} // 类型
|
||||
Description interface{} // 描述文字
|
||||
Day interface{} // YYYYMMDD
|
||||
CreatedAt interface{} // 时间
|
||||
Params interface{} // 参数
|
||||
}
|
||||
|
||||
func NewUserAccountLogOperator() *UserAccountLogOperator {
|
||||
return &UserAccountLogOperator{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package accounts
|
||||
20
internal/db/models/accounts/user_account_model.go
Normal file
20
internal/db/models/accounts/user_account_model.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package accounts
|
||||
|
||||
// UserAccount 用户账号
|
||||
type UserAccount struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
UserId uint64 `field:"userId"` // 用户ID
|
||||
Total float64 `field:"total"` // 可用总余额
|
||||
TotalFrozen float64 `field:"totalFrozen"` // 冻结余额
|
||||
}
|
||||
|
||||
type UserAccountOperator struct {
|
||||
Id interface{} // ID
|
||||
UserId interface{} // 用户ID
|
||||
Total interface{} // 可用总余额
|
||||
TotalFrozen interface{} // 冻结余额
|
||||
}
|
||||
|
||||
func NewUserAccountOperator() *UserAccountOperator {
|
||||
return &UserAccountOperator{}
|
||||
}
|
||||
1
internal/db/models/accounts/user_account_model_ext.go
Normal file
1
internal/db/models/accounts/user_account_model_ext.go
Normal file
@@ -0,0 +1 @@
|
||||
package accounts
|
||||
127
internal/db/models/acme/acme_provider_account_dao.go
Normal file
127
internal/db/models/acme/acme_provider_account_dao.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
)
|
||||
|
||||
const (
|
||||
ACMEProviderAccountStateEnabled = 1 // 已启用
|
||||
ACMEProviderAccountStateDisabled = 0 // 已禁用
|
||||
)
|
||||
|
||||
type ACMEProviderAccountDAO dbs.DAO
|
||||
|
||||
func NewACMEProviderAccountDAO() *ACMEProviderAccountDAO {
|
||||
return dbs.NewDAO(&ACMEProviderAccountDAO{
|
||||
DAOObject: dbs.DAOObject{
|
||||
DB: Tea.Env,
|
||||
Table: "edgeACMEProviderAccounts",
|
||||
Model: new(ACMEProviderAccount),
|
||||
PkName: "id",
|
||||
},
|
||||
}).(*ACMEProviderAccountDAO)
|
||||
}
|
||||
|
||||
var SharedACMEProviderAccountDAO *ACMEProviderAccountDAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedACMEProviderAccountDAO = NewACMEProviderAccountDAO()
|
||||
})
|
||||
}
|
||||
|
||||
// EnableACMEProviderAccount 启用条目
|
||||
func (this *ACMEProviderAccountDAO) EnableACMEProviderAccount(tx *dbs.Tx, id int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
Set("state", ACMEProviderAccountStateEnabled).
|
||||
Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// DisableACMEProviderAccount 禁用条目
|
||||
func (this *ACMEProviderAccountDAO) DisableACMEProviderAccount(tx *dbs.Tx, id int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
Set("state", ACMEProviderAccountStateDisabled).
|
||||
Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// FindEnabledACMEProviderAccount 查找启用中的条目
|
||||
func (this *ACMEProviderAccountDAO) FindEnabledACMEProviderAccount(tx *dbs.Tx, id int64) (*ACMEProviderAccount, error) {
|
||||
result, err := this.Query(tx).
|
||||
Pk(id).
|
||||
Attr("state", ACMEProviderAccountStateEnabled).
|
||||
Find()
|
||||
if result == nil {
|
||||
return nil, err
|
||||
}
|
||||
return result.(*ACMEProviderAccount), err
|
||||
}
|
||||
|
||||
// FindACMEProviderAccountName 根据主键查找名称
|
||||
func (this *ACMEProviderAccountDAO) FindACMEProviderAccountName(tx *dbs.Tx, id int64) (string, error) {
|
||||
return this.Query(tx).
|
||||
Pk(id).
|
||||
Result("name").
|
||||
FindStringCol("")
|
||||
}
|
||||
|
||||
// CreateAccount 创建账号
|
||||
func (this *ACMEProviderAccountDAO) CreateAccount(tx *dbs.Tx, name string, providerCode string, eabKid string, eabKey string) (int64, error) {
|
||||
var op = NewACMEProviderAccountOperator()
|
||||
op.Name = name
|
||||
op.ProviderCode = providerCode
|
||||
op.EabKid = eabKid
|
||||
op.EabKey = eabKey
|
||||
|
||||
op.IsOn = true
|
||||
op.State = ACMEProviderAccountStateEnabled
|
||||
return this.SaveInt64(tx, op)
|
||||
}
|
||||
|
||||
// UpdateAccount 修改账号
|
||||
func (this *ACMEProviderAccountDAO) UpdateAccount(tx *dbs.Tx, accountId int64, name string, eabKid string, eabKey string) error {
|
||||
if accountId <= 0 {
|
||||
return errors.New("invalid accountId")
|
||||
}
|
||||
var op = NewACMEProviderAccountOperator()
|
||||
op.Id = accountId
|
||||
op.Name = name
|
||||
op.EabKid = eabKid
|
||||
op.EabKey = eabKey
|
||||
return this.Save(tx, op)
|
||||
}
|
||||
|
||||
// CountAllEnabledAccounts 计算账号数量
|
||||
func (this *ACMEProviderAccountDAO) CountAllEnabledAccounts(tx *dbs.Tx) (int64, error) {
|
||||
return this.Query(tx).
|
||||
Count()
|
||||
}
|
||||
|
||||
// ListEnabledAccounts 查找单页账号
|
||||
func (this *ACMEProviderAccountDAO) ListEnabledAccounts(tx *dbs.Tx, offset int64, size int64) (result []*ACMEProviderAccount, err error) {
|
||||
_, err = this.Query(tx).
|
||||
State(ACMEProviderAccountStateEnabled).
|
||||
Offset(offset).
|
||||
Limit(size).
|
||||
DescPk().
|
||||
Slice(&result).
|
||||
FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// FindAllEnabledAccountsWithProviderCode 根据服务商代号查找账号
|
||||
func (this *ACMEProviderAccountDAO) FindAllEnabledAccountsWithProviderCode(tx *dbs.Tx, providerCode string) (result []*ACMEProviderAccount, err error) {
|
||||
_, err = this.Query(tx).
|
||||
State(ACMEProviderAccountStateEnabled).
|
||||
Attr("providerCode", providerCode).
|
||||
DescPk().
|
||||
Slice(&result).
|
||||
FindAll()
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
)
|
||||
28
internal/db/models/acme/acme_provider_account_model.go
Normal file
28
internal/db/models/acme/acme_provider_account_model.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package acme
|
||||
|
||||
// ACMEProviderAccount ACME提供商
|
||||
type ACMEProviderAccount struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
IsOn uint8 `field:"isOn"` // 是否启用
|
||||
Name string `field:"name"` // 名称
|
||||
ProviderCode string `field:"providerCode"` // 代号
|
||||
Error string `field:"error"` // 最后一条错误信息
|
||||
EabKid string `field:"eabKid"` // KID
|
||||
EabKey string `field:"eabKey"` // Key
|
||||
State uint8 `field:"state"` // 状态
|
||||
}
|
||||
|
||||
type ACMEProviderAccountOperator struct {
|
||||
Id interface{} // ID
|
||||
IsOn interface{} // 是否启用
|
||||
Name interface{} // 名称
|
||||
ProviderCode interface{} // 代号
|
||||
Error interface{} // 最后一条错误信息
|
||||
EabKid interface{} // KID
|
||||
EabKey interface{} // Key
|
||||
State interface{} // 状态
|
||||
}
|
||||
|
||||
func NewACMEProviderAccountOperator() *ACMEProviderAccountOperator {
|
||||
return &ACMEProviderAccountOperator{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package acme
|
||||
@@ -1,20 +1,26 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/acme"
|
||||
acmeutils "github.com/TeaOSLab/EdgeAPI/internal/acme"
|
||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models/dns"
|
||||
dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -44,7 +50,7 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
// 启用条目
|
||||
// EnableACMETask 启用条目
|
||||
func (this *ACMETaskDAO) EnableACMETask(tx *dbs.Tx, id int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -53,7 +59,7 @@ func (this *ACMETaskDAO) EnableACMETask(tx *dbs.Tx, id int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 禁用条目
|
||||
// DisableACMETask 禁用条目
|
||||
func (this *ACMETaskDAO) DisableACMETask(tx *dbs.Tx, id int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -62,7 +68,7 @@ func (this *ACMETaskDAO) DisableACMETask(tx *dbs.Tx, id int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 查找启用中的条目
|
||||
// FindEnabledACMETask 查找启用中的条目
|
||||
func (this *ACMETaskDAO) FindEnabledACMETask(tx *dbs.Tx, id int64) (*ACMETask, error) {
|
||||
result, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -74,7 +80,7 @@ func (this *ACMETaskDAO) FindEnabledACMETask(tx *dbs.Tx, id int64) (*ACMETask, e
|
||||
return result.(*ACMETask), err
|
||||
}
|
||||
|
||||
// 计算某个ACME用户相关的任务数量
|
||||
// CountACMETasksWithACMEUserId 计算某个ACME用户相关的任务数量
|
||||
func (this *ACMETaskDAO) CountACMETasksWithACMEUserId(tx *dbs.Tx, acmeUserId int64) (int64, error) {
|
||||
return this.Query(tx).
|
||||
State(ACMETaskStateEnabled).
|
||||
@@ -82,7 +88,7 @@ func (this *ACMETaskDAO) CountACMETasksWithACMEUserId(tx *dbs.Tx, acmeUserId int
|
||||
Count()
|
||||
}
|
||||
|
||||
// 计算某个DNS服务商相关的任务数量
|
||||
// CountACMETasksWithDNSProviderId 计算某个DNS服务商相关的任务数量
|
||||
func (this *ACMETaskDAO) CountACMETasksWithDNSProviderId(tx *dbs.Tx, dnsProviderId int64) (int64, error) {
|
||||
return this.Query(tx).
|
||||
State(ACMETaskStateEnabled).
|
||||
@@ -90,7 +96,7 @@ func (this *ACMETaskDAO) CountACMETasksWithDNSProviderId(tx *dbs.Tx, dnsProvider
|
||||
Count()
|
||||
}
|
||||
|
||||
// 停止某个证书相关任务
|
||||
// DisableAllTasksWithCertId 停止某个证书相关任务
|
||||
func (this *ACMETaskDAO) DisableAllTasksWithCertId(tx *dbs.Tx, certId int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Attr("certId", certId).
|
||||
@@ -99,7 +105,7 @@ func (this *ACMETaskDAO) DisableAllTasksWithCertId(tx *dbs.Tx, certId int64) err
|
||||
return err
|
||||
}
|
||||
|
||||
// 计算所有任务数量
|
||||
// CountAllEnabledACMETasks 计算所有任务数量
|
||||
func (this *ACMETaskDAO) CountAllEnabledACMETasks(tx *dbs.Tx, adminId int64, userId int64, isAvailable bool, isExpired bool, expiringDays int64, keyword string) (int64, error) {
|
||||
query := dbutils.NewQuery(tx, this, adminId, userId)
|
||||
if isAvailable || isExpired || expiringDays > 0 {
|
||||
@@ -130,7 +136,7 @@ func (this *ACMETaskDAO) CountAllEnabledACMETasks(tx *dbs.Tx, adminId int64, use
|
||||
Count()
|
||||
}
|
||||
|
||||
// 列出单页任务
|
||||
// ListEnabledACMETasks 列出单页任务
|
||||
func (this *ACMETaskDAO) ListEnabledACMETasks(tx *dbs.Tx, adminId int64, userId int64, isAvailable bool, isExpired bool, expiringDays int64, keyword string, offset int64, size int64) (result []*ACMETask, err error) {
|
||||
query := dbutils.NewQuery(tx, this, adminId, userId)
|
||||
if isAvailable || isExpired || expiringDays > 0 {
|
||||
@@ -161,8 +167,8 @@ func (this *ACMETaskDAO) ListEnabledACMETasks(tx *dbs.Tx, adminId int64, userId
|
||||
return
|
||||
}
|
||||
|
||||
// 创建任务
|
||||
func (this *ACMETaskDAO) CreateACMETask(tx *dbs.Tx, adminId int64, userId int64, authType acme.AuthType, acmeUserId int64, dnsProviderId int64, dnsDomain string, domains []string, autoRenew bool) (int64, error) {
|
||||
// CreateACMETask 创建任务
|
||||
func (this *ACMETaskDAO) CreateACMETask(tx *dbs.Tx, adminId int64, userId int64, authType acmeutils.AuthType, acmeUserId int64, dnsProviderId int64, dnsDomain string, domains []string, autoRenew bool, authURL string) (int64, error) {
|
||||
op := NewACMETaskOperator()
|
||||
op.AdminId = adminId
|
||||
op.UserId = userId
|
||||
@@ -182,6 +188,7 @@ func (this *ACMETaskDAO) CreateACMETask(tx *dbs.Tx, adminId int64, userId int64,
|
||||
}
|
||||
|
||||
op.AutoRenew = autoRenew
|
||||
op.AuthURL = authURL
|
||||
op.IsOn = true
|
||||
op.State = ACMETaskStateEnabled
|
||||
err := this.Save(tx, op)
|
||||
@@ -191,8 +198,8 @@ func (this *ACMETaskDAO) CreateACMETask(tx *dbs.Tx, adminId int64, userId int64,
|
||||
return types.Int64(op.Id), nil
|
||||
}
|
||||
|
||||
// 修改任务
|
||||
func (this *ACMETaskDAO) UpdateACMETask(tx *dbs.Tx, acmeTaskId int64, acmeUserId int64, dnsProviderId int64, dnsDomain string, domains []string, autoRenew bool) error {
|
||||
// UpdateACMETask 修改任务
|
||||
func (this *ACMETaskDAO) UpdateACMETask(tx *dbs.Tx, acmeTaskId int64, acmeUserId int64, dnsProviderId int64, dnsDomain string, domains []string, autoRenew bool, authURL string) error {
|
||||
if acmeTaskId <= 0 {
|
||||
return errors.New("invalid acmeTaskId")
|
||||
}
|
||||
@@ -214,11 +221,12 @@ func (this *ACMETaskDAO) UpdateACMETask(tx *dbs.Tx, acmeTaskId int64, acmeUserId
|
||||
}
|
||||
|
||||
op.AutoRenew = autoRenew
|
||||
op.AuthURL = authURL
|
||||
err := this.Save(tx, op)
|
||||
return err
|
||||
}
|
||||
|
||||
// 检查权限
|
||||
// CheckACMETask 检查权限
|
||||
func (this *ACMETaskDAO) CheckACMETask(tx *dbs.Tx, adminId int64, userId int64, acmeTaskId int64) (bool, error) {
|
||||
return dbutils.NewQuery(tx, this, adminId, userId).
|
||||
State(ACMETaskStateEnabled).
|
||||
@@ -226,7 +234,7 @@ func (this *ACMETaskDAO) CheckACMETask(tx *dbs.Tx, adminId int64, userId int64,
|
||||
Exist()
|
||||
}
|
||||
|
||||
// 设置任务关联的证书
|
||||
// UpdateACMETaskCert 设置任务关联的证书
|
||||
func (this *ACMETaskDAO) UpdateACMETaskCert(tx *dbs.Tx, taskId int64, certId int64) error {
|
||||
if taskId <= 0 {
|
||||
return errors.New("invalid taskId")
|
||||
@@ -239,7 +247,7 @@ func (this *ACMETaskDAO) UpdateACMETaskCert(tx *dbs.Tx, taskId int64, certId int
|
||||
return err
|
||||
}
|
||||
|
||||
// 执行任务并记录日志
|
||||
// RunTask 执行任务并记录日志
|
||||
func (this *ACMETaskDAO) RunTask(tx *dbs.Tx, taskId int64) (isOk bool, errMsg string, resultCertId int64) {
|
||||
isOk, errMsg, resultCertId = this.runTaskWithoutLog(tx, taskId)
|
||||
|
||||
@@ -279,13 +287,39 @@ func (this *ACMETaskDAO) runTaskWithoutLog(tx *dbs.Tx, taskId int64) (isOk bool,
|
||||
return
|
||||
}
|
||||
|
||||
privateKey, err := acme.ParsePrivateKeyFromBase64(user.PrivateKey)
|
||||
// 服务商
|
||||
if len(user.ProviderCode) == 0 {
|
||||
user.ProviderCode = acmeutils.DefaultProviderCode
|
||||
}
|
||||
var acmeProvider = acmeutils.FindProviderWithCode(user.ProviderCode)
|
||||
if acmeProvider == nil {
|
||||
errMsg = "服务商已不可用"
|
||||
return
|
||||
}
|
||||
|
||||
// 账号
|
||||
var acmeAccount *acmeutils.Account
|
||||
if user.AccountId > 0 {
|
||||
account, err := SharedACMEProviderAccountDAO.FindEnabledACMEProviderAccount(tx, int64(user.AccountId))
|
||||
if err != nil {
|
||||
errMsg = "查询ACME账号时出错:" + err.Error()
|
||||
return
|
||||
}
|
||||
if account != nil {
|
||||
acmeAccount = &acmeutils.Account{
|
||||
EABKid: account.EabKid,
|
||||
EABKey: account.EabKey,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
privateKey, err := acmeutils.ParsePrivateKeyFromBase64(user.PrivateKey)
|
||||
if err != nil {
|
||||
errMsg = "解析私钥时出错:" + err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
remoteUser := acme.NewUser(user.Email, privateKey, func(resource *registration.Resource) error {
|
||||
remoteUser := acmeutils.NewUser(user.Email, privateKey, func(resource *registration.Resource) error {
|
||||
resourceJSON, err := json.Marshal(resource)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -303,8 +337,8 @@ func (this *ACMETaskDAO) runTaskWithoutLog(tx *dbs.Tx, taskId int64) (isOk bool,
|
||||
}
|
||||
}
|
||||
|
||||
var acmeTask *acme.Task = nil
|
||||
if task.AuthType == acme.AuthTypeDNS {
|
||||
var acmeTask *acmeutils.Task = nil
|
||||
if task.AuthType == acmeutils.AuthTypeDNS {
|
||||
// DNS服务商
|
||||
dnsProvider, err := dns.SharedDNSProviderDAO.FindEnabledDNSProvider(tx, int64(task.DnsProviderId))
|
||||
if err != nil {
|
||||
@@ -331,26 +365,55 @@ func (this *ACMETaskDAO) runTaskWithoutLog(tx *dbs.Tx, taskId int64) (isOk bool,
|
||||
return
|
||||
}
|
||||
|
||||
acmeTask = &acme.Task{
|
||||
acmeTask = &acmeutils.Task{
|
||||
User: remoteUser,
|
||||
AuthType: acme.AuthTypeDNS,
|
||||
AuthType: acmeutils.AuthTypeDNS,
|
||||
DNSProvider: providerInterface,
|
||||
DNSDomain: task.DnsDomain,
|
||||
Domains: task.DecodeDomains(),
|
||||
}
|
||||
} else if task.AuthType == acme.AuthTypeHTTP {
|
||||
acmeTask = &acme.Task{
|
||||
} else if task.AuthType == acmeutils.AuthTypeHTTP {
|
||||
acmeTask = &acmeutils.Task{
|
||||
User: remoteUser,
|
||||
AuthType: acme.AuthTypeHTTP,
|
||||
AuthType: acmeutils.AuthTypeHTTP,
|
||||
Domains: task.DecodeDomains(),
|
||||
}
|
||||
}
|
||||
acmeTask.Provider = acmeProvider
|
||||
acmeTask.Account = acmeAccount
|
||||
|
||||
acmeRequest := acme.NewRequest(acmeTask)
|
||||
acmeRequest := acmeutils.NewRequest(acmeTask)
|
||||
acmeRequest.OnAuth(func(domain, token, keyAuth string) {
|
||||
err := SharedACMEAuthenticationDAO.CreateAuth(tx, taskId, domain, token, keyAuth)
|
||||
if err != nil {
|
||||
logs.Println("[ACME]write authentication to database error: " + err.Error())
|
||||
remotelogs.Error("ACME", "write authentication to database error: "+err.Error())
|
||||
} else {
|
||||
// 调用校验URL
|
||||
if len(task.AuthURL) > 0 {
|
||||
authJSON, err := json.Marshal(maps.Map{
|
||||
"domain": domain,
|
||||
"token": token,
|
||||
"key": keyAuth,
|
||||
})
|
||||
if err != nil {
|
||||
remotelogs.Error("ACME", "encode auth data failed: '"+task.AuthURL+"'")
|
||||
} else {
|
||||
client := utils.SharedHttpClient(5 * time.Second)
|
||||
req, err := http.NewRequest(http.MethodPost, task.AuthURL, bytes.NewReader(authJSON))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("User-Agent", teaconst.ProductName+"/"+teaconst.Version)
|
||||
if err != nil {
|
||||
remotelogs.Error("ACME", "parse auth url failed '"+task.AuthURL+"': "+err.Error())
|
||||
} else {
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
remotelogs.Error("ACME", "call auth url failed '"+task.AuthURL+"': "+err.Error())
|
||||
} else {
|
||||
_ = resp.Body.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
certData, keyData, err := acmeRequest.Run()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package acme
|
||||
|
||||
// ACME任务
|
||||
// ACMETask ACME任务
|
||||
type ACMETask struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
AdminId uint32 `field:"adminId"` // 管理员ID
|
||||
@@ -15,6 +15,7 @@ type ACMETask struct {
|
||||
CertId uint64 `field:"certId"` // 生成的证书ID
|
||||
AutoRenew uint8 `field:"autoRenew"` // 是否自动更新
|
||||
AuthType string `field:"authType"` // 认证类型
|
||||
AuthURL string `field:"authURL"` // 认证URL
|
||||
}
|
||||
|
||||
type ACMETaskOperator struct {
|
||||
@@ -31,6 +32,7 @@ type ACMETaskOperator struct {
|
||||
CertId interface{} // 生成的证书ID
|
||||
AutoRenew interface{} // 是否自动更新
|
||||
AuthType interface{} // 认证类型
|
||||
AuthURL interface{} // 认证URL
|
||||
}
|
||||
|
||||
func NewACMETaskOperator() *ACMETaskOperator {
|
||||
|
||||
@@ -39,7 +39,7 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
// 启用条目
|
||||
// EnableACMEUser 启用条目
|
||||
func (this *ACMEUserDAO) EnableACMEUser(tx *dbs.Tx, id int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -48,7 +48,7 @@ func (this *ACMEUserDAO) EnableACMEUser(tx *dbs.Tx, id int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 禁用条目
|
||||
// DisableACMEUser 禁用条目
|
||||
func (this *ACMEUserDAO) DisableACMEUser(tx *dbs.Tx, id int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -69,8 +69,8 @@ func (this *ACMEUserDAO) FindEnabledACMEUser(tx *dbs.Tx, id int64) (*ACMEUser, e
|
||||
return result.(*ACMEUser), err
|
||||
}
|
||||
|
||||
// 创建用户
|
||||
func (this *ACMEUserDAO) CreateACMEUser(tx *dbs.Tx, adminId int64, userId int64, email string, description string) (int64, error) {
|
||||
// CreateACMEUser 创建用户
|
||||
func (this *ACMEUserDAO) CreateACMEUser(tx *dbs.Tx, adminId int64, userId int64, providerCode string, accountId int64, email string, description string) (int64, error) {
|
||||
// 生成私钥
|
||||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
@@ -86,6 +86,8 @@ func (this *ACMEUserDAO) CreateACMEUser(tx *dbs.Tx, adminId int64, userId int64,
|
||||
op := NewACMEUserOperator()
|
||||
op.AdminId = adminId
|
||||
op.UserId = userId
|
||||
op.ProviderCode = providerCode
|
||||
op.AccountId = accountId
|
||||
op.Email = email
|
||||
op.Description = description
|
||||
op.PrivateKey = privateKeyText
|
||||
@@ -97,7 +99,7 @@ func (this *ACMEUserDAO) CreateACMEUser(tx *dbs.Tx, adminId int64, userId int64,
|
||||
return types.Int64(op.Id), nil
|
||||
}
|
||||
|
||||
// 修改用户信息
|
||||
// UpdateACMEUser 修改用户信息
|
||||
func (this *ACMEUserDAO) UpdateACMEUser(tx *dbs.Tx, acmeUserId int64, description string) error {
|
||||
if acmeUserId <= 0 {
|
||||
return errors.New("invalid acmeUserId")
|
||||
@@ -109,7 +111,7 @@ func (this *ACMEUserDAO) UpdateACMEUser(tx *dbs.Tx, acmeUserId int64, descriptio
|
||||
return err
|
||||
}
|
||||
|
||||
// 修改用户ACME注册信息
|
||||
// UpdateACMEUserRegistration 修改用户ACME注册信息
|
||||
func (this *ACMEUserDAO) UpdateACMEUserRegistration(tx *dbs.Tx, acmeUserId int64, registrationJSON []byte) error {
|
||||
if acmeUserId <= 0 {
|
||||
return errors.New("invalid acmeUserId")
|
||||
@@ -121,8 +123,8 @@ func (this *ACMEUserDAO) UpdateACMEUserRegistration(tx *dbs.Tx, acmeUserId int64
|
||||
return err
|
||||
}
|
||||
|
||||
// 计算用户数量
|
||||
func (this *ACMEUserDAO) CountACMEUsersWithAdminId(tx *dbs.Tx, adminId int64, userId int64) (int64, error) {
|
||||
// CountACMEUsersWithAdminId 计算用户数量
|
||||
func (this *ACMEUserDAO) CountACMEUsersWithAdminId(tx *dbs.Tx, adminId int64, userId int64, accountId int64) (int64, error) {
|
||||
query := this.Query(tx)
|
||||
if adminId > 0 {
|
||||
query.Attr("adminId", adminId)
|
||||
@@ -130,13 +132,16 @@ func (this *ACMEUserDAO) CountACMEUsersWithAdminId(tx *dbs.Tx, adminId int64, us
|
||||
if userId > 0 {
|
||||
query.Attr("userId", userId)
|
||||
}
|
||||
if accountId > 0 {
|
||||
query.Attr("accountId", accountId)
|
||||
}
|
||||
|
||||
return query.
|
||||
State(ACMEUserStateEnabled).
|
||||
Count()
|
||||
}
|
||||
|
||||
// 列出当前管理员的用户
|
||||
// ListACMEUsers 列出当前管理员的用户
|
||||
func (this *ACMEUserDAO) ListACMEUsers(tx *dbs.Tx, adminId int64, userId int64, offset int64, size int64) (result []*ACMEUser, err error) {
|
||||
query := this.Query(tx)
|
||||
if adminId > 0 {
|
||||
@@ -156,8 +161,8 @@ func (this *ACMEUserDAO) ListACMEUsers(tx *dbs.Tx, adminId int64, userId int64,
|
||||
return
|
||||
}
|
||||
|
||||
// 查找所有用户
|
||||
func (this *ACMEUserDAO) FindAllACMEUsers(tx *dbs.Tx, adminId int64, userId int64) (result []*ACMEUser, err error) {
|
||||
// FindAllACMEUsers 查找所有用户
|
||||
func (this *ACMEUserDAO) FindAllACMEUsers(tx *dbs.Tx, adminId int64, userId int64, providerCode string) (result []*ACMEUser, err error) {
|
||||
// 防止没有传入条件导致返回的数据过多
|
||||
if adminId <= 0 && userId <= 0 {
|
||||
return nil, errors.New("'adminId' or 'userId' should not be empty")
|
||||
@@ -170,6 +175,9 @@ func (this *ACMEUserDAO) FindAllACMEUsers(tx *dbs.Tx, adminId int64, userId int6
|
||||
if userId > 0 {
|
||||
query.Attr("userId", userId)
|
||||
}
|
||||
if len(providerCode) > 0 {
|
||||
query.Attr("providerCode", providerCode)
|
||||
}
|
||||
_, err = query.
|
||||
State(ACMEUserStateEnabled).
|
||||
Slice(&result).
|
||||
@@ -178,7 +186,7 @@ func (this *ACMEUserDAO) FindAllACMEUsers(tx *dbs.Tx, adminId int64, userId int6
|
||||
return
|
||||
}
|
||||
|
||||
// 检查用户权限
|
||||
// CheckACMEUser 检查用户权限
|
||||
func (this *ACMEUserDAO) CheckACMEUser(tx *dbs.Tx, acmeUserId int64, adminId int64, userId int64) (bool, error) {
|
||||
if acmeUserId <= 0 {
|
||||
return false, nil
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package acme
|
||||
|
||||
//
|
||||
// ACMEUser ACME用户
|
||||
type ACMEUser struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
AdminId uint32 `field:"adminId"` // 管理员ID
|
||||
@@ -11,6 +11,8 @@ type ACMEUser struct {
|
||||
State uint8 `field:"state"` // 状态
|
||||
Description string `field:"description"` // 备注介绍
|
||||
Registration string `field:"registration"` // 注册信息
|
||||
ProviderCode string `field:"providerCode"` // 服务商代号
|
||||
AccountId uint64 `field:"accountId"` // 提供商ID
|
||||
}
|
||||
|
||||
type ACMEUserOperator struct {
|
||||
@@ -23,6 +25,8 @@ type ACMEUserOperator struct {
|
||||
State interface{} // 状态
|
||||
Description interface{} // 备注介绍
|
||||
Registration interface{} // 注册信息
|
||||
ProviderCode interface{} // 服务商代号
|
||||
AccountId interface{} // 提供商ID
|
||||
}
|
||||
|
||||
func NewACMEUserOperator() *ACMEUserOperator {
|
||||
|
||||
@@ -35,7 +35,7 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
// 启用条目
|
||||
// EnableAdmin 启用条目
|
||||
func (this *AdminDAO) EnableAdmin(tx *dbs.Tx, id int64) (rowsAffected int64, err error) {
|
||||
return this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -43,7 +43,7 @@ func (this *AdminDAO) EnableAdmin(tx *dbs.Tx, id int64) (rowsAffected int64, err
|
||||
Update()
|
||||
}
|
||||
|
||||
// 禁用条目
|
||||
// DisableAdmin 禁用条目
|
||||
func (this *AdminDAO) DisableAdmin(tx *dbs.Tx, id int64) (rowsAffected int64, err error) {
|
||||
return this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -51,7 +51,7 @@ func (this *AdminDAO) DisableAdmin(tx *dbs.Tx, id int64) (rowsAffected int64, er
|
||||
Update()
|
||||
}
|
||||
|
||||
// 查找启用中的条目
|
||||
// FindEnabledAdmin 查找启用中的条目
|
||||
func (this *AdminDAO) FindEnabledAdmin(tx *dbs.Tx, id int64) (*Admin, error) {
|
||||
result, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -63,7 +63,7 @@ func (this *AdminDAO) FindEnabledAdmin(tx *dbs.Tx, id int64) (*Admin, error) {
|
||||
return result.(*Admin), err
|
||||
}
|
||||
|
||||
// 检查管理员是否存在
|
||||
// ExistEnabledAdmin 检查管理员是否存在
|
||||
func (this *AdminDAO) ExistEnabledAdmin(tx *dbs.Tx, adminId int64) (bool, error) {
|
||||
return this.Query(tx).
|
||||
Pk(adminId).
|
||||
@@ -71,7 +71,7 @@ func (this *AdminDAO) ExistEnabledAdmin(tx *dbs.Tx, adminId int64) (bool, error)
|
||||
Exist()
|
||||
}
|
||||
|
||||
// 获取管理员名称
|
||||
// FindAdminFullname 获取管理员名称
|
||||
func (this *AdminDAO) FindAdminFullname(tx *dbs.Tx, adminId int64) (string, error) {
|
||||
return this.Query(tx).
|
||||
Pk(adminId).
|
||||
@@ -79,7 +79,7 @@ func (this *AdminDAO) FindAdminFullname(tx *dbs.Tx, adminId int64) (string, erro
|
||||
FindStringCol("")
|
||||
}
|
||||
|
||||
// 检查用户名、密码
|
||||
// CheckAdminPassword 检查用户名、密码
|
||||
func (this *AdminDAO) CheckAdminPassword(tx *dbs.Tx, username string, encryptedPassword string) (int64, error) {
|
||||
if len(username) == 0 || len(encryptedPassword) == 0 {
|
||||
return 0, nil
|
||||
@@ -94,7 +94,7 @@ func (this *AdminDAO) CheckAdminPassword(tx *dbs.Tx, username string, encryptedP
|
||||
FindInt64Col(0)
|
||||
}
|
||||
|
||||
// 根据用户名查询管理员ID
|
||||
// FindAdminIdWithUsername 根据用户名查询管理员ID
|
||||
func (this *AdminDAO) FindAdminIdWithUsername(tx *dbs.Tx, username string) (int64, error) {
|
||||
one, err := this.Query(tx).
|
||||
Attr("username", username).
|
||||
@@ -110,7 +110,7 @@ func (this *AdminDAO) FindAdminIdWithUsername(tx *dbs.Tx, username string) (int6
|
||||
return int64(one.(*Admin).Id), nil
|
||||
}
|
||||
|
||||
// 更改管理员密码
|
||||
// UpdateAdminPassword 更改管理员密码
|
||||
func (this *AdminDAO) UpdateAdminPassword(tx *dbs.Tx, adminId int64, password string) error {
|
||||
if adminId <= 0 {
|
||||
return errors.New("invalid adminId")
|
||||
@@ -122,7 +122,7 @@ func (this *AdminDAO) UpdateAdminPassword(tx *dbs.Tx, adminId int64, password st
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建管理员
|
||||
// CreateAdmin 创建管理员
|
||||
func (this *AdminDAO) CreateAdmin(tx *dbs.Tx, username string, canLogin bool, password string, fullname string, isSuper bool, modulesJSON []byte) (int64, error) {
|
||||
op := NewAdminOperator()
|
||||
op.IsOn = true
|
||||
@@ -144,7 +144,7 @@ func (this *AdminDAO) CreateAdmin(tx *dbs.Tx, username string, canLogin bool, pa
|
||||
return types.Int64(op.Id), nil
|
||||
}
|
||||
|
||||
// 修改管理员个人资料
|
||||
// UpdateAdminInfo 修改管理员个人资料
|
||||
func (this *AdminDAO) UpdateAdminInfo(tx *dbs.Tx, adminId int64, fullname string) error {
|
||||
if adminId <= 0 {
|
||||
return errors.New("invalid adminId")
|
||||
@@ -156,7 +156,7 @@ func (this *AdminDAO) UpdateAdminInfo(tx *dbs.Tx, adminId int64, fullname string
|
||||
return err
|
||||
}
|
||||
|
||||
// 修改管理员详细信息
|
||||
// UpdateAdmin 修改管理员详细信息
|
||||
func (this *AdminDAO) UpdateAdmin(tx *dbs.Tx, adminId int64, username string, canLogin bool, password string, fullname string, isSuper bool, modulesJSON []byte, isOn bool) error {
|
||||
if adminId <= 0 {
|
||||
return errors.New("invalid adminId")
|
||||
@@ -180,7 +180,7 @@ func (this *AdminDAO) UpdateAdmin(tx *dbs.Tx, adminId int64, username string, ca
|
||||
return err
|
||||
}
|
||||
|
||||
// 检查用户名是否存在
|
||||
// CheckAdminUsername 检查用户名是否存在
|
||||
func (this *AdminDAO) CheckAdminUsername(tx *dbs.Tx, adminId int64, username string) (bool, error) {
|
||||
query := this.Query(tx).
|
||||
State(AdminStateEnabled).
|
||||
@@ -193,7 +193,7 @@ func (this *AdminDAO) CheckAdminUsername(tx *dbs.Tx, adminId int64, username str
|
||||
return query.Exist()
|
||||
}
|
||||
|
||||
// 修改管理员登录信息
|
||||
// UpdateAdminLogin 修改管理员登录信息
|
||||
func (this *AdminDAO) UpdateAdminLogin(tx *dbs.Tx, adminId int64, username string, password string) error {
|
||||
if adminId <= 0 {
|
||||
return errors.New("invalid adminId")
|
||||
@@ -208,7 +208,7 @@ func (this *AdminDAO) UpdateAdminLogin(tx *dbs.Tx, adminId int64, username strin
|
||||
return err
|
||||
}
|
||||
|
||||
// 修改管理员可以管理的模块
|
||||
// UpdateAdminModules 修改管理员可以管理的模块
|
||||
func (this *AdminDAO) UpdateAdminModules(tx *dbs.Tx, adminId int64, allowModulesJSON []byte) error {
|
||||
if adminId <= 0 {
|
||||
return errors.New("invalid adminId")
|
||||
@@ -223,25 +223,25 @@ func (this *AdminDAO) UpdateAdminModules(tx *dbs.Tx, adminId int64, allowModules
|
||||
return nil
|
||||
}
|
||||
|
||||
// 查询所有管理的权限
|
||||
// FindAllAdminModules 查询所有管理的权限
|
||||
func (this *AdminDAO) FindAllAdminModules(tx *dbs.Tx) (result []*Admin, err error) {
|
||||
_, err = this.Query(tx).
|
||||
State(AdminStateEnabled).
|
||||
Attr("isOn", true).
|
||||
Result("id", "modules", "isSuper", "fullname").
|
||||
Result("id", "modules", "isSuper", "fullname", "theme").
|
||||
Slice(&result).
|
||||
FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// 计算所有管理员数量
|
||||
// CountAllEnabledAdmins 计算所有管理员数量
|
||||
func (this *AdminDAO) CountAllEnabledAdmins(tx *dbs.Tx) (int64, error) {
|
||||
return this.Query(tx).
|
||||
State(AdminStateEnabled).
|
||||
Count()
|
||||
}
|
||||
|
||||
// 列出单页的管理员
|
||||
// ListEnabledAdmins 列出单页的管理员
|
||||
func (this *AdminDAO) ListEnabledAdmins(tx *dbs.Tx, offset int64, size int64) (result []*Admin, err error) {
|
||||
_, err = this.Query(tx).
|
||||
State(AdminStateEnabled).
|
||||
@@ -253,3 +253,11 @@ func (this *AdminDAO) ListEnabledAdmins(tx *dbs.Tx, offset int64, size int64) (r
|
||||
FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateAdminTheme 设置管理员Theme
|
||||
func (this *AdminDAO) UpdateAdminTheme(tx *dbs.Tx, adminId int64, theme string) error {
|
||||
return this.Query(tx).
|
||||
Pk(adminId).
|
||||
Set("theme", theme).
|
||||
UpdateQuickly()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package models
|
||||
|
||||
// 管理员
|
||||
// Admin 管理员
|
||||
type Admin struct {
|
||||
Id uint32 `field:"id"` // ID
|
||||
IsOn uint8 `field:"isOn"` // 是否启用
|
||||
@@ -13,6 +13,7 @@ type Admin struct {
|
||||
State uint8 `field:"state"` // 状态
|
||||
Modules string `field:"modules"` // 允许的模块
|
||||
CanLogin uint8 `field:"canLogin"` // 是否可以登录
|
||||
Theme string `field:"theme"` // 模板设置
|
||||
}
|
||||
|
||||
type AdminOperator struct {
|
||||
@@ -27,6 +28,7 @@ type AdminOperator struct {
|
||||
State interface{} // 状态
|
||||
Modules interface{} // 允许的模块
|
||||
CanLogin interface{} // 是否可以登录
|
||||
Theme interface{} // 模板设置
|
||||
}
|
||||
|
||||
func NewAdminOperator() *AdminOperator {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
@@ -29,10 +30,23 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
// 生成AccessToken
|
||||
func (this *APIAccessTokenDAO) GenerateAccessToken(tx *dbs.Tx, userId int64) (token string, expiresAt int64, err error) {
|
||||
// GenerateAccessToken 生成AccessToken
|
||||
func (this *APIAccessTokenDAO) GenerateAccessToken(tx *dbs.Tx, adminId int64, userId int64) (token string, expiresAt int64, err error) {
|
||||
if adminId <= 0 && userId <= 0 {
|
||||
err = errors.New("either 'adminId' or 'userId' should not be zero")
|
||||
return
|
||||
}
|
||||
|
||||
if adminId > 0 {
|
||||
userId = 0
|
||||
}
|
||||
if userId > 0 {
|
||||
adminId = 0
|
||||
}
|
||||
|
||||
// 查询以前的
|
||||
accessToken, err := this.Query(tx).
|
||||
Attr("adminId", adminId).
|
||||
Attr("userId", userId).
|
||||
Find()
|
||||
if err != nil {
|
||||
@@ -48,6 +62,7 @@ func (this *APIAccessTokenDAO) GenerateAccessToken(tx *dbs.Tx, userId int64) (to
|
||||
op.Id = accessToken.(*APIAccessToken).Id
|
||||
}
|
||||
|
||||
op.AdminId = adminId
|
||||
op.UserId = userId
|
||||
op.Token = token
|
||||
op.CreatedAt = time.Now().Unix()
|
||||
@@ -56,7 +71,7 @@ func (this *APIAccessTokenDAO) GenerateAccessToken(tx *dbs.Tx, userId int64) (to
|
||||
return
|
||||
}
|
||||
|
||||
// 查找AccessToken
|
||||
// FindAccessToken 查找AccessToken
|
||||
func (this *APIAccessTokenDAO) FindAccessToken(tx *dbs.Tx, token string) (*APIAccessToken, error) {
|
||||
one, err := this.Query(tx).
|
||||
Attr("token", token).
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package models
|
||||
|
||||
// API访问令牌
|
||||
// APIAccessToken API访问令牌
|
||||
type APIAccessToken struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
UserId uint32 `field:"userId"` // 用户ID
|
||||
AdminId uint32 `field:"adminId"` // 管理员ID
|
||||
Token string `field:"token"` // 令牌
|
||||
CreatedAt uint64 `field:"createdAt"` // 创建时间
|
||||
ExpiredAt uint64 `field:"expiredAt"` // 过期时间
|
||||
@@ -12,6 +13,7 @@ type APIAccessToken struct {
|
||||
type APIAccessTokenOperator struct {
|
||||
Id interface{} // ID
|
||||
UserId interface{} // 用户ID
|
||||
AdminId interface{} // 管理员ID
|
||||
Token interface{} // 令牌
|
||||
CreatedAt interface{} // 创建时间
|
||||
ExpiredAt interface{} // 过期时间
|
||||
|
||||
76
internal/db/models/api_method_stat_dao.go
Normal file
76
internal/db/models/api_method_stat_dao.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
)
|
||||
|
||||
type APIMethodStatDAO dbs.DAO
|
||||
|
||||
func NewAPIMethodStatDAO() *APIMethodStatDAO {
|
||||
return dbs.NewDAO(&APIMethodStatDAO{
|
||||
DAOObject: dbs.DAOObject{
|
||||
DB: Tea.Env,
|
||||
Table: "edgeAPIMethodStats",
|
||||
Model: new(APIMethodStat),
|
||||
PkName: "id",
|
||||
},
|
||||
}).(*APIMethodStatDAO)
|
||||
}
|
||||
|
||||
var SharedAPIMethodStatDAO *APIMethodStatDAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedAPIMethodStatDAO = NewAPIMethodStatDAO()
|
||||
})
|
||||
}
|
||||
|
||||
// CreateStat 记录统计数据
|
||||
func (this *APIMethodStatDAO) CreateStat(tx *dbs.Tx, method string, tag string, costMs float64) error {
|
||||
var day = timeutil.Format("Ymd")
|
||||
return this.Query(tx).
|
||||
Param("costMs", costMs).
|
||||
InsertOrUpdateQuickly(map[string]interface{}{
|
||||
"apiNodeId": teaconst.NodeId,
|
||||
"method": method,
|
||||
"tag": tag,
|
||||
"costMs": costMs,
|
||||
"peekMs": costMs,
|
||||
"countCalls": 1,
|
||||
"day": day,
|
||||
}, map[string]interface{}{
|
||||
"costMs": dbs.SQL("(costMs*countCalls+:costMs)/(countCalls+1)"),
|
||||
"peekMs": dbs.SQL("IF(peekMs>:costMs, peekMs, :costMs)"),
|
||||
"countCalls": dbs.SQL("countCalls+1"),
|
||||
})
|
||||
}
|
||||
|
||||
// FindAllStatsWithDay 查询当前统计
|
||||
func (this *APIMethodStatDAO) FindAllStatsWithDay(tx *dbs.Tx, day string) (result []*APIMethodStat, err error) {
|
||||
_, err = this.Query(tx).
|
||||
Attr("day", day).
|
||||
Slice(&result).
|
||||
FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// CountAllStatsWithDay 统计当天数量
|
||||
func (this *APIMethodStatDAO) CountAllStatsWithDay(tx *dbs.Tx, day string) (int64, error) {
|
||||
return this.Query(tx).
|
||||
Attr("day", day).
|
||||
Count()
|
||||
}
|
||||
|
||||
// Clean 清理数据
|
||||
func (this *APIMethodStatDAO) Clean(tx *dbs.Tx) error {
|
||||
var day = timeutil.Format("Ymd")
|
||||
_, err := this.Query(tx).
|
||||
Param("day", day).
|
||||
Where("day<:day").
|
||||
Delete()
|
||||
return err
|
||||
}
|
||||
19
internal/db/models/api_method_stat_dao_test.go
Normal file
19
internal/db/models/api_method_stat_dao_test.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAPIMethodStatDAO_CreateStat(t *testing.T) {
|
||||
var dao = NewAPIMethodStatDAO()
|
||||
var tx *dbs.Tx
|
||||
|
||||
err := dao.CreateStat(tx, "/pb.Hello/World", "tag", 1.123)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("ok")
|
||||
}
|
||||
28
internal/db/models/api_method_stat_model.go
Normal file
28
internal/db/models/api_method_stat_model.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package models
|
||||
|
||||
// APIMethodStat API方法统计
|
||||
type APIMethodStat struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
ApiNodeId uint32 `field:"apiNodeId"` // API节点ID
|
||||
Method string `field:"method"` // 方法
|
||||
Tag string `field:"tag"` // 标签方法
|
||||
CostMs float64 `field:"costMs"` // 耗时Ms
|
||||
PeekMs float64 `field:"peekMs"` // 峰值耗时
|
||||
CountCalls uint64 `field:"countCalls"` // 调用次数
|
||||
Day string `field:"day"` // 日期
|
||||
}
|
||||
|
||||
type APIMethodStatOperator struct {
|
||||
Id interface{} // ID
|
||||
ApiNodeId interface{} // API节点ID
|
||||
Method interface{} // 方法
|
||||
Tag interface{} // 标签方法
|
||||
CostMs interface{} // 耗时Ms
|
||||
PeekMs interface{} // 峰值耗时
|
||||
CountCalls interface{} // 调用次数
|
||||
Day interface{} // 日期
|
||||
}
|
||||
|
||||
func NewAPIMethodStatOperator() *APIMethodStatOperator {
|
||||
return &APIMethodStatOperator{}
|
||||
}
|
||||
1
internal/db/models/api_method_stat_model_ext.go
Normal file
1
internal/db/models/api_method_stat_model_ext.go
Normal file
@@ -0,0 +1 @@
|
||||
package models
|
||||
@@ -4,13 +4,17 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -58,7 +62,15 @@ func (this *APINodeDAO) DisableAPINode(tx *dbs.Tx, id int64) error {
|
||||
}
|
||||
|
||||
// FindEnabledAPINode 查找启用中的条目
|
||||
func (this *APINodeDAO) FindEnabledAPINode(tx *dbs.Tx, id int64) (*APINode, error) {
|
||||
func (this *APINodeDAO) FindEnabledAPINode(tx *dbs.Tx, id int64, cacheMap *utils.CacheMap) (*APINode, error) {
|
||||
var cacheKey = this.Table + ":FindEnabledAPINode:" + types.String(id)
|
||||
if cacheMap != nil {
|
||||
cache, ok := cacheMap.Get(cacheKey)
|
||||
if ok {
|
||||
return cache.(*APINode), nil
|
||||
}
|
||||
}
|
||||
|
||||
result, err := this.Query(tx).
|
||||
Pk(id).
|
||||
Attr("state", APINodeStateEnabled).
|
||||
@@ -66,6 +78,11 @@ func (this *APINodeDAO) FindEnabledAPINode(tx *dbs.Tx, id int64) (*APINode, erro
|
||||
if result == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cacheMap != nil {
|
||||
cacheMap.Put(cacheKey, result)
|
||||
}
|
||||
|
||||
return result.(*APINode), err
|
||||
}
|
||||
|
||||
@@ -97,7 +114,7 @@ func (this *APINodeDAO) CreateAPINode(tx *dbs.Tx, name string, description strin
|
||||
return 0, err
|
||||
}
|
||||
secret := rands.String(32)
|
||||
err = NewApiTokenDAO().CreateAPIToken(tx, uniqueId, secret, NodeRoleAPI)
|
||||
err = NewApiTokenDAO().CreateAPIToken(tx, uniqueId, secret, nodeconfigs.NodeRoleAPI)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -210,6 +227,23 @@ func (this *APINodeDAO) CountAllEnabledAPINodes(tx *dbs.Tx) (int64, error) {
|
||||
Count()
|
||||
}
|
||||
|
||||
// CountAllEnabledAndOnAPINodes 计算启用中的API节点数量
|
||||
func (this *APINodeDAO) CountAllEnabledAndOnAPINodes(tx *dbs.Tx) (int64, error) {
|
||||
return this.Query(tx).
|
||||
State(APINodeStateEnabled).
|
||||
Attr("isOn", true).
|
||||
Count()
|
||||
}
|
||||
|
||||
// CountAllEnabledAndOnOfflineAPINodes 计算API节点数量
|
||||
func (this *APINodeDAO) CountAllEnabledAndOnOfflineAPINodes(tx *dbs.Tx) (int64, error) {
|
||||
return this.Query(tx).
|
||||
State(APINodeStateEnabled).
|
||||
Attr("isOn", true).
|
||||
Where("(status IS NULL OR NOT JSON_EXTRACT(status, '$.isActive') OR UNIX_TIMESTAMP()-JSON_EXTRACT(status, '$.updatedAt')>60)").
|
||||
Count()
|
||||
}
|
||||
|
||||
// ListEnabledAPINodes 列出单页的API节点
|
||||
func (this *APINodeDAO) ListEnabledAPINodes(tx *dbs.Tx, offset int64, size int64) (result []*APINode, err error) {
|
||||
_, err = this.Query(tx).
|
||||
@@ -286,3 +320,67 @@ func (this *APINodeDAO) CountAllLowerVersionNodes(tx *dbs.Tx, version string) (i
|
||||
Param("version", utils.VersionToLong(version)).
|
||||
Count()
|
||||
}
|
||||
|
||||
// CountAllEnabledAPINodesWithSSLPolicyIds 计算使用SSL策略的所有API节点数量
|
||||
func (this *APINodeDAO) CountAllEnabledAPINodesWithSSLPolicyIds(tx *dbs.Tx, sslPolicyIds []int64) (count int64, err error) {
|
||||
if len(sslPolicyIds) == 0 {
|
||||
return
|
||||
}
|
||||
policyStringIds := []string{}
|
||||
for _, policyId := range sslPolicyIds {
|
||||
policyStringIds = append(policyStringIds, strconv.FormatInt(policyId, 10))
|
||||
}
|
||||
return this.Query(tx).
|
||||
State(APINodeStateEnabled).
|
||||
Where("(FIND_IN_SET(JSON_EXTRACT(https, '$.sslPolicyRef.sslPolicyId'), :policyIds) OR FIND_IN_SET(JSON_EXTRACT(restHTTPS, '$.sslPolicyRef.sslPolicyId'), :policyIds))").
|
||||
Param("policyIds", strings.Join(policyStringIds, ",")).
|
||||
Count()
|
||||
}
|
||||
|
||||
// FindAllEnabledAPIAccessIPs 获取所有的API可访问IP地址
|
||||
func (this *APINodeDAO) FindAllEnabledAPIAccessIPs(tx *dbs.Tx, cacheMap *utils.CacheMap) ([]string, error) {
|
||||
var cacheKey = this.Table + ":FindAllEnabledAPIAccessIPs"
|
||||
if cacheMap != nil {
|
||||
cache, ok := cacheMap.Get(cacheKey)
|
||||
if ok {
|
||||
return cache.([]string), nil
|
||||
}
|
||||
}
|
||||
|
||||
ones, _, err := this.Query(tx).
|
||||
State(APINodeStateEnabled).
|
||||
Result("JSON_EXTRACT(accessAddrs, '$[*].host') AS host").
|
||||
FindOnes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result = []string{}
|
||||
for _, one := range ones {
|
||||
var host = one.GetString("host")
|
||||
if len(host) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var ips = []string{}
|
||||
err = json.Unmarshal([]byte(host), &ips)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
if !lists.ContainsString(result, ip) {
|
||||
if net.ParseIP(ip) == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
result = append(result, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cacheMap != nil {
|
||||
cacheMap.Put(cacheKey, result)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"runtime"
|
||||
@@ -27,6 +28,12 @@ func TestAPINodeDAO_FindEnabledAPINodeIdWithAddr(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPINodeDAO_FindAllEnabledAPIAccessIPs(t *testing.T) {
|
||||
var cacheMap = utils.NewCacheMap()
|
||||
t.Log(NewAPINodeDAO().FindAllEnabledAPIAccessIPs(nil, cacheMap))
|
||||
t.Log(NewAPINodeDAO().FindAllEnabledAPIAccessIPs(nil, cacheMap))
|
||||
}
|
||||
|
||||
func BenchmarkAPINodeDAO_New(b *testing.B) {
|
||||
runtime.GOMAXPROCS(1)
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
||||
@@ -2,11 +2,12 @@ package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
)
|
||||
|
||||
// 解析HTTP配置
|
||||
// DecodeHTTP 解析HTTP配置
|
||||
func (this *APINode) DecodeHTTP() (*serverconfigs.HTTPProtocolConfig, error) {
|
||||
if !IsNotNull(this.Http) {
|
||||
return nil, nil
|
||||
@@ -25,8 +26,8 @@ func (this *APINode) DecodeHTTP() (*serverconfigs.HTTPProtocolConfig, error) {
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// 解析HTTPS配置
|
||||
func (this *APINode) DecodeHTTPS(tx *dbs.Tx) (*serverconfigs.HTTPSProtocolConfig, error) {
|
||||
// DecodeHTTPS 解析HTTPS配置
|
||||
func (this *APINode) DecodeHTTPS(tx *dbs.Tx, cacheMap *utils.CacheMap) (*serverconfigs.HTTPSProtocolConfig, error) {
|
||||
if !IsNotNull(this.Https) {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -44,7 +45,7 @@ func (this *APINode) DecodeHTTPS(tx *dbs.Tx) (*serverconfigs.HTTPSProtocolConfig
|
||||
if config.SSLPolicyRef != nil {
|
||||
policyId := config.SSLPolicyRef.SSLPolicyId
|
||||
if policyId > 0 {
|
||||
sslPolicy, err := SharedSSLPolicyDAO.ComposePolicyConfig(tx, policyId)
|
||||
sslPolicy, err := SharedSSLPolicyDAO.ComposePolicyConfig(tx, policyId, cacheMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -62,7 +63,7 @@ func (this *APINode) DecodeHTTPS(tx *dbs.Tx) (*serverconfigs.HTTPSProtocolConfig
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// 解析访问地址
|
||||
// DecodeAccessAddrs 解析访问地址
|
||||
func (this *APINode) DecodeAccessAddrs() ([]*serverconfigs.NetworkAddressConfig, error) {
|
||||
if !IsNotNull(this.AccessAddrs) {
|
||||
return nil, nil
|
||||
@@ -82,7 +83,7 @@ func (this *APINode) DecodeAccessAddrs() ([]*serverconfigs.NetworkAddressConfig,
|
||||
return addrConfigs, nil
|
||||
}
|
||||
|
||||
// 解析访问地址,并返回字符串形式
|
||||
// DecodeAccessAddrStrings 解析访问地址,并返回字符串形式
|
||||
func (this *APINode) DecodeAccessAddrStrings() ([]string, error) {
|
||||
addrs, err := this.DecodeAccessAddrs()
|
||||
if err != nil {
|
||||
@@ -95,7 +96,7 @@ func (this *APINode) DecodeAccessAddrStrings() ([]string, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 解析Rest HTTP配置
|
||||
// DecodeRestHTTP 解析Rest HTTP配置
|
||||
func (this *APINode) DecodeRestHTTP() (*serverconfigs.HTTPProtocolConfig, error) {
|
||||
if this.RestIsOn != 1 {
|
||||
return nil, nil
|
||||
@@ -117,8 +118,11 @@ func (this *APINode) DecodeRestHTTP() (*serverconfigs.HTTPProtocolConfig, error)
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// 解析HTTPS配置
|
||||
func (this *APINode) DecodeRestHTTPS(tx *dbs.Tx) (*serverconfigs.HTTPSProtocolConfig, error) {
|
||||
// DecodeRestHTTPS 解析HTTPS配置
|
||||
func (this *APINode) DecodeRestHTTPS(tx *dbs.Tx, cacheMap *utils.CacheMap) (*serverconfigs.HTTPSProtocolConfig, error) {
|
||||
if cacheMap == nil {
|
||||
cacheMap = utils.NewCacheMap()
|
||||
}
|
||||
if this.RestIsOn != 1 {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -139,7 +143,7 @@ func (this *APINode) DecodeRestHTTPS(tx *dbs.Tx) (*serverconfigs.HTTPSProtocolCo
|
||||
if config.SSLPolicyRef != nil {
|
||||
policyId := config.SSLPolicyRef.SSLPolicyId
|
||||
if policyId > 0 {
|
||||
sslPolicy, err := SharedSSLPolicyDAO.ComposePolicyConfig(tx, policyId)
|
||||
sslPolicy, err := SharedSSLPolicyDAO.ComposePolicyConfig(tx, policyId, cacheMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
@@ -34,7 +35,7 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
// 启用条目
|
||||
// EnableApiToken 启用条目
|
||||
func (this *ApiTokenDAO) EnableApiToken(tx *dbs.Tx, id uint32) (rowsAffected int64, err error) {
|
||||
return this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -42,7 +43,7 @@ func (this *ApiTokenDAO) EnableApiToken(tx *dbs.Tx, id uint32) (rowsAffected int
|
||||
Update()
|
||||
}
|
||||
|
||||
// 禁用条目
|
||||
// DisableApiToken 禁用条目
|
||||
func (this *ApiTokenDAO) DisableApiToken(tx *dbs.Tx, id uint32) (rowsAffected int64, err error) {
|
||||
return this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -50,7 +51,7 @@ func (this *ApiTokenDAO) DisableApiToken(tx *dbs.Tx, id uint32) (rowsAffected in
|
||||
Update()
|
||||
}
|
||||
|
||||
// 查找启用中的条目
|
||||
// FindEnabledApiToken 查找启用中的条目
|
||||
func (this *ApiTokenDAO) FindEnabledApiToken(tx *dbs.Tx, id uint32) (*ApiToken, error) {
|
||||
result, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -62,7 +63,7 @@ func (this *ApiTokenDAO) FindEnabledApiToken(tx *dbs.Tx, id uint32) (*ApiToken,
|
||||
return result.(*ApiToken), err
|
||||
}
|
||||
|
||||
// 获取可缓存的节点Token信息
|
||||
// FindEnabledTokenWithNodeCacheable 获取可缓存的节点Token信息
|
||||
func (this *ApiTokenDAO) FindEnabledTokenWithNodeCacheable(tx *dbs.Tx, nodeId string) (*ApiToken, error) {
|
||||
SharedCacheLocker.RLock()
|
||||
token, ok := apiTokenCacheMap[nodeId]
|
||||
@@ -85,7 +86,7 @@ func (this *ApiTokenDAO) FindEnabledTokenWithNodeCacheable(tx *dbs.Tx, nodeId st
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取节点Token信息并可以缓存
|
||||
// FindEnabledTokenWithNode 获取节点Token信息并可以缓存
|
||||
func (this *ApiTokenDAO) FindEnabledTokenWithNode(tx *dbs.Tx, nodeId string) (*ApiToken, error) {
|
||||
one, err := this.Query(tx).
|
||||
Attr("nodeId", nodeId).
|
||||
@@ -97,7 +98,7 @@ func (this *ApiTokenDAO) FindEnabledTokenWithNode(tx *dbs.Tx, nodeId string) (*A
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 根据角色获取节点
|
||||
// FindEnabledTokenWithRole 根据角色获取节点
|
||||
func (this *ApiTokenDAO) FindEnabledTokenWithRole(tx *dbs.Tx, role string) (*ApiToken, error) {
|
||||
one, err := this.Query(tx).
|
||||
Attr("role", role).
|
||||
@@ -109,8 +110,8 @@ func (this *ApiTokenDAO) FindEnabledTokenWithRole(tx *dbs.Tx, role string) (*Api
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 保存API Token
|
||||
func (this *ApiTokenDAO) CreateAPIToken(tx *dbs.Tx, nodeId string, secret string, role NodeRole) error {
|
||||
// CreateAPIToken 保存API Token
|
||||
func (this *ApiTokenDAO) CreateAPIToken(tx *dbs.Tx, nodeId string, secret string, role nodeconfigs.NodeRole) error {
|
||||
op := NewApiTokenOperator()
|
||||
op.NodeId = nodeId
|
||||
op.Secret = secret
|
||||
@@ -119,3 +120,13 @@ func (this *ApiTokenDAO) CreateAPIToken(tx *dbs.Tx, nodeId string, secret string
|
||||
err := this.Save(tx, op)
|
||||
return err
|
||||
}
|
||||
|
||||
// FindAllEnabledAPITokens 读取API令牌
|
||||
func (this *ApiTokenDAO) FindAllEnabledAPITokens(tx *dbs.Tx, role string) (result []*ApiToken, err error) {
|
||||
_, err = this.Query(tx).
|
||||
Attr("role", role).
|
||||
State(ApiTokenStateEnabled).
|
||||
Slice(&result).
|
||||
FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@ package authority
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -26,6 +28,9 @@ var SharedAuthorityKeyDAO *AuthorityKeyDAO
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedAuthorityKeyDAO = NewAuthorityKeyDAO()
|
||||
|
||||
// 初始化IsPlus值
|
||||
_, _ = SharedAuthorityKeyDAO.IsPlus(nil)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -72,7 +77,14 @@ func (this *AuthorityKeyDAO) ReadKey(tx *dbs.Tx) (key *AuthorityKey, err error)
|
||||
if one == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return one.(*AuthorityKey), nil
|
||||
key = one.(*AuthorityKey)
|
||||
|
||||
// 顺便更新相关变量
|
||||
if key.DayTo >= timeutil.Format("Y-m-d") {
|
||||
teaconst.IsPlus = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ResetKey 重置Key
|
||||
|
||||
14
internal/db/models/authority/authority_key_dao_community.go
Normal file
14
internal/db/models/authority/authority_key_dao_community.go
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
//go:build !plus
|
||||
// +build !plus
|
||||
|
||||
package authority
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
)
|
||||
|
||||
// IsPlus 判断是否为企业版
|
||||
func (this *AuthorityKeyDAO) IsPlus(tx *dbs.Tx) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
@@ -7,17 +7,17 @@ import (
|
||||
)
|
||||
|
||||
func TestAuthorityKeyDAO_UpdateValue(t *testing.T) {
|
||||
err := NewAuthorityKeyDAO().UpdateValue(nil, "12345678")
|
||||
err := NewAuthorityKeyDAO().UpdateKey(nil, "12345678", "", "", "", []string{}, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("ok")
|
||||
t.Log("ok")
|
||||
}
|
||||
|
||||
func TestAuthorityKeyDAO_ReadValue(t *testing.T) {
|
||||
value, err := NewAuthorityKeyDAO().ReadValue(nil)
|
||||
value, err := NewAuthorityKeyDAO().ReadKey(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf(value)
|
||||
t.Log(value)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
@@ -113,7 +114,7 @@ func (this *AuthorityNodeDAO) CreateAuthorityNode(tx *dbs.Tx, name string, descr
|
||||
return 0, err
|
||||
}
|
||||
secret := rands.String(32)
|
||||
err = models.NewApiTokenDAO().CreateAPIToken(tx, uniqueId, secret, models.NodeRoleAuthority)
|
||||
err = models.NewApiTokenDAO().CreateAPIToken(tx, uniqueId, secret, nodeconfigs.NodeRoleAuthority)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -35,7 +36,7 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
// 启用条目
|
||||
// EnableClientBrowser 启用条目
|
||||
func (this *ClientBrowserDAO) EnableClientBrowser(tx *dbs.Tx, id uint32) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -44,7 +45,7 @@ func (this *ClientBrowserDAO) EnableClientBrowser(tx *dbs.Tx, id uint32) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 禁用条目
|
||||
// DisableClientBrowser 禁用条目
|
||||
func (this *ClientBrowserDAO) DisableClientBrowser(tx *dbs.Tx, id uint32) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -53,7 +54,7 @@ func (this *ClientBrowserDAO) DisableClientBrowser(tx *dbs.Tx, id uint32) error
|
||||
return err
|
||||
}
|
||||
|
||||
// 查找启用中的条目
|
||||
// FindEnabledClientBrowser 查找启用中的条目
|
||||
func (this *ClientBrowserDAO) FindEnabledClientBrowser(tx *dbs.Tx, id int64) (*ClientBrowser, error) {
|
||||
result, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -65,7 +66,7 @@ func (this *ClientBrowserDAO) FindEnabledClientBrowser(tx *dbs.Tx, id int64) (*C
|
||||
return result.(*ClientBrowser), err
|
||||
}
|
||||
|
||||
// 根据主键查找名称
|
||||
// FindClientBrowserName 根据主键查找名称
|
||||
func (this *ClientBrowserDAO) FindClientBrowserName(tx *dbs.Tx, id uint32) (string, error) {
|
||||
return this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -73,7 +74,7 @@ func (this *ClientBrowserDAO) FindClientBrowserName(tx *dbs.Tx, id uint32) (stri
|
||||
FindStringCol("")
|
||||
}
|
||||
|
||||
// 根据浏览器名称查找浏览器ID
|
||||
// FindBrowserIdWithNameCacheable 根据浏览器名称查找浏览器ID
|
||||
func (this *ClientBrowserDAO) FindBrowserIdWithNameCacheable(tx *dbs.Tx, browserName string) (int64, error) {
|
||||
SharedCacheLocker.RLock()
|
||||
browserId, ok := clientBrowserNameAndIdCacheMap[browserName]
|
||||
@@ -85,7 +86,7 @@ func (this *ClientBrowserDAO) FindBrowserIdWithNameCacheable(tx *dbs.Tx, browser
|
||||
|
||||
browserId, err := this.Query(tx).
|
||||
Where("JSON_CONTAINS(codes, :browserName)").
|
||||
Param("browserName", "\""+browserName+"\""). // 查询的需要是个JSON字符串,所以这里加双引号
|
||||
Param("browserName", strconv.Quote(browserName)). // 查询的需要是个JSON字符串,所以这里加双引号
|
||||
ResultPk().
|
||||
FindInt64Col(0)
|
||||
if err != nil {
|
||||
@@ -102,8 +103,28 @@ func (this *ClientBrowserDAO) FindBrowserIdWithNameCacheable(tx *dbs.Tx, browser
|
||||
return browserId, nil
|
||||
}
|
||||
|
||||
// 创建浏览器
|
||||
// CreateBrowser 创建浏览器
|
||||
func (this *ClientBrowserDAO) CreateBrowser(tx *dbs.Tx, browserName string) (int64, error) {
|
||||
var maxlength = 50
|
||||
if len(browserName) > maxlength {
|
||||
browserName = browserName[:50]
|
||||
}
|
||||
|
||||
SharedCacheLocker.Lock()
|
||||
defer SharedCacheLocker.Unlock()
|
||||
|
||||
// 检查是否已经创建
|
||||
browserId, err := this.Query(tx).
|
||||
Attr("name", browserName).
|
||||
ResultPk().
|
||||
FindInt64Col(0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if browserId > 0 {
|
||||
return browserId, nil
|
||||
}
|
||||
|
||||
op := NewClientBrowserOperator()
|
||||
op.Name = browserName
|
||||
codes := []string{browserName}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -35,7 +36,7 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
// 启用条目
|
||||
// EnableClientSystem 启用条目
|
||||
func (this *ClientSystemDAO) EnableClientSystem(tx *dbs.Tx, id uint32) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -44,7 +45,7 @@ func (this *ClientSystemDAO) EnableClientSystem(tx *dbs.Tx, id uint32) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 禁用条目
|
||||
// DisableClientSystem 禁用条目
|
||||
func (this *ClientSystemDAO) DisableClientSystem(tx *dbs.Tx, id uint32) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -53,7 +54,7 @@ func (this *ClientSystemDAO) DisableClientSystem(tx *dbs.Tx, id uint32) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 查找启用中的条目
|
||||
// FindEnabledClientSystem 查找启用中的条目
|
||||
func (this *ClientSystemDAO) FindEnabledClientSystem(tx *dbs.Tx, id int64) (*ClientSystem, error) {
|
||||
result, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -65,7 +66,7 @@ func (this *ClientSystemDAO) FindEnabledClientSystem(tx *dbs.Tx, id int64) (*Cli
|
||||
return result.(*ClientSystem), err
|
||||
}
|
||||
|
||||
// 根据主键查找名称
|
||||
// FindClientSystemName 根据主键查找名称
|
||||
func (this *ClientSystemDAO) FindClientSystemName(tx *dbs.Tx, id uint32) (string, error) {
|
||||
return this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -73,7 +74,7 @@ func (this *ClientSystemDAO) FindClientSystemName(tx *dbs.Tx, id uint32) (string
|
||||
FindStringCol("")
|
||||
}
|
||||
|
||||
// 根据操作系统名称查找系统ID
|
||||
// FindSystemIdWithNameCacheable 根据操作系统名称查找系统ID
|
||||
func (this *ClientSystemDAO) FindSystemIdWithNameCacheable(tx *dbs.Tx, systemName string) (int64, error) {
|
||||
SharedCacheLocker.RLock()
|
||||
systemId, ok := clientSystemNameAndIdCacheMap[systemName]
|
||||
@@ -85,7 +86,7 @@ func (this *ClientSystemDAO) FindSystemIdWithNameCacheable(tx *dbs.Tx, systemNam
|
||||
|
||||
systemId, err := this.Query(tx).
|
||||
Where("JSON_CONTAINS(codes, :systemName)").
|
||||
Param("systemName", "\""+systemName+"\""). // 查询的需要是个JSON字符串,所以这里加双引号
|
||||
Param("systemName", strconv.Quote(systemName)). // 查询的需要是个JSON字符串,所以这里加双引号
|
||||
ResultPk().
|
||||
FindInt64Col(0)
|
||||
if err != nil {
|
||||
@@ -102,8 +103,28 @@ func (this *ClientSystemDAO) FindSystemIdWithNameCacheable(tx *dbs.Tx, systemNam
|
||||
return systemId, nil
|
||||
}
|
||||
|
||||
// 创建浏览器
|
||||
// CreateSystem 创建浏览器
|
||||
func (this *ClientSystemDAO) CreateSystem(tx *dbs.Tx, systemName string) (int64, error) {
|
||||
var maxlength = 50
|
||||
if len(systemName) > maxlength {
|
||||
systemName = systemName[:50]
|
||||
}
|
||||
|
||||
SharedCacheLocker.Lock()
|
||||
defer SharedCacheLocker.Unlock()
|
||||
|
||||
// 检查是否已经创建
|
||||
systemId, err := this.Query(tx).
|
||||
Attr("name", systemName).
|
||||
ResultPk().
|
||||
FindInt64Col(0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if systemId > 0 {
|
||||
return systemId, nil
|
||||
}
|
||||
|
||||
op := NewClientSystemOperator()
|
||||
op.Name = systemName
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
// 启用条目
|
||||
// EnableDBNode 启用条目
|
||||
func (this *DBNodeDAO) EnableDBNode(tx *dbs.Tx, id int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -48,7 +48,7 @@ func (this *DBNodeDAO) EnableDBNode(tx *dbs.Tx, id int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 禁用条目
|
||||
// DisableDBNode 禁用条目
|
||||
func (this *DBNodeDAO) DisableDBNode(tx *dbs.Tx, id int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -57,7 +57,7 @@ func (this *DBNodeDAO) DisableDBNode(tx *dbs.Tx, id int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 查找启用中的条目
|
||||
// FindEnabledDBNode 查找启用中的条目
|
||||
func (this *DBNodeDAO) FindEnabledDBNode(tx *dbs.Tx, id int64) (*DBNode, error) {
|
||||
result, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -71,7 +71,7 @@ func (this *DBNodeDAO) FindEnabledDBNode(tx *dbs.Tx, id int64) (*DBNode, error)
|
||||
return node, nil
|
||||
}
|
||||
|
||||
// 根据主键查找名称
|
||||
// FindDBNodeName 根据主键查找名称
|
||||
func (this *DBNodeDAO) FindDBNodeName(tx *dbs.Tx, id int64) (string, error) {
|
||||
return this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -79,14 +79,14 @@ func (this *DBNodeDAO) FindDBNodeName(tx *dbs.Tx, id int64) (string, error) {
|
||||
FindStringCol("")
|
||||
}
|
||||
|
||||
// 计算可用的节点数量
|
||||
// CountAllEnabledNodes 计算可用的节点数量
|
||||
func (this *DBNodeDAO) CountAllEnabledNodes(tx *dbs.Tx) (int64, error) {
|
||||
return this.Query(tx).
|
||||
State(DBNodeStateEnabled).
|
||||
Count()
|
||||
}
|
||||
|
||||
// 获取单页的节点
|
||||
// ListEnabledNodes 获取单页的节点
|
||||
func (this *DBNodeDAO) ListEnabledNodes(tx *dbs.Tx, offset int64, size int64) (result []*DBNode, err error) {
|
||||
_, err = this.Query(tx).
|
||||
State(DBNodeStateEnabled).
|
||||
@@ -101,7 +101,7 @@ func (this *DBNodeDAO) ListEnabledNodes(tx *dbs.Tx, offset int64, size int64) (r
|
||||
return
|
||||
}
|
||||
|
||||
// 创建节点
|
||||
// CreateDBNode 创建节点
|
||||
func (this *DBNodeDAO) CreateDBNode(tx *dbs.Tx, isOn bool, name string, description string, host string, port int32, database string, username string, password string, charset string) (int64, error) {
|
||||
op := NewDBNodeOperator()
|
||||
op.State = NodeStateEnabled
|
||||
@@ -121,7 +121,7 @@ func (this *DBNodeDAO) CreateDBNode(tx *dbs.Tx, isOn bool, name string, descript
|
||||
return types.Int64(op.Id), nil
|
||||
}
|
||||
|
||||
// 修改节点
|
||||
// UpdateNode 修改节点
|
||||
func (this *DBNodeDAO) UpdateNode(tx *dbs.Tx, nodeId int64, isOn bool, name string, description string, host string, port int32, database string, username string, password string, charset string) error {
|
||||
if nodeId <= 0 {
|
||||
return errors.New("invalid nodeId")
|
||||
@@ -141,7 +141,7 @@ func (this *DBNodeDAO) UpdateNode(tx *dbs.Tx, nodeId int64, isOn bool, name stri
|
||||
return err
|
||||
}
|
||||
|
||||
// 查找所有可用的数据库节点
|
||||
// FindAllEnabledAndOnDBNodes 查找所有可用的数据库节点
|
||||
func (this *DBNodeDAO) FindAllEnabledAndOnDBNodes(tx *dbs.Tx) (result []*DBNode, err error) {
|
||||
_, err = this.Query(tx).
|
||||
State(DBNodeStateEnabled).
|
||||
@@ -155,7 +155,7 @@ func (this *DBNodeDAO) FindAllEnabledAndOnDBNodes(tx *dbs.Tx) (result []*DBNode,
|
||||
return
|
||||
}
|
||||
|
||||
// 加密密码
|
||||
// EncodePassword 加密密码
|
||||
func (this *DBNodeDAO) EncodePassword(password string) string {
|
||||
if strings.HasPrefix(password, DBNodePasswordEncodedPrefix) {
|
||||
return password
|
||||
@@ -164,7 +164,7 @@ func (this *DBNodeDAO) EncodePassword(password string) string {
|
||||
return DBNodePasswordEncodedPrefix + encodedString
|
||||
}
|
||||
|
||||
// 解密密码
|
||||
// DecodePassword 解密密码
|
||||
func (this *DBNodeDAO) DecodePassword(password string) string {
|
||||
if !strings.HasPrefix(password, DBNodePasswordEncodedPrefix) {
|
||||
return password
|
||||
@@ -176,3 +176,15 @@ func (this *DBNodeDAO) DecodePassword(password string) string {
|
||||
}
|
||||
return string(encrypt.MagicKeyDecode(data))
|
||||
}
|
||||
|
||||
// CheckNodeIsOn 检查节点是否已经启用
|
||||
func (this *DBNodeDAO) CheckNodeIsOn(tx *dbs.Tx, nodeId int64) (bool, error) {
|
||||
isOn, err := this.Query(tx).
|
||||
Pk(nodeId).
|
||||
Result("isOn").
|
||||
FindIntCol(0)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return isOn == 1, nil
|
||||
}
|
||||
|
||||
@@ -3,9 +3,12 @@ package models
|
||||
import (
|
||||
"fmt"
|
||||
"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/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"hash/crc32"
|
||||
"regexp"
|
||||
@@ -15,30 +18,66 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var accessLogDBMapping = map[int64]*dbs.DB{} // dbNodeId => DB
|
||||
var accessLogDAOMapping = map[int64]*HTTPAccessLogDAOWrapper{} // dbNodeId => DAO
|
||||
var accessLogDBMapping = map[int64]*dbs.DB{} // dbNodeId => DB
|
||||
var accessLogLocker = &sync.RWMutex{}
|
||||
var accessLogTableMapping = map[string]bool{} // tableName_crc(dsn) => true
|
||||
|
||||
type httpAccessLogDefinition struct {
|
||||
Name string
|
||||
HasRemoteAddr bool
|
||||
HasDomain bool
|
||||
Exists bool
|
||||
}
|
||||
|
||||
// HTTP服务访问
|
||||
var httpAccessLogDAOMapping = map[int64]*HTTPAccessLogDAOWrapper{} // dbNodeId => DAO
|
||||
var httpAccessLogTableMapping = map[string]*httpAccessLogDefinition{} // tableName_crc(dsn) => true
|
||||
|
||||
// DNS服务访问
|
||||
var nsAccessLogDAOMapping = map[int64]*NSAccessLogDAOWrapper{} // dbNodeId => DAO
|
||||
var nsAccessLogTableMapping = map[string]bool{} // tableName_crc(dsn) => true
|
||||
|
||||
// HTTPAccessLogDAOWrapper HTTP访问日志DAO
|
||||
type HTTPAccessLogDAOWrapper struct {
|
||||
DAO *HTTPAccessLogDAO
|
||||
NodeId int64
|
||||
}
|
||||
|
||||
// NSAccessLogDAOWrapper NS访问日志DAO
|
||||
type NSAccessLogDAOWrapper struct {
|
||||
DAO *NSAccessLogDAO
|
||||
NodeId int64
|
||||
}
|
||||
|
||||
func init() {
|
||||
initializer := NewDBNodeInitializer()
|
||||
dbs.OnReadyDone(func() {
|
||||
go initializer.Start()
|
||||
goman.New(func() {
|
||||
initializer.Start()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 获取获取DAO
|
||||
func randomAccessLogDAO() (dao *HTTPAccessLogDAOWrapper) {
|
||||
func randomHTTPAccessLogDAO() (dao *HTTPAccessLogDAOWrapper) {
|
||||
accessLogLocker.RLock()
|
||||
if len(accessLogDAOMapping) == 0 {
|
||||
if len(httpAccessLogDAOMapping) == 0 {
|
||||
dao = nil
|
||||
} else {
|
||||
for _, d := range accessLogDAOMapping {
|
||||
for _, d := range httpAccessLogDAOMapping {
|
||||
dao = d
|
||||
break
|
||||
}
|
||||
}
|
||||
accessLogLocker.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
func randomNSAccessLogDAO() (dao *NSAccessLogDAOWrapper) {
|
||||
accessLogLocker.RLock()
|
||||
if len(nsAccessLogDAOMapping) == 0 {
|
||||
dao = nil
|
||||
} else {
|
||||
for _, d := range nsAccessLogDAOMapping {
|
||||
dao = d
|
||||
break
|
||||
}
|
||||
@@ -48,7 +87,36 @@ func randomAccessLogDAO() (dao *HTTPAccessLogDAOWrapper) {
|
||||
}
|
||||
|
||||
// 检查表格是否存在
|
||||
func findAccessLogTableName(db *dbs.DB, day string) (tableName string, ok bool, err error) {
|
||||
func findHTTPAccessLogTableName(db *dbs.DB, day string) (tableName string, hasRemoteAddr bool, hasDomain bool, ok bool, err error) {
|
||||
if !regexp.MustCompile(`^\d{8}$`).MatchString(day) {
|
||||
err = errors.New("invalid day '" + day + "', should be YYYYMMDD")
|
||||
return
|
||||
}
|
||||
|
||||
config, err := db.Config()
|
||||
if err != nil {
|
||||
return "", false, false, false, err
|
||||
}
|
||||
|
||||
tableName = "edgeHTTPAccessLogs_" + day
|
||||
cacheKey := tableName + "_" + fmt.Sprintf("%d", crc32.ChecksumIEEE([]byte(config.Dsn)))
|
||||
|
||||
accessLogLocker.RLock()
|
||||
def, ok := httpAccessLogTableMapping[cacheKey]
|
||||
accessLogLocker.RUnlock()
|
||||
if ok {
|
||||
return tableName, def.HasRemoteAddr, def.HasDomain, true, nil
|
||||
}
|
||||
|
||||
def, err = findHTTPAccessLogTable(db, day, false)
|
||||
if err != nil {
|
||||
return tableName, false, false, false, err
|
||||
}
|
||||
|
||||
return tableName, def.HasRemoteAddr, def.HasDomain, def.Exists, nil
|
||||
}
|
||||
|
||||
func findNSAccessLogTableName(db *dbs.DB, day string) (tableName string, ok bool, err error) {
|
||||
if !regexp.MustCompile(`^\d{8}$`).MatchString(day) {
|
||||
err = errors.New("invalid day '" + day + "', should be YYYYMMDD")
|
||||
return
|
||||
@@ -59,11 +127,11 @@ func findAccessLogTableName(db *dbs.DB, day string) (tableName string, ok bool,
|
||||
return "", false, err
|
||||
}
|
||||
|
||||
tableName = "edgeHTTPAccessLogs_" + day
|
||||
tableName = "edgeNSAccessLogs_" + day
|
||||
cacheKey := tableName + "_" + fmt.Sprintf("%d", crc32.ChecksumIEEE([]byte(config.Dsn)))
|
||||
|
||||
accessLogLocker.RLock()
|
||||
_, ok = accessLogTableMapping[cacheKey]
|
||||
_, ok = nsAccessLogTableMapping[cacheKey]
|
||||
accessLogLocker.RUnlock()
|
||||
if ok {
|
||||
return tableName, true, nil
|
||||
@@ -74,14 +142,14 @@ func findAccessLogTableName(db *dbs.DB, day string) (tableName string, ok bool,
|
||||
return tableName, false, err
|
||||
}
|
||||
|
||||
return tableName, lists.ContainsString(tableNames, tableName), nil
|
||||
return tableName, utils.ContainsStringInsensitive(tableNames, tableName), nil
|
||||
}
|
||||
|
||||
// 根据日期获取表名
|
||||
func findAccessLogTable(db *dbs.DB, day string, force bool) (string, error) {
|
||||
func findHTTPAccessLogTable(db *dbs.DB, day string, force bool) (*httpAccessLogDefinition, error) {
|
||||
config, err := db.Config()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tableName := "edgeHTTPAccessLogs_" + day
|
||||
@@ -89,7 +157,75 @@ func findAccessLogTable(db *dbs.DB, day string, force bool) (string, error) {
|
||||
|
||||
if !force {
|
||||
accessLogLocker.RLock()
|
||||
_, ok := accessLogTableMapping[cacheKey]
|
||||
definition, ok := httpAccessLogTableMapping[cacheKey]
|
||||
accessLogLocker.RUnlock()
|
||||
if ok {
|
||||
return definition, nil
|
||||
}
|
||||
}
|
||||
|
||||
tableNames, err := db.TableNames()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if utils.ContainsStringInsensitive(tableNames, tableName) {
|
||||
table, err := db.FindTable(tableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accessLogLocker.Lock()
|
||||
var definition = &httpAccessLogDefinition{
|
||||
Name: tableName,
|
||||
HasRemoteAddr: table.FindFieldWithName("remoteAddr") != nil,
|
||||
HasDomain: table.FindFieldWithName("domain") != nil,
|
||||
Exists: true,
|
||||
}
|
||||
httpAccessLogTableMapping[cacheKey] = definition
|
||||
accessLogLocker.Unlock()
|
||||
return definition, nil
|
||||
}
|
||||
|
||||
if !force {
|
||||
return &httpAccessLogDefinition{
|
||||
Name: tableName,
|
||||
HasRemoteAddr: true,
|
||||
HasDomain: true,
|
||||
Exists: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 创建表格
|
||||
_, err = db.Exec("CREATE TABLE `" + tableName + "` (\n `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',\n `serverId` int(11) unsigned DEFAULT '0' COMMENT '服务ID',\n `nodeId` int(11) unsigned DEFAULT '0' COMMENT '节点ID',\n `status` int(3) unsigned DEFAULT '0' COMMENT '状态码',\n `createdAt` bigint(11) unsigned DEFAULT '0' COMMENT '创建时间',\n `content` json DEFAULT NULL COMMENT '日志内容',\n `requestId` varchar(128) DEFAULT NULL COMMENT '请求ID',\n `firewallPolicyId` int(11) unsigned DEFAULT '0' COMMENT 'WAF策略ID',\n `firewallRuleGroupId` int(11) unsigned DEFAULT '0' COMMENT 'WAF分组ID',\n `firewallRuleSetId` int(11) unsigned DEFAULT '0' COMMENT 'WAF集ID',\n `firewallRuleId` int(11) unsigned DEFAULT '0' COMMENT 'WAF规则ID',\n `remoteAddr` varchar(64) DEFAULT NULL COMMENT 'IP地址',\n `domain` varchar(128) DEFAULT NULL COMMENT '域名',\n `requestBody` mediumblob COMMENT '请求内容',\n `responseBody` mediumblob COMMENT '响应内容',\n PRIMARY KEY (`id`),\n KEY `serverId` (`serverId`),\n KEY `nodeId` (`nodeId`),\n KEY `serverId_status` (`serverId`,`status`),\n KEY `requestId` (`requestId`),\n KEY `firewallPolicyId` (`firewallPolicyId`),\n KEY `firewallRuleGroupId` (`firewallRuleGroupId`),\n KEY `firewallRuleSetId` (`firewallRuleSetId`),\n KEY `firewallRuleId` (`firewallRuleId`),\n KEY `remoteAddr` (`remoteAddr`),\n KEY `domain` (`domain`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='访问日志';")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accessLogLocker.Lock()
|
||||
var definition = &httpAccessLogDefinition{
|
||||
Name: tableName,
|
||||
HasRemoteAddr: true,
|
||||
Exists: true,
|
||||
}
|
||||
httpAccessLogTableMapping[cacheKey] = definition
|
||||
accessLogLocker.Unlock()
|
||||
|
||||
return definition, nil
|
||||
}
|
||||
|
||||
func findNSAccessLogTable(db *dbs.DB, day string, force bool) (string, error) {
|
||||
config, err := db.Config()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
tableName := "edgeNSAccessLogs_" + day
|
||||
cacheKey := tableName + "_" + fmt.Sprintf("%d", crc32.ChecksumIEEE([]byte(config.Dsn)))
|
||||
|
||||
if !force {
|
||||
accessLogLocker.RLock()
|
||||
_, ok := nsAccessLogTableMapping[cacheKey]
|
||||
accessLogLocker.RUnlock()
|
||||
if ok {
|
||||
return tableName, nil
|
||||
@@ -101,27 +237,27 @@ func findAccessLogTable(db *dbs.DB, day string, force bool) (string, error) {
|
||||
return tableName, err
|
||||
}
|
||||
|
||||
if lists.ContainsString(tableNames, tableName) {
|
||||
if utils.ContainsStringInsensitive(tableNames, tableName) {
|
||||
accessLogLocker.Lock()
|
||||
accessLogTableMapping[cacheKey] = true
|
||||
nsAccessLogTableMapping[cacheKey] = true
|
||||
accessLogLocker.Unlock()
|
||||
return tableName, nil
|
||||
}
|
||||
|
||||
// 创建表格
|
||||
_, err = db.Exec("CREATE TABLE `" + tableName + "` (\n `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',\n `serverId` int(11) unsigned DEFAULT '0' COMMENT '服务ID',\n `nodeId` int(11) unsigned DEFAULT '0' COMMENT '节点ID',\n `status` int(3) unsigned DEFAULT '0' COMMENT '状态码',\n `createdAt` bigint(11) unsigned DEFAULT '0' COMMENT '创建时间',\n `content` json DEFAULT NULL COMMENT '日志内容',\n `requestId` varchar(128) DEFAULT NULL COMMENT '请求ID',\n `firewallPolicyId` int(11) unsigned DEFAULT '0' COMMENT 'WAF策略ID',\n `firewallRuleGroupId` int(11) unsigned DEFAULT '0' COMMENT 'WAF分组ID',\n `firewallRuleSetId` int(11) unsigned DEFAULT '0' COMMENT 'WAF集ID',\n `firewallRuleId` int(11) unsigned DEFAULT '0' COMMENT 'WAF规则ID',\n PRIMARY KEY (`id`),\n KEY `serverId` (`serverId`),\n KEY `nodeId` (`nodeId`),\n KEY `serverId_status` (`serverId`,`status`),\n KEY `requestId` (`requestId`),\n KEY `firewallPolicyId` (`firewallPolicyId`),\n KEY `firewallRuleGroupId` (`firewallRuleGroupId`),\n KEY `firewallRuleSetId` (`firewallRuleSetId`),\n KEY `firewallRuleId` (`firewallRuleId`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='访问日志';")
|
||||
_, err = db.Exec("CREATE TABLE `" + tableName + "` (\n `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',\n `nodeId` int(11) unsigned DEFAULT '0' COMMENT '节点ID',\n `domainId` int(11) unsigned DEFAULT '0' COMMENT '域名ID',\n `recordId` int(11) unsigned DEFAULT '0' COMMENT '记录ID',\n `content` json DEFAULT NULL COMMENT '访问数据',\n `requestId` varchar(128) DEFAULT NULL COMMENT '请求ID',\n `createdAt` bigint(11) unsigned DEFAULT '0' COMMENT '创建时间',\n `remoteAddr` varchar(128) DEFAULT NULL COMMENT 'IP',\n PRIMARY KEY (`id`),\n KEY `nodeId` (`nodeId`),\n KEY `domainId` (`domainId`),\n KEY `recordId` (`recordId`),\n KEY `requestId` (`requestId`),\n KEY `remoteAddr` (`remoteAddr`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='域名服务访问日志';")
|
||||
if err != nil {
|
||||
return tableName, err
|
||||
}
|
||||
|
||||
accessLogLocker.Lock()
|
||||
accessLogTableMapping[cacheKey] = true
|
||||
nsAccessLogTableMapping[cacheKey] = true
|
||||
accessLogLocker.Unlock()
|
||||
|
||||
return tableName, nil
|
||||
}
|
||||
|
||||
// 初始化数据库连接
|
||||
// DBNodeInitializer 初始化数据库连接
|
||||
type DBNodeInitializer struct {
|
||||
}
|
||||
|
||||
@@ -129,12 +265,12 @@ func NewDBNodeInitializer() *DBNodeInitializer {
|
||||
return &DBNodeInitializer{}
|
||||
}
|
||||
|
||||
// 启动
|
||||
// Start 启动
|
||||
func (this *DBNodeInitializer) Start() {
|
||||
// 初始运行
|
||||
err := this.loop()
|
||||
if err != nil {
|
||||
logs.Println("[DB_NODE]" + err.Error())
|
||||
remotelogs.Error("DB_NODE", err.Error())
|
||||
}
|
||||
|
||||
// 定时运行
|
||||
@@ -142,7 +278,7 @@ func (this *DBNodeInitializer) Start() {
|
||||
for range ticker.C {
|
||||
err := this.loop()
|
||||
if err != nil {
|
||||
logs.Println("[DB_NODE]" + err.Error())
|
||||
remotelogs.Error("DB_NODE", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,8 +302,9 @@ func (this *DBNodeInitializer) loop() error {
|
||||
if !lists.ContainsInt64(nodeIds, nodeId) {
|
||||
closingDbs = append(closingDbs, db)
|
||||
delete(accessLogDBMapping, nodeId)
|
||||
delete(accessLogDAOMapping, nodeId)
|
||||
logs.Println("[DB_NODE]close db node '" + strconv.FormatInt(nodeId, 10) + "'")
|
||||
delete(httpAccessLogDAOMapping, nodeId)
|
||||
delete(nsAccessLogDAOMapping, nodeId)
|
||||
remotelogs.Error("DB_NODE", "close db node '"+strconv.FormatInt(nodeId, 10)+"'")
|
||||
}
|
||||
}
|
||||
accessLogLocker.Unlock()
|
||||
@@ -188,7 +325,7 @@ func (this *DBNodeInitializer) loop() error {
|
||||
// 检查配置是否有变化
|
||||
oldConfig, err := db.Config()
|
||||
if err != nil {
|
||||
logs.Println("[DB_NODE]read database old config failed: " + err.Error())
|
||||
remotelogs.Error("DB_NODE", "read database old config failed: "+err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -207,51 +344,99 @@ func (this *DBNodeInitializer) loop() error {
|
||||
}
|
||||
db, err := dbs.NewInstanceFromConfig(config)
|
||||
if err != nil {
|
||||
logs.Println("[DB_NODE]initialize database config failed: " + err.Error())
|
||||
remotelogs.Error("DB_NODE", "initialize database config failed: "+err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
// 检查表是否存在
|
||||
tableName, err := findAccessLogTable(db, timeutil.Format("Ymd"), false)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "1050") { // 非表格已存在错误
|
||||
logs.Println("[DB_NODE]create first table in database node failed: " + err.Error())
|
||||
// httpAccessLog
|
||||
{
|
||||
tableDef, err := findHTTPAccessLogTable(db, timeutil.Format("Ymd"), true)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "1050") { // 非表格已存在错误
|
||||
remotelogs.Error("DB_NODE", "create first table in database node failed: "+err.Error())
|
||||
|
||||
// 创建节点日志
|
||||
createLogErr := SharedNodeLogDAO.CreateLog(nil, NodeRoleDatabase, nodeId, 0, "error", "ACCESS_LOG", "can not create access log table: "+err.Error(), time.Now().Unix())
|
||||
if createLogErr != nil {
|
||||
logs.Println("[NODE_LOG]" + createLogErr.Error())
|
||||
// 创建节点日志
|
||||
createLogErr := SharedNodeLogDAO.CreateLog(nil, nodeconfigs.NodeRoleDatabase, nodeId, 0, 0, "error", "ACCESS_LOG", "can not create access log table: "+err.Error(), time.Now().Unix(), "", nil)
|
||||
if createLogErr != nil {
|
||||
remotelogs.Error("NODE_LOG", createLogErr.Error())
|
||||
}
|
||||
|
||||
continue
|
||||
} else {
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
|
||||
continue
|
||||
} else {
|
||||
err = nil
|
||||
}
|
||||
|
||||
daoObject := dbs.DAOObject{
|
||||
Instance: db,
|
||||
DB: node.Name + "(id:" + strconv.Itoa(int(node.Id)) + ")",
|
||||
Table: tableDef.Name,
|
||||
PkName: "id",
|
||||
Model: new(HTTPAccessLog),
|
||||
}
|
||||
err = daoObject.Init()
|
||||
if err != nil {
|
||||
remotelogs.Error("DB_NODE", "initialize dao failed: "+err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
accessLogLocker.Lock()
|
||||
accessLogDBMapping[nodeId] = db
|
||||
dao := &HTTPAccessLogDAO{
|
||||
DAOObject: daoObject,
|
||||
}
|
||||
httpAccessLogDAOMapping[nodeId] = &HTTPAccessLogDAOWrapper{
|
||||
DAO: dao,
|
||||
NodeId: nodeId,
|
||||
}
|
||||
accessLogLocker.Unlock()
|
||||
}
|
||||
|
||||
daoObject := dbs.DAOObject{
|
||||
Instance: db,
|
||||
DB: node.Name + "(id:" + strconv.Itoa(int(node.Id)) + ")",
|
||||
Table: tableName,
|
||||
PkName: "id",
|
||||
Model: new(HTTPAccessLog),
|
||||
}
|
||||
err = daoObject.Init()
|
||||
if err != nil {
|
||||
logs.Println("[DB_NODE]initialize dao failed: " + err.Error())
|
||||
continue
|
||||
}
|
||||
// nsAccessLog
|
||||
{
|
||||
tableName, err := findNSAccessLogTable(db, timeutil.Format("Ymd"), false)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "1050") { // 非表格已存在错误
|
||||
remotelogs.Error("DB_NODE", "create first table in database node failed: "+err.Error())
|
||||
|
||||
accessLogLocker.Lock()
|
||||
accessLogDBMapping[nodeId] = db
|
||||
dao := &HTTPAccessLogDAO{
|
||||
DAOObject: daoObject,
|
||||
// 创建节点日志
|
||||
createLogErr := SharedNodeLogDAO.CreateLog(nil, nodeconfigs.NodeRoleDatabase, nodeId, 0, 0, "error", "ACCESS_LOG", "can not create access log table: "+err.Error(), time.Now().Unix(), "", nil)
|
||||
if createLogErr != nil {
|
||||
remotelogs.Error("NODE_LOG", createLogErr.Error())
|
||||
}
|
||||
|
||||
continue
|
||||
} else {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
daoObject := dbs.DAOObject{
|
||||
Instance: db,
|
||||
DB: node.Name + "(id:" + strconv.Itoa(int(node.Id)) + ")",
|
||||
Table: tableName,
|
||||
PkName: "id",
|
||||
Model: new(NSAccessLog),
|
||||
}
|
||||
err = daoObject.Init()
|
||||
if err != nil {
|
||||
remotelogs.Error("DB_NODE", "initialize dao failed: "+err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
accessLogLocker.Lock()
|
||||
accessLogDBMapping[nodeId] = db
|
||||
dao := &NSAccessLogDAO{
|
||||
DAOObject: daoObject,
|
||||
}
|
||||
nsAccessLogDAOMapping[nodeId] = &NSAccessLogDAOWrapper{
|
||||
DAO: dao,
|
||||
NodeId: nodeId,
|
||||
}
|
||||
accessLogLocker.Unlock()
|
||||
}
|
||||
accessLogDAOMapping[nodeId] = &HTTPAccessLogDAOWrapper{
|
||||
DAO: dao,
|
||||
NodeId: nodeId,
|
||||
}
|
||||
accessLogLocker.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,13 +12,13 @@ func TestDBNodeInitializer_loop(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(len(accessLogDBMapping), len(accessLogDAOMapping))
|
||||
t.Log(len(accessLogDBMapping), len(httpAccessLogDAOMapping))
|
||||
}
|
||||
|
||||
func TestFindAccessLogTable(t *testing.T) {
|
||||
before := time.Now()
|
||||
db := SharedHTTPAccessLogDAO.Instance
|
||||
tableName, err := findAccessLogTable(db, "20201010", false)
|
||||
tableName, err := findHTTPAccessLogTable(db, "20201010", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -26,7 +26,7 @@ func TestFindAccessLogTable(t *testing.T) {
|
||||
t.Log(time.Since(before).Seconds()*1000, "ms")
|
||||
|
||||
before = time.Now()
|
||||
tableName, err = findAccessLogTable(db, "20201010", false)
|
||||
tableName, err = findHTTPAccessLogTable(db, "20201010", false)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -40,6 +40,6 @@ func BenchmarkFindAccessLogTable(b *testing.B) {
|
||||
|
||||
runtime.GOMAXPROCS(1)
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = findAccessLogTable(db, "20201010", false)
|
||||
_, _ = findHTTPAccessLogTable(db, "20201010", false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@ package dns
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/dnstypes"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
@@ -39,7 +40,7 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
// 启用条目
|
||||
// EnableDNSDomain 启用条目
|
||||
func (this *DNSDomainDAO) EnableDNSDomain(tx *dbs.Tx, id int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -48,7 +49,7 @@ func (this *DNSDomainDAO) EnableDNSDomain(tx *dbs.Tx, id int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 禁用条目
|
||||
// DisableDNSDomain 禁用条目
|
||||
func (this *DNSDomainDAO) DisableDNSDomain(tx *dbs.Tx, id int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -57,19 +58,30 @@ func (this *DNSDomainDAO) DisableDNSDomain(tx *dbs.Tx, id int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 查找启用中的条目
|
||||
func (this *DNSDomainDAO) FindEnabledDNSDomain(tx *dbs.Tx, id int64) (*DNSDomain, error) {
|
||||
// FindEnabledDNSDomain 查找启用中的条目
|
||||
func (this *DNSDomainDAO) FindEnabledDNSDomain(tx *dbs.Tx, domainId int64, cacheMap *utils.CacheMap) (*DNSDomain, error) {
|
||||
var cacheKey = this.Table + ":record:" + types.String(domainId)
|
||||
if cacheMap != nil {
|
||||
cache, _ := cacheMap.Get(cacheKey)
|
||||
if cache != nil {
|
||||
return cache.(*DNSDomain), nil
|
||||
}
|
||||
}
|
||||
|
||||
result, err := this.Query(tx).
|
||||
Pk(id).
|
||||
Pk(domainId).
|
||||
Attr("state", DNSDomainStateEnabled).
|
||||
Find()
|
||||
if result == nil {
|
||||
return nil, err
|
||||
}
|
||||
if cacheMap != nil {
|
||||
cacheMap.Put(cacheKey, result)
|
||||
}
|
||||
return result.(*DNSDomain), err
|
||||
}
|
||||
|
||||
// 根据主键查找名称
|
||||
// FindDNSDomainName 根据主键查找名称
|
||||
func (this *DNSDomainDAO) FindDNSDomainName(tx *dbs.Tx, id int64) (string, error) {
|
||||
return this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -77,7 +89,7 @@ func (this *DNSDomainDAO) FindDNSDomainName(tx *dbs.Tx, id int64) (string, error
|
||||
FindStringCol("")
|
||||
}
|
||||
|
||||
// 创建域名
|
||||
// CreateDomain 创建域名
|
||||
func (this *DNSDomainDAO) CreateDomain(tx *dbs.Tx, adminId int64, userId int64, providerId int64, name string) (int64, error) {
|
||||
op := NewDNSDomainOperator()
|
||||
op.ProviderId = providerId
|
||||
@@ -86,6 +98,7 @@ func (this *DNSDomainDAO) CreateDomain(tx *dbs.Tx, adminId int64, userId int64,
|
||||
op.Name = name
|
||||
op.State = DNSDomainStateEnabled
|
||||
op.IsOn = true
|
||||
op.IsUp = true
|
||||
err := this.Save(tx, op)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -93,7 +106,7 @@ func (this *DNSDomainDAO) CreateDomain(tx *dbs.Tx, adminId int64, userId int64,
|
||||
return types.Int64(op.Id), nil
|
||||
}
|
||||
|
||||
// 修改域名
|
||||
// UpdateDomain 修改域名
|
||||
func (this *DNSDomainDAO) UpdateDomain(tx *dbs.Tx, domainId int64, name string, isOn bool) error {
|
||||
if domainId <= 0 {
|
||||
return errors.New("invalid domainId")
|
||||
@@ -109,7 +122,7 @@ func (this *DNSDomainDAO) UpdateDomain(tx *dbs.Tx, domainId int64, name string,
|
||||
return nil
|
||||
}
|
||||
|
||||
// 查询一个服务商下面的所有域名
|
||||
// FindAllEnabledDomainsWithProviderId 查询一个服务商下面的所有域名
|
||||
func (this *DNSDomainDAO) FindAllEnabledDomainsWithProviderId(tx *dbs.Tx, providerId int64) (result []*DNSDomain, err error) {
|
||||
_, err = this.Query(tx).
|
||||
State(DNSDomainStateEnabled).
|
||||
@@ -120,7 +133,7 @@ func (this *DNSDomainDAO) FindAllEnabledDomainsWithProviderId(tx *dbs.Tx, provid
|
||||
return
|
||||
}
|
||||
|
||||
// 计算某个服务商下的域名数量
|
||||
// CountAllEnabledDomainsWithProviderId 计算某个服务商下的域名数量
|
||||
func (this *DNSDomainDAO) CountAllEnabledDomainsWithProviderId(tx *dbs.Tx, providerId int64) (int64, error) {
|
||||
return this.Query(tx).
|
||||
State(DNSDomainStateEnabled).
|
||||
@@ -128,7 +141,7 @@ func (this *DNSDomainDAO) CountAllEnabledDomainsWithProviderId(tx *dbs.Tx, provi
|
||||
Count()
|
||||
}
|
||||
|
||||
// 更新域名数据
|
||||
// UpdateDomainData 更新域名数据
|
||||
func (this *DNSDomainDAO) UpdateDomainData(tx *dbs.Tx, domainId int64, data string) error {
|
||||
if domainId <= 0 {
|
||||
return errors.New("invalid domainId")
|
||||
@@ -140,7 +153,7 @@ func (this *DNSDomainDAO) UpdateDomainData(tx *dbs.Tx, domainId int64, data stri
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新域名解析记录
|
||||
// UpdateDomainRecords 更新域名解析记录
|
||||
func (this *DNSDomainDAO) UpdateDomainRecords(tx *dbs.Tx, domainId int64, recordsJSON []byte) error {
|
||||
if domainId <= 0 {
|
||||
return errors.New("invalid domainId")
|
||||
@@ -153,7 +166,7 @@ func (this *DNSDomainDAO) UpdateDomainRecords(tx *dbs.Tx, domainId int64, record
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新线路
|
||||
// UpdateDomainRoutes 更新线路
|
||||
func (this *DNSDomainDAO) UpdateDomainRoutes(tx *dbs.Tx, domainId int64, routesJSON []byte) error {
|
||||
if domainId <= 0 {
|
||||
return errors.New("invalid domainId")
|
||||
@@ -166,8 +179,8 @@ func (this *DNSDomainDAO) UpdateDomainRoutes(tx *dbs.Tx, domainId int64, routesJ
|
||||
return err
|
||||
}
|
||||
|
||||
// 查找域名线路
|
||||
func (this *DNSDomainDAO) FindDomainRoutes(tx *dbs.Tx, domainId int64) ([]*dnsclients.Route, error) {
|
||||
// FindDomainRoutes 查找域名线路
|
||||
func (this *DNSDomainDAO) FindDomainRoutes(tx *dbs.Tx, domainId int64) ([]*dnstypes.Route, error) {
|
||||
routes, err := this.Query(tx).
|
||||
Pk(domainId).
|
||||
Result("routes").
|
||||
@@ -178,7 +191,7 @@ func (this *DNSDomainDAO) FindDomainRoutes(tx *dbs.Tx, domainId int64) ([]*dnscl
|
||||
if len(routes) == 0 || routes == "null" {
|
||||
return nil, nil
|
||||
}
|
||||
result := []*dnsclients.Route{}
|
||||
result := []*dnstypes.Route{}
|
||||
err = json.Unmarshal([]byte(routes), &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -186,7 +199,7 @@ func (this *DNSDomainDAO) FindDomainRoutes(tx *dbs.Tx, domainId int64) ([]*dnscl
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 查找线路名称
|
||||
// FindDomainRouteName 查找线路名称
|
||||
func (this *DNSDomainDAO) FindDomainRouteName(tx *dbs.Tx, domainId int64, routeCode string) (string, error) {
|
||||
routes, err := this.FindDomainRoutes(tx, domainId)
|
||||
if err != nil {
|
||||
@@ -200,7 +213,7 @@ func (this *DNSDomainDAO) FindDomainRouteName(tx *dbs.Tx, domainId int64, routeC
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// 判断是否有域名可选
|
||||
// ExistAvailableDomains 判断是否有域名可选
|
||||
func (this *DNSDomainDAO) ExistAvailableDomains(tx *dbs.Tx) (bool, error) {
|
||||
subQuery, err := SharedDNSProviderDAO.Query(tx).
|
||||
Where("state=1"). // 这里要使用非变量
|
||||
@@ -216,8 +229,10 @@ func (this *DNSDomainDAO) ExistAvailableDomains(tx *dbs.Tx) (bool, error) {
|
||||
Exist()
|
||||
}
|
||||
|
||||
// 检查域名解析记录是否存在
|
||||
// ExistDomainRecord 检查域名解析记录是否存在
|
||||
func (this *DNSDomainDAO) ExistDomainRecord(tx *dbs.Tx, domainId int64, recordName string, recordType string, recordRoute string, recordValue string) (bool, error) {
|
||||
recordType = strings.ToUpper(recordType)
|
||||
|
||||
query := maps.Map{
|
||||
"name": recordName,
|
||||
"type": recordType,
|
||||
@@ -239,10 +254,38 @@ func (this *DNSDomainDAO) ExistDomainRecord(tx *dbs.Tx, domainId int64, recordNa
|
||||
}
|
||||
}
|
||||
}
|
||||
recordType = strings.ToUpper(recordType)
|
||||
return this.Query(tx).
|
||||
Pk(domainId).
|
||||
Where("JSON_CONTAINS(records, :query)").
|
||||
Param("query", query.AsJSON()).
|
||||
Exist()
|
||||
}
|
||||
|
||||
// FindEnabledDomainWithName 根据名称查找某个域名
|
||||
func (this *DNSDomainDAO) FindEnabledDomainWithName(tx *dbs.Tx, providerId int64, domainName string) (*DNSDomain, error) {
|
||||
one, err := this.Query(tx).
|
||||
State(DNSDomainStateEnabled).
|
||||
Attr("providerId", providerId).
|
||||
Attr("name", domainName).
|
||||
Find()
|
||||
if one != nil {
|
||||
return one.(*DNSDomain), nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// UpdateDomainIsUp 设置是否在线
|
||||
func (this *DNSDomainDAO) UpdateDomainIsUp(tx *dbs.Tx, domainId int64, isUp bool) error {
|
||||
return this.Query(tx).
|
||||
Pk(domainId).
|
||||
Set("isUp", isUp).
|
||||
UpdateQuickly()
|
||||
}
|
||||
|
||||
// UpdateDomainIsDeleted 设置域名为删除
|
||||
func (this *DNSDomainDAO) UpdateDomainIsDeleted(tx *dbs.Tx, domainId int64, isDeleted bool) error {
|
||||
return this.Query(tx).
|
||||
Pk(domainId).
|
||||
Set("isDeleted", isDeleted).
|
||||
UpdateQuickly()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package dns
|
||||
|
||||
// 管理的域名
|
||||
// DNSDomain 管理的域名
|
||||
type DNSDomain struct {
|
||||
Id uint32 `field:"id"` // ID
|
||||
AdminId uint32 `field:"adminId"` // 管理员ID
|
||||
@@ -14,7 +14,9 @@ type DNSDomain struct {
|
||||
Data string `field:"data"` // 原始数据信息
|
||||
Records string `field:"records"` // 所有解析记录
|
||||
Routes string `field:"routes"` // 线路数据
|
||||
IsUp uint8 `field:"isUp"` // 是否在线
|
||||
State uint8 `field:"state"` // 状态
|
||||
IsDeleted uint8 `field:"isDeleted"` // 是否已删除
|
||||
}
|
||||
|
||||
type DNSDomainOperator struct {
|
||||
@@ -30,7 +32,9 @@ type DNSDomainOperator struct {
|
||||
Data interface{} // 原始数据信息
|
||||
Records interface{} // 所有解析记录
|
||||
Routes interface{} // 线路数据
|
||||
IsUp interface{} // 是否在线
|
||||
State interface{} // 状态
|
||||
IsDeleted interface{} // 是否已删除
|
||||
}
|
||||
|
||||
func NewDNSDomainOperator() *DNSDomainOperator {
|
||||
|
||||
@@ -2,15 +2,15 @@ package dns
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/dnstypes"
|
||||
)
|
||||
|
||||
// 获取所有的线路
|
||||
func (this *DNSDomain) DecodeRoutes() ([]*dnsclients.Route, error) {
|
||||
// DecodeRoutes 获取所有的线路
|
||||
func (this *DNSDomain) DecodeRoutes() ([]*dnstypes.Route, error) {
|
||||
if len(this.Routes) == 0 || this.Routes == "null" {
|
||||
return nil, nil
|
||||
}
|
||||
result := []*dnsclients.Route{}
|
||||
result := []*dnstypes.Route{}
|
||||
err := json.Unmarshal([]byte(this.Routes), &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -18,7 +18,7 @@ func (this *DNSDomain) DecodeRoutes() ([]*dnsclients.Route, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 检查是否包含某个线路
|
||||
// ContainsRouteCode 检查是否包含某个线路
|
||||
func (this *DNSDomain) ContainsRouteCode(route string) (bool, error) {
|
||||
routes, err := this.DecodeRoutes()
|
||||
if err != nil {
|
||||
@@ -32,13 +32,13 @@ func (this *DNSDomain) ContainsRouteCode(route string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// 获取所有的记录
|
||||
func (this *DNSDomain) DecodeRecords() ([]*dnsclients.Record, error) {
|
||||
// DecodeRecords 获取所有的记录
|
||||
func (this *DNSDomain) DecodeRecords() ([]*dnstypes.Record, error) {
|
||||
records := this.Records
|
||||
if len(records) == 0 || records == "null" {
|
||||
return nil, nil
|
||||
}
|
||||
result := []*dnsclients.Record{}
|
||||
result := []*dnstypes.Record{}
|
||||
err := json.Unmarshal([]byte(records), &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -36,7 +36,7 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
// 启用条目
|
||||
// EnableDNSProvider 启用条目
|
||||
func (this *DNSProviderDAO) EnableDNSProvider(tx *dbs.Tx, id int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -45,7 +45,7 @@ func (this *DNSProviderDAO) EnableDNSProvider(tx *dbs.Tx, id int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 禁用条目
|
||||
// DisableDNSProvider 禁用条目
|
||||
func (this *DNSProviderDAO) DisableDNSProvider(tx *dbs.Tx, id int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -54,7 +54,7 @@ func (this *DNSProviderDAO) DisableDNSProvider(tx *dbs.Tx, id int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 查找启用中的条目
|
||||
// FindEnabledDNSProvider 查找启用中的条目
|
||||
func (this *DNSProviderDAO) FindEnabledDNSProvider(tx *dbs.Tx, id int64) (*DNSProvider, error) {
|
||||
result, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -66,7 +66,7 @@ func (this *DNSProviderDAO) FindEnabledDNSProvider(tx *dbs.Tx, id int64) (*DNSPr
|
||||
return result.(*DNSProvider), err
|
||||
}
|
||||
|
||||
// 创建服务商
|
||||
// CreateDNSProvider 创建服务商
|
||||
func (this *DNSProviderDAO) CreateDNSProvider(tx *dbs.Tx, adminId int64, userId int64, providerType string, name string, apiParamsJSON []byte) (int64, error) {
|
||||
op := NewDNSProviderOperator()
|
||||
op.AdminId = adminId
|
||||
@@ -84,7 +84,7 @@ func (this *DNSProviderDAO) CreateDNSProvider(tx *dbs.Tx, adminId int64, userId
|
||||
return types.Int64(op.Id), nil
|
||||
}
|
||||
|
||||
// 修改服务商
|
||||
// UpdateDNSProvider 修改服务商
|
||||
func (this *DNSProviderDAO) UpdateDNSProvider(tx *dbs.Tx, dnsProviderId int64, name string, apiParamsJSON []byte) error {
|
||||
if dnsProviderId <= 0 {
|
||||
return errors.New("invalid dnsProviderId")
|
||||
@@ -106,16 +106,25 @@ func (this *DNSProviderDAO) UpdateDNSProvider(tx *dbs.Tx, dnsProviderId int64, n
|
||||
return nil
|
||||
}
|
||||
|
||||
// 计算服务商数量
|
||||
func (this *DNSProviderDAO) CountAllEnabledDNSProviders(tx *dbs.Tx, adminId int64, userId int64) (int64, error) {
|
||||
return dbutils.NewQuery(tx, this, adminId, userId).
|
||||
State(DNSProviderStateEnabled).
|
||||
// CountAllEnabledDNSProviders 计算服务商数量
|
||||
func (this *DNSProviderDAO) CountAllEnabledDNSProviders(tx *dbs.Tx, adminId int64, userId int64, keyword string) (int64, error) {
|
||||
var query = dbutils.NewQuery(tx, this, adminId, userId)
|
||||
if len(keyword) > 0 {
|
||||
query.Where("(name LIKE :keyword)").
|
||||
Param("keyword", "%"+keyword+"%")
|
||||
}
|
||||
return query.State(DNSProviderStateEnabled).
|
||||
Count()
|
||||
}
|
||||
|
||||
// 列出单页服务商
|
||||
func (this *DNSProviderDAO) ListEnabledDNSProviders(tx *dbs.Tx, adminId int64, userId int64, offset int64, size int64) (result []*DNSProvider, err error) {
|
||||
_, err = dbutils.NewQuery(tx, this, adminId, userId).
|
||||
// ListEnabledDNSProviders 列出单页服务商
|
||||
func (this *DNSProviderDAO) ListEnabledDNSProviders(tx *dbs.Tx, adminId int64, userId int64, keyword string, offset int64, size int64) (result []*DNSProvider, err error) {
|
||||
var query = dbutils.NewQuery(tx, this, adminId, userId)
|
||||
if len(keyword) > 0 {
|
||||
query.Where("(name LIKE :keyword)").
|
||||
Param("keyword", "%"+keyword+"%")
|
||||
}
|
||||
_, err = query.
|
||||
State(DNSProviderStateEnabled).
|
||||
Offset(offset).
|
||||
Limit(size).
|
||||
@@ -125,7 +134,7 @@ func (this *DNSProviderDAO) ListEnabledDNSProviders(tx *dbs.Tx, adminId int64, u
|
||||
return
|
||||
}
|
||||
|
||||
// 列出所有服务商
|
||||
// FindAllEnabledDNSProviders 列出所有服务商
|
||||
func (this *DNSProviderDAO) FindAllEnabledDNSProviders(tx *dbs.Tx, adminId int64, userId int64) (result []*DNSProvider, err error) {
|
||||
_, err = dbutils.NewQuery(tx, this, adminId, userId).
|
||||
State(DNSProviderStateEnabled).
|
||||
@@ -135,7 +144,7 @@ func (this *DNSProviderDAO) FindAllEnabledDNSProviders(tx *dbs.Tx, adminId int64
|
||||
return
|
||||
}
|
||||
|
||||
// 查询某个类型下的所有服务商
|
||||
// FindAllEnabledDNSProvidersWithType 查询某个类型下的所有服务商
|
||||
func (this *DNSProviderDAO) FindAllEnabledDNSProvidersWithType(tx *dbs.Tx, providerType string) (result []*DNSProvider, err error) {
|
||||
_, err = this.Query(tx).
|
||||
State(DNSProviderStateEnabled).
|
||||
@@ -146,7 +155,7 @@ func (this *DNSProviderDAO) FindAllEnabledDNSProvidersWithType(tx *dbs.Tx, provi
|
||||
return
|
||||
}
|
||||
|
||||
// 更新数据更新时间
|
||||
// UpdateProviderDataUpdatedTime 更新数据更新时间
|
||||
func (this *DNSProviderDAO) UpdateProviderDataUpdatedTime(tx *dbs.Tx, providerId int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(providerId).
|
||||
|
||||
@@ -94,8 +94,12 @@ func (this *DNSTaskDAO) FindAllDoingTasks(tx *dbs.Tx) (result []*DNSTask, err er
|
||||
}
|
||||
|
||||
// FindAllDoingOrErrorTasks 查找正在执行的和错误的任务
|
||||
func (this *DNSTaskDAO) FindAllDoingOrErrorTasks(tx *dbs.Tx) (result []*DNSTask, err error) {
|
||||
_, err = this.Query(tx).
|
||||
func (this *DNSTaskDAO) FindAllDoingOrErrorTasks(tx *dbs.Tx, nodeClusterId int64) (result []*DNSTask, err error) {
|
||||
var query = this.Query(tx)
|
||||
if nodeClusterId > 0 {
|
||||
query.Attr("clusterId", nodeClusterId)
|
||||
}
|
||||
_, err = query.
|
||||
Where("(isDone=0 OR (isDone=1 AND isOk=0))").
|
||||
AscPk().
|
||||
Slice(&result).
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
func TestDNSTaskDAO_CreateDNSTask(t *testing.T) {
|
||||
dbs.NotifyReady()
|
||||
err := SharedDNSTaskDAO.CreateDNSTask(nil, 1, 2, 3, "taskType")
|
||||
err := SharedDNSTaskDAO.CreateDNSTask(nil, 1, 2, 3, 0, "taskType")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
207
internal/db/models/dns/dnsutils/dns_utils.go
Normal file
207
internal/db/models/dns/dnsutils/dns_utils.go
Normal file
@@ -0,0 +1,207 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package dnsutils
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models/dns"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
)
|
||||
|
||||
// CheckClusterDNS 检查集群的DNS问题
|
||||
// 藏这么深是避免package循环引用的问题
|
||||
func CheckClusterDNS(tx *dbs.Tx, cluster *models.NodeCluster) (issues []*pb.DNSIssue, err error) {
|
||||
clusterId := int64(cluster.Id)
|
||||
domainId := int64(cluster.DnsDomainId)
|
||||
|
||||
// 检查域名
|
||||
domain, err := dns.SharedDNSDomainDAO.FindEnabledDNSDomain(tx, domainId, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if domain == nil {
|
||||
issues = append(issues, &pb.DNSIssue{
|
||||
Target: cluster.Name,
|
||||
TargetId: clusterId,
|
||||
Type: "cluster",
|
||||
Description: "域名选择错误,需要重新选择",
|
||||
Params: nil,
|
||||
MustFix: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Provider
|
||||
provider, err := dns.SharedDNSProviderDAO.FindEnabledDNSProvider(tx, int64(domain.ProviderId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if provider == nil {
|
||||
issues = append(issues, &pb.DNSIssue{
|
||||
Target: cluster.Name,
|
||||
TargetId: clusterId,
|
||||
Type: "cluster",
|
||||
Description: "域名服务商不可用,需要重新选择",
|
||||
Params: nil,
|
||||
MustFix: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
paramsMap, err := provider.DecodeAPIParams()
|
||||
if err != nil {
|
||||
issues = append(issues, &pb.DNSIssue{
|
||||
Target: cluster.Name,
|
||||
TargetId: clusterId,
|
||||
Type: "cluster",
|
||||
Description: "域名服务商参数配置错误,需要重新配置",
|
||||
Params: nil,
|
||||
MustFix: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
var dnsProvider = dnsclients.FindProvider(provider.Type)
|
||||
if dnsProvider == nil {
|
||||
issues = append(issues, &pb.DNSIssue{
|
||||
Target: cluster.Name,
|
||||
TargetId: clusterId,
|
||||
Type: "cluster",
|
||||
Description: "目前不支持\"" + provider.Type + "\"服务商,需要重新配置",
|
||||
Params: nil,
|
||||
MustFix: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
err = dnsProvider.Auth(paramsMap)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var defaultRoute = dnsProvider.DefaultRoute()
|
||||
var hasDefaultRoute = len(defaultRoute) > 0
|
||||
|
||||
// 检查二级域名
|
||||
if len(cluster.DnsName) == 0 {
|
||||
issues = append(issues, &pb.DNSIssue{
|
||||
Target: cluster.Name,
|
||||
TargetId: clusterId,
|
||||
Type: "cluster",
|
||||
Description: "没有设置二级域名",
|
||||
Params: nil,
|
||||
MustFix: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// TODO 检查域名格式
|
||||
|
||||
// TODO 检查域名是否已解析
|
||||
|
||||
// 检查节点
|
||||
nodes, err := models.SharedNodeDAO.FindAllEnabledNodesDNSWithClusterId(tx, clusterId, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO 检查节点数量不能为0
|
||||
|
||||
for _, node := range nodes {
|
||||
nodeId := int64(node.Id)
|
||||
|
||||
routeCodes, err := node.DNSRouteCodesForDomainId(domainId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(routeCodes) == 0 && !hasDefaultRoute {
|
||||
issues = append(issues, &pb.DNSIssue{
|
||||
Target: node.Name,
|
||||
TargetId: nodeId,
|
||||
Type: "node",
|
||||
Description: "没有选择节点所属线路",
|
||||
Params: map[string]string{
|
||||
"clusterName": cluster.Name,
|
||||
"clusterId": numberutils.FormatInt64(clusterId),
|
||||
},
|
||||
MustFix: true,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// 检查线路是否在已有线路中
|
||||
for _, routeCode := range routeCodes {
|
||||
routeOk, err := domain.ContainsRouteCode(routeCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !routeOk {
|
||||
issues = append(issues, &pb.DNSIssue{
|
||||
Target: node.Name,
|
||||
TargetId: nodeId,
|
||||
Type: "node",
|
||||
Description: "线路已经失效,请重新选择",
|
||||
Params: map[string]string{
|
||||
"clusterName": cluster.Name,
|
||||
"clusterId": numberutils.FormatInt64(clusterId),
|
||||
},
|
||||
MustFix: true,
|
||||
})
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// 检查IP地址
|
||||
ipAddr, _, err := models.SharedNodeIPAddressDAO.FindFirstNodeAccessIPAddress(tx, nodeId, nodeconfigs.NodeRoleNode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ipAddr) == 0 {
|
||||
issues = append(issues, &pb.DNSIssue{
|
||||
Target: node.Name,
|
||||
TargetId: nodeId,
|
||||
Type: "node",
|
||||
Description: "没有设置IP地址",
|
||||
Params: map[string]string{
|
||||
"clusterName": cluster.Name,
|
||||
"clusterId": numberutils.FormatInt64(clusterId),
|
||||
},
|
||||
MustFix: true,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO 检查是否有解析记录
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindDefaultDomainRoute 获取域名默认的线路
|
||||
func FindDefaultDomainRoute(tx *dbs.Tx, domain *dns.DNSDomain) (string, error) {
|
||||
if domain == nil {
|
||||
return "", errors.New("can not find domain")
|
||||
}
|
||||
|
||||
provider, err := dns.SharedDNSProviderDAO.FindEnabledDNSProvider(tx, int64(domain.ProviderId))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if provider == nil {
|
||||
return "", errors.New("provider not found")
|
||||
}
|
||||
paramsMap, err := provider.DecodeAPIParams()
|
||||
if err != nil {
|
||||
return "", errors.New("decode provider params failed: " + err.Error())
|
||||
}
|
||||
var dnsProvider = dnsclients.FindProvider(provider.Type)
|
||||
if dnsProvider == nil {
|
||||
return "", errors.New("not supported provider type '" + provider.Type + "'")
|
||||
}
|
||||
err = dnsProvider.Auth(paramsMap)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return dnsProvider.DefaultRoute(), nil
|
||||
}
|
||||
29
internal/db/models/dns/dnsutils/dns_utils_test.go
Normal file
29
internal/db/models/dns/dnsutils/dns_utils_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package dnsutils
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNodeClusterDAO_CheckClusterDNS(t *testing.T) {
|
||||
dbs.NotifyReady()
|
||||
|
||||
var tx *dbs.Tx
|
||||
cluster, err := models.SharedNodeClusterDAO.FindEnabledNodeCluster(tx, 34)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cluster == nil {
|
||||
t.Log("cluster not found, skip the test")
|
||||
return
|
||||
}
|
||||
issues, err := CheckClusterDNS(tx, cluster)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
logs.PrintAsJSON(issues, t)
|
||||
}
|
||||
@@ -1,20 +1,29 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/configs"
|
||||
"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/zero"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"net"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -24,10 +33,53 @@ type HTTPAccessLogDAO dbs.DAO
|
||||
|
||||
var SharedHTTPAccessLogDAO *HTTPAccessLogDAO
|
||||
|
||||
// 队列
|
||||
var oldAccessLogQueue = make(chan *pb.HTTPAccessLog)
|
||||
var accessLogQueue = make(chan *pb.HTTPAccessLog, 10_000)
|
||||
var accessLogQueueMaxLength = 100_000
|
||||
var accessLogQueuePercent = 100 // 0-100
|
||||
var accessLogCountPerSecond = 10_000 // 0 表示不限制
|
||||
var accessLogConfigJSON = []byte{}
|
||||
var accessLogQueueChanged = make(chan zero.Zero, 1)
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedHTTPAccessLogDAO = NewHTTPAccessLogDAO()
|
||||
})
|
||||
|
||||
// 队列相关
|
||||
dbs.OnReadyDone(func() {
|
||||
// 检查队列变化
|
||||
goman.New(func() {
|
||||
var ticker = time.NewTicker(60 * time.Second)
|
||||
|
||||
// 先执行一次初始化
|
||||
SharedHTTPAccessLogDAO.SetupQueue()
|
||||
|
||||
// 循环执行
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
SharedHTTPAccessLogDAO.SetupQueue()
|
||||
case <-accessLogQueueChanged:
|
||||
SharedHTTPAccessLogDAO.SetupQueue()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 导出队列内容
|
||||
goman.New(func() {
|
||||
var ticker = time.NewTicker(1 * time.Second)
|
||||
for range ticker.C {
|
||||
var tx *dbs.Tx
|
||||
err := SharedHTTPAccessLogDAO.DumpAccessLogsFromQueue(tx, accessLogCountPerSecond)
|
||||
if err != nil {
|
||||
remotelogs.Error("HTTP_ACCESS_LOG_QUEUE", "dump access logs failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func NewHTTPAccessLogDAO() *HTTPAccessLogDAO {
|
||||
@@ -41,82 +93,159 @@ func NewHTTPAccessLogDAO() *HTTPAccessLogDAO {
|
||||
}).(*HTTPAccessLogDAO)
|
||||
}
|
||||
|
||||
// 创建访问日志
|
||||
// CreateHTTPAccessLogs 创建访问日志
|
||||
func (this *HTTPAccessLogDAO) CreateHTTPAccessLogs(tx *dbs.Tx, accessLogs []*pb.HTTPAccessLog) error {
|
||||
dao := randomAccessLogDAO()
|
||||
if dao == nil {
|
||||
dao = &HTTPAccessLogDAOWrapper{
|
||||
DAO: SharedHTTPAccessLogDAO,
|
||||
NodeId: 0,
|
||||
}
|
||||
}
|
||||
return this.CreateHTTPAccessLogsWithDAO(tx, dao, accessLogs)
|
||||
}
|
||||
|
||||
// 使用特定的DAO创建访问日志
|
||||
func (this *HTTPAccessLogDAO) CreateHTTPAccessLogsWithDAO(tx *dbs.Tx, daoWrapper *HTTPAccessLogDAOWrapper, accessLogs []*pb.HTTPAccessLog) error {
|
||||
if daoWrapper == nil {
|
||||
return errors.New("dao should not be nil")
|
||||
}
|
||||
if len(accessLogs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
dao := daoWrapper.DAO
|
||||
|
||||
// TODO 改成事务批量提交,以加快速度
|
||||
|
||||
// 写入队列
|
||||
var queue = accessLogQueue // 这样写非常重要,防止在写入过程中队列有切换
|
||||
for _, accessLog := range accessLogs {
|
||||
day := timeutil.Format("Ymd", time.Unix(accessLog.Timestamp, 0))
|
||||
table, err := findAccessLogTable(dao.Instance, day, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{}
|
||||
fields["serverId"] = accessLog.ServerId
|
||||
fields["nodeId"] = accessLog.NodeId
|
||||
fields["status"] = accessLog.Status
|
||||
fields["createdAt"] = accessLog.Timestamp
|
||||
fields["requestId"] = accessLog.RequestId + strconv.FormatInt(time.Now().UnixNano(), 10) + configs.PaddingId
|
||||
fields["firewallPolicyId"] = accessLog.FirewallPolicyId
|
||||
fields["firewallRuleGroupId"] = accessLog.FirewallRuleGroupId
|
||||
fields["firewallRuleSetId"] = accessLog.FirewallRuleSetId
|
||||
fields["firewallRuleId"] = accessLog.FirewallRuleId
|
||||
|
||||
content, err := json.Marshal(accessLog)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fields["content"] = content
|
||||
|
||||
_, err = dao.Query(tx).
|
||||
Table(table).
|
||||
Sets(fields).
|
||||
Insert()
|
||||
if err != nil {
|
||||
// 是否为 Error 1146: Table 'xxx.xxx' doesn't exist 如果是,则创建表之后重试
|
||||
if strings.Contains(err.Error(), "1146") {
|
||||
table, err = findAccessLogTable(dao.Instance, day, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = dao.Query(tx).
|
||||
Table(table).
|
||||
Sets(fields).
|
||||
Insert()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if accessLog.FirewallPolicyId == 0 { // 如果是WAF记录,则采取采样率
|
||||
// 采样率
|
||||
if accessLogQueuePercent <= 0 {
|
||||
return nil
|
||||
}
|
||||
if accessLogQueuePercent < 100 && rands.Int(1, 100) > accessLogQueuePercent {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case queue <- accessLog:
|
||||
default:
|
||||
// 超出的丢弃
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 读取往前的 单页访问日志
|
||||
func (this *HTTPAccessLogDAO) ListAccessLogs(tx *dbs.Tx, lastRequestId string, size int64, day string, serverId int64, reverse bool, hasError bool, firewallPolicyId int64, firewallRuleGroupId int64, firewallRuleSetId int64, hasFirewallPolicy bool, userId int64) (result []*HTTPAccessLog, nextLastRequestId string, hasMore bool, err error) {
|
||||
// DumpAccessLogsFromQueue 从队列导入访问日志
|
||||
func (this *HTTPAccessLogDAO) DumpAccessLogsFromQueue(tx *dbs.Tx, size int) error {
|
||||
dao := randomHTTPAccessLogDAO()
|
||||
if dao == nil {
|
||||
dao = &HTTPAccessLogDAOWrapper{
|
||||
DAO: SharedHTTPAccessLogDAO,
|
||||
NodeId: 0,
|
||||
}
|
||||
}
|
||||
|
||||
if size <= 0 {
|
||||
size = 1_000_000
|
||||
}
|
||||
|
||||
// 复制变量,防止中途改变
|
||||
var oldQueue = oldAccessLogQueue
|
||||
var newQueue = accessLogQueue
|
||||
|
||||
Loop:
|
||||
for i := 0; i < size; i++ {
|
||||
// old
|
||||
select {
|
||||
case accessLog := <-oldQueue:
|
||||
err := this.CreateHTTPAccessLog(tx, dao.DAO, accessLog)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue Loop
|
||||
default:
|
||||
|
||||
}
|
||||
|
||||
// new
|
||||
select {
|
||||
case accessLog := <-newQueue:
|
||||
err := this.CreateHTTPAccessLog(tx, dao.DAO, accessLog)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue Loop
|
||||
default:
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateHTTPAccessLog 写入单条访问日志
|
||||
func (this *HTTPAccessLogDAO) CreateHTTPAccessLog(tx *dbs.Tx, dao *HTTPAccessLogDAO, accessLog *pb.HTTPAccessLog) error {
|
||||
day := timeutil.Format("Ymd", time.Unix(accessLog.Timestamp, 0))
|
||||
tableDef, err := findHTTPAccessLogTable(dao.Instance, day, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{}
|
||||
fields["serverId"] = accessLog.ServerId
|
||||
fields["nodeId"] = accessLog.NodeId
|
||||
fields["status"] = accessLog.Status
|
||||
fields["createdAt"] = accessLog.Timestamp
|
||||
fields["requestId"] = accessLog.RequestId
|
||||
fields["firewallPolicyId"] = accessLog.FirewallPolicyId
|
||||
fields["firewallRuleGroupId"] = accessLog.FirewallRuleGroupId
|
||||
fields["firewallRuleSetId"] = accessLog.FirewallRuleSetId
|
||||
fields["firewallRuleId"] = accessLog.FirewallRuleId
|
||||
|
||||
if len(accessLog.RequestBody) > 0 {
|
||||
fields["requestBody"] = accessLog.RequestBody
|
||||
accessLog.RequestBody = nil
|
||||
}
|
||||
|
||||
if tableDef.HasRemoteAddr {
|
||||
fields["remoteAddr"] = accessLog.RemoteAddr
|
||||
}
|
||||
if tableDef.HasDomain {
|
||||
fields["domain"] = accessLog.Host
|
||||
}
|
||||
|
||||
content, err := json.Marshal(accessLog)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fields["content"] = content
|
||||
|
||||
_, err = dao.Query(tx).
|
||||
Table(tableDef.Name).
|
||||
Sets(fields).
|
||||
Insert()
|
||||
if err != nil {
|
||||
// 是否为 Error 1146: Table 'xxx.xxx' doesn't exist 如果是,则创建表之后重试
|
||||
if strings.Contains(err.Error(), "1146") {
|
||||
tableDef, err = findHTTPAccessLogTable(dao.Instance, day, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = dao.Query(tx).
|
||||
Table(tableDef.Name).
|
||||
Sets(fields).
|
||||
Insert()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
remotelogs.Error("HTTP_ACCESS_LOG", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListAccessLogs 读取往前的 单页访问日志
|
||||
func (this *HTTPAccessLogDAO) ListAccessLogs(tx *dbs.Tx, lastRequestId string,
|
||||
size int64,
|
||||
day string,
|
||||
clusterId int64,
|
||||
nodeId int64,
|
||||
serverId int64,
|
||||
reverse bool,
|
||||
hasError bool,
|
||||
firewallPolicyId int64,
|
||||
firewallRuleGroupId int64,
|
||||
firewallRuleSetId int64,
|
||||
hasFirewallPolicy bool,
|
||||
userId int64,
|
||||
keyword string,
|
||||
ip string,
|
||||
domain string) (result []*HTTPAccessLog, nextLastRequestId string, hasMore bool, err error) {
|
||||
if len(day) != 8 {
|
||||
return
|
||||
}
|
||||
@@ -126,18 +255,18 @@ func (this *HTTPAccessLogDAO) ListAccessLogs(tx *dbs.Tx, lastRequestId string, s
|
||||
size = 1000
|
||||
}
|
||||
|
||||
result, nextLastRequestId, err = this.listAccessLogs(tx, lastRequestId, size, day, serverId, reverse, hasError, firewallPolicyId, firewallRuleGroupId, firewallRuleSetId, hasFirewallPolicy, userId)
|
||||
result, nextLastRequestId, err = this.listAccessLogs(tx, lastRequestId, size, day, clusterId, nodeId, serverId, reverse, hasError, firewallPolicyId, firewallRuleGroupId, firewallRuleSetId, hasFirewallPolicy, userId, keyword, ip, domain)
|
||||
if err != nil || int64(len(result)) < size {
|
||||
return
|
||||
}
|
||||
|
||||
moreResult, _, _ := this.listAccessLogs(tx, nextLastRequestId, 1, day, serverId, reverse, hasError, firewallPolicyId, firewallRuleGroupId, firewallRuleSetId, hasFirewallPolicy, userId)
|
||||
moreResult, _, _ := this.listAccessLogs(tx, nextLastRequestId, 1, day, clusterId, nodeId, serverId, reverse, hasError, firewallPolicyId, firewallRuleGroupId, firewallRuleSetId, hasFirewallPolicy, userId, keyword, ip, domain)
|
||||
hasMore = len(moreResult) > 0
|
||||
return
|
||||
}
|
||||
|
||||
// 读取往前的单页访问日志
|
||||
func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx, lastRequestId string, size int64, day string, serverId int64, reverse bool, hasError bool, firewallPolicyId int64, firewallRuleGroupId int64, firewallRuleSetId int64, hasFirewallPolicy bool, userId int64) (result []*HTTPAccessLog, nextLastRequestId string, err error) {
|
||||
func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx, lastRequestId string, size int64, day string, clusterId int64, nodeId int64, serverId int64, reverse bool, hasError bool, firewallPolicyId int64, firewallRuleGroupId int64, firewallRuleSetId int64, hasFirewallPolicy bool, userId int64, keyword string, ip string, domain string) (result []*HTTPAccessLog, nextLastRequestId string, err error) {
|
||||
if size <= 0 {
|
||||
return nil, lastRequestId, nil
|
||||
}
|
||||
@@ -155,7 +284,7 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx, lastRequestId string, s
|
||||
|
||||
accessLogLocker.RLock()
|
||||
daoList := []*HTTPAccessLogDAOWrapper{}
|
||||
for _, daoWrapper := range accessLogDAOMapping {
|
||||
for _, daoWrapper := range httpAccessLogDAOMapping {
|
||||
daoList = append(daoList, daoWrapper)
|
||||
}
|
||||
accessLogLocker.RUnlock()
|
||||
@@ -178,19 +307,43 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx, lastRequestId string, s
|
||||
|
||||
dao := daoWrapper.DAO
|
||||
|
||||
tableName, exists, err := findAccessLogTableName(dao.Instance, day)
|
||||
if !exists {
|
||||
// 表格不存在则跳过
|
||||
return
|
||||
}
|
||||
tableName, hasRemoteAddrField, hasDomainField, exists, err := findHTTPAccessLogTableName(dao.Instance, day)
|
||||
if err != nil {
|
||||
logs.Println("[DB_NODE]" + err.Error())
|
||||
return
|
||||
}
|
||||
if !exists {
|
||||
// 表格不存在则跳过
|
||||
return
|
||||
}
|
||||
|
||||
query := dao.Query(tx)
|
||||
|
||||
// 条件
|
||||
if nodeId > 0 {
|
||||
query.Attr("nodeId", nodeId)
|
||||
} else if clusterId > 0 {
|
||||
nodeIds, err := SharedNodeDAO.FindAllEnabledNodeIdsWithClusterId(tx, clusterId)
|
||||
if err != nil {
|
||||
remotelogs.Error("DBNODE", err.Error())
|
||||
return
|
||||
}
|
||||
if len(nodeIds) > 0 {
|
||||
sort.Slice(nodeIds, func(i, j int) bool {
|
||||
return nodeIds[i] < nodeIds[j]
|
||||
})
|
||||
var nodeIdStrings = []string{}
|
||||
for _, subNodeId := range nodeIds {
|
||||
nodeIdStrings = append(nodeIdStrings, types.String(subNodeId))
|
||||
}
|
||||
|
||||
query.Where("nodeId IN (" + strings.Join(nodeIdStrings, ",") + ")")
|
||||
query.Reuse(false)
|
||||
} else {
|
||||
// 如果没有节点,则直接返回空
|
||||
return
|
||||
}
|
||||
}
|
||||
if serverId > 0 {
|
||||
query.Attr("serverId", serverId)
|
||||
} else if userId > 0 && len(serverIds) > 0 {
|
||||
@@ -211,6 +364,111 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx, lastRequestId string, s
|
||||
}
|
||||
if hasFirewallPolicy {
|
||||
query.Where("firewallPolicyId>0")
|
||||
query.UseIndex("firewallPolicyId")
|
||||
}
|
||||
|
||||
// keyword
|
||||
if len(ip) > 0 {
|
||||
// TODO 支持IP范围
|
||||
if hasRemoteAddrField {
|
||||
// IP格式
|
||||
if strings.Contains(ip, ",") || strings.Contains(ip, "-") {
|
||||
rangeConfig, err := shared.ParseIPRange(ip)
|
||||
if err == nil {
|
||||
if len(rangeConfig.IPFrom) > 0 && len(rangeConfig.IPTo) > 0 {
|
||||
query.Between("INET_ATON(remoteAddr)", utils.IP2Long(rangeConfig.IPFrom), utils.IP2Long(rangeConfig.IPTo))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
query.Attr("remoteAddr", ip)
|
||||
query.UseIndex("remoteAddr")
|
||||
}
|
||||
} else {
|
||||
query.Where("JSON_EXTRACT(content, '$.remoteAddr')=:ip1").
|
||||
Param("ip1", ip)
|
||||
}
|
||||
}
|
||||
if len(domain) > 0 {
|
||||
if hasDomainField {
|
||||
if strings.Contains(domain, "*") {
|
||||
domain = strings.ReplaceAll(domain, "*", "%")
|
||||
domain = regexp.MustCompile(`[^a-zA-Z0-9-.%]`).ReplaceAllString(domain, "")
|
||||
query.Where("domain LIKE :host2").
|
||||
Param("host2", domain)
|
||||
} else {
|
||||
query.Attr("domain", domain)
|
||||
query.UseIndex("domain")
|
||||
}
|
||||
} else {
|
||||
query.Where("JSON_EXTRACT(content, '$.host')=:host1").
|
||||
Param("host1", domain)
|
||||
}
|
||||
}
|
||||
if len(keyword) > 0 {
|
||||
// remoteAddr
|
||||
if hasRemoteAddrField && net.ParseIP(keyword) != nil {
|
||||
query.Attr("remoteAddr", keyword)
|
||||
} else if hasRemoteAddrField && regexp.MustCompile(`^ip:.+`).MatchString(keyword) {
|
||||
keyword = keyword[3:]
|
||||
pieces := strings.SplitN(keyword, ",", 2)
|
||||
if len(pieces) == 1 || len(pieces[1]) == 0 {
|
||||
query.Attr("remoteAddr", pieces[0])
|
||||
} else {
|
||||
query.Between("INET_ATON(remoteAddr)", utils.IP2Long(pieces[0]), utils.IP2Long(pieces[1]))
|
||||
}
|
||||
} else {
|
||||
if regexp.MustCompile(`^ip:.+`).MatchString(keyword) {
|
||||
keyword = keyword[3:]
|
||||
}
|
||||
|
||||
useOriginKeyword := false
|
||||
|
||||
where := "JSON_EXTRACT(content, '$.remoteAddr') LIKE :keyword OR JSON_EXTRACT(content, '$.requestURI') LIKE :keyword OR JSON_EXTRACT(content, '$.host') LIKE :keyword OR JSON_EXTRACT(content, '$.userAgent') LIKE :keyword"
|
||||
|
||||
jsonKeyword, err := json.Marshal(keyword)
|
||||
if err == nil {
|
||||
where += " OR JSON_CONTAINS(content, :jsonKeyword, '$.tags')"
|
||||
query.Param("jsonKeyword", jsonKeyword)
|
||||
}
|
||||
|
||||
// 请求方法
|
||||
if keyword == http.MethodGet ||
|
||||
keyword == http.MethodPost ||
|
||||
keyword == http.MethodHead ||
|
||||
keyword == http.MethodConnect ||
|
||||
keyword == http.MethodPut ||
|
||||
keyword == http.MethodTrace ||
|
||||
keyword == http.MethodOptions ||
|
||||
keyword == http.MethodDelete ||
|
||||
keyword == http.MethodPatch {
|
||||
where += " OR JSON_EXTRACT(content, '$.requestMethod')=:originKeyword"
|
||||
useOriginKeyword = true
|
||||
}
|
||||
|
||||
// 响应状态码
|
||||
if regexp.MustCompile(`^\d{3}$`).MatchString(keyword) {
|
||||
where += " OR JSON_EXTRACT(content, '$.status')=:intKeyword"
|
||||
query.Param("intKeyword", types.Int(keyword))
|
||||
}
|
||||
|
||||
if regexp.MustCompile(`^\d{3}-\d{3}$`).MatchString(keyword) {
|
||||
pieces := strings.Split(keyword, "-")
|
||||
where += " OR JSON_EXTRACT(content, '$.status') BETWEEN :intKeyword1 AND :intKeyword2"
|
||||
query.Param("intKeyword1", types.Int(pieces[0]))
|
||||
query.Param("intKeyword2", types.Int(pieces[1]))
|
||||
}
|
||||
|
||||
if regexp.MustCompile(`^\d{20,}\s*\.?$`).MatchString(keyword) {
|
||||
where += " OR requestId=:requestId"
|
||||
query.Param("requestId", strings.TrimRight(keyword, ". "))
|
||||
}
|
||||
|
||||
query.Where("("+where+")").
|
||||
Param("keyword", "%"+keyword+"%")
|
||||
if useOriginKeyword {
|
||||
query.Param("originKeyword", keyword)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// offset
|
||||
@@ -278,15 +536,15 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx, lastRequestId string, s
|
||||
}
|
||||
}
|
||||
|
||||
// 根据请求ID获取访问日志
|
||||
// FindAccessLogWithRequestId 根据请求ID获取访问日志
|
||||
func (this *HTTPAccessLogDAO) FindAccessLogWithRequestId(tx *dbs.Tx, requestId string) (*HTTPAccessLog, error) {
|
||||
if !regexp.MustCompile(`^\d{30,}`).MatchString(requestId) {
|
||||
if !regexp.MustCompile(`^\d{11,}`).MatchString(requestId) {
|
||||
return nil, errors.New("invalid requestId")
|
||||
}
|
||||
|
||||
accessLogLocker.RLock()
|
||||
daoList := []*HTTPAccessLogDAOWrapper{}
|
||||
for _, daoWrapper := range accessLogDAOMapping {
|
||||
for _, daoWrapper := range httpAccessLogDAOMapping {
|
||||
daoList = append(daoList, daoWrapper)
|
||||
}
|
||||
accessLogLocker.RUnlock()
|
||||
@@ -309,7 +567,7 @@ func (this *HTTPAccessLogDAO) FindAccessLogWithRequestId(tx *dbs.Tx, requestId s
|
||||
|
||||
dao := daoWrapper.DAO
|
||||
|
||||
tableName, exists, err := findAccessLogTableName(dao.Instance, day)
|
||||
tableName, _, _, exists, err := findHTTPAccessLogTableName(dao.Instance, day)
|
||||
if err != nil {
|
||||
logs.Println("[DB_NODE]" + err.Error())
|
||||
return
|
||||
@@ -334,3 +592,42 @@ func (this *HTTPAccessLogDAO) FindAccessLogWithRequestId(tx *dbs.Tx, requestId s
|
||||
wg.Wait()
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// SetupQueue 建立队列
|
||||
func (this *HTTPAccessLogDAO) SetupQueue() {
|
||||
configJSON, err := SharedSysSettingDAO.ReadSetting(nil, systemconfigs.SettingCodeAccessLogQueue)
|
||||
if err != nil {
|
||||
remotelogs.Error("HTTP_ACCESS_LOG_QUEUE", "read settings failed: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if len(configJSON) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if bytes.Compare(accessLogConfigJSON, configJSON) == 0 {
|
||||
return
|
||||
}
|
||||
accessLogConfigJSON = configJSON
|
||||
|
||||
var config = &serverconfigs.AccessLogQueueConfig{}
|
||||
err = json.Unmarshal(configJSON, config)
|
||||
if err != nil {
|
||||
remotelogs.Error("HTTP_ACCESS_LOG_QUEUE", "decode settings failed: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
accessLogQueuePercent = config.Percent
|
||||
accessLogCountPerSecond = config.CountPerSecond
|
||||
if config.MaxLength <= 0 {
|
||||
config.MaxLength = 100_000
|
||||
}
|
||||
|
||||
if accessLogQueueMaxLength != config.MaxLength {
|
||||
accessLogQueueMaxLength = config.MaxLength
|
||||
oldAccessLogQueue = accessLogQueue
|
||||
accessLogQueue = make(chan *pb.HTTPAccessLog, config.MaxLength)
|
||||
}
|
||||
|
||||
remotelogs.Println("HTTP_ACCESS_LOG_QUEUE", "change queue max length: "+types.String(config.MaxLength)+", percent: "+types.String(config.Percent)+", countPerSecond: "+types.String(config.CountPerSecond))
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCreateHTTPAccessLogs(t *testing.T) {
|
||||
func TestCreateHTTPAccessLog(t *testing.T) {
|
||||
dbs.NotifyReady()
|
||||
|
||||
var tx *dbs.Tx
|
||||
|
||||
err := NewDBNodeInitializer().loop()
|
||||
@@ -24,11 +26,21 @@ func TestCreateHTTPAccessLogs(t *testing.T) {
|
||||
Status: 200,
|
||||
Timestamp: time.Now().Unix(),
|
||||
}
|
||||
dao := randomAccessLogDAO()
|
||||
dao := randomHTTPAccessLogDAO()
|
||||
t.Log("dao:", dao)
|
||||
err = SharedHTTPAccessLogDAO.CreateHTTPAccessLogsWithDAO(tx, dao, []*pb.HTTPAccessLog{accessLog})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
// 先初始化
|
||||
_ = SharedHTTPAccessLogDAO.CreateHTTPAccessLog(tx, dao.DAO, accessLog)
|
||||
|
||||
var before = time.Now()
|
||||
defer func() {
|
||||
t.Log(time.Since(before).Seconds()*1000, "ms")
|
||||
}()
|
||||
for i := 0; i < 1000; i++ {
|
||||
err = SharedHTTPAccessLogDAO.CreateHTTPAccessLog(tx, dao.DAO, accessLog)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
t.Log("ok")
|
||||
}
|
||||
@@ -41,7 +53,7 @@ func TestHTTPAccessLogDAO_ListAccessLogs(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
accessLogs, requestId, hasMore, err := SharedHTTPAccessLogDAO.ListAccessLogs(tx, "", 10, timeutil.Format("Ymd"), 0, false, false, 0, 0, 0, false, 0)
|
||||
accessLogs, requestId, hasMore, err := SharedHTTPAccessLogDAO.ListAccessLogs(tx, "", 10, timeutil.Format("Ymd"), 0, 0, 0, false, false, 0, 0, 0, false, 0, "", "", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -68,7 +80,7 @@ func TestHTTPAccessLogDAO_ListAccessLogs_Page(t *testing.T) {
|
||||
times := 0 // 防止循环次数太多
|
||||
for {
|
||||
before := time.Now()
|
||||
accessLogs, requestId, hasMore, err := SharedHTTPAccessLogDAO.ListAccessLogs(tx, lastRequestId, 2, timeutil.Format("Ymd"), 0, false, false, 0, 0, 0, false, 0)
|
||||
accessLogs, requestId, hasMore, err := SharedHTTPAccessLogDAO.ListAccessLogs(tx, lastRequestId, 2, timeutil.Format("Ymd"), 0, 0, 0, false, false, 0, 0, 0, false, 0, "", "", "")
|
||||
cost := time.Since(before).Seconds()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -99,7 +111,7 @@ func TestHTTPAccessLogDAO_ListAccessLogs_Reverse(t *testing.T) {
|
||||
}
|
||||
|
||||
before := time.Now()
|
||||
accessLogs, requestId, hasMore, err := SharedHTTPAccessLogDAO.ListAccessLogs(tx, "16023261176446590001000000000000003500000004", 2, timeutil.Format("Ymd"), 0, true, false, 0, 0, 0, false, 0)
|
||||
accessLogs, requestId, hasMore, err := SharedHTTPAccessLogDAO.ListAccessLogs(tx, "16023261176446590001000000000000003500000004", 2, timeutil.Format("Ymd"), 0, 0, 0, true, false, 0, 0, 0, false, 0, "", "", "")
|
||||
cost := time.Since(before).Seconds()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -124,7 +136,7 @@ func TestHTTPAccessLogDAO_ListAccessLogs_Page_NotExists(t *testing.T) {
|
||||
times := 0 // 防止循环次数太多
|
||||
for {
|
||||
before := time.Now()
|
||||
accessLogs, requestId, hasMore, err := SharedHTTPAccessLogDAO.ListAccessLogs(tx, lastRequestId, 2, timeutil.Format("Ymd", time.Now().AddDate(0, 0, 1)), 0, false, false, 0, 0, 0, false, 0)
|
||||
accessLogs, requestId, hasMore, err := SharedHTTPAccessLogDAO.ListAccessLogs(tx, lastRequestId, 2, timeutil.Format("Ymd", time.Now().AddDate(0, 0, 1)), 0, 0, 0, false, false, 0, 0, 0, false, 0, "", "", "")
|
||||
cost := time.Since(before).Seconds()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package models
|
||||
|
||||
//
|
||||
// HTTPAccessLog 访问日志
|
||||
type HTTPAccessLog struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
ServerId uint32 `field:"serverId"` // 服务ID
|
||||
@@ -13,6 +13,10 @@ type HTTPAccessLog struct {
|
||||
FirewallRuleGroupId uint32 `field:"firewallRuleGroupId"` // WAF分组ID
|
||||
FirewallRuleSetId uint32 `field:"firewallRuleSetId"` // WAF集ID
|
||||
FirewallRuleId uint32 `field:"firewallRuleId"` // WAF规则ID
|
||||
RemoteAddr string `field:"remoteAddr"` // IP地址
|
||||
Domain string `field:"domain"` // 域名
|
||||
RequestBody string `field:"requestBody"` // 请求内容
|
||||
ResponseBody string `field:"responseBody"` // 响应内容
|
||||
}
|
||||
|
||||
type HTTPAccessLogOperator struct {
|
||||
@@ -27,6 +31,10 @@ type HTTPAccessLogOperator struct {
|
||||
FirewallRuleGroupId interface{} // WAF分组ID
|
||||
FirewallRuleSetId interface{} // WAF集ID
|
||||
FirewallRuleId interface{} // WAF规则ID
|
||||
RemoteAddr interface{} // IP地址
|
||||
Domain interface{} // 域名
|
||||
RequestBody interface{} // 请求内容
|
||||
ResponseBody interface{} // 响应内容
|
||||
}
|
||||
|
||||
func NewHTTPAccessLogOperator() *HTTPAccessLogOperator {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
// 转换成PB对象
|
||||
// ToPB 转换成PB对象
|
||||
func (this *HTTPAccessLog) ToPB() (*pb.HTTPAccessLog, error) {
|
||||
p := &pb.HTTPAccessLog{}
|
||||
err := json.Unmarshal([]byte(this.Content), p)
|
||||
@@ -13,5 +13,6 @@ func (this *HTTPAccessLog) ToPB() (*pb.HTTPAccessLog, error) {
|
||||
return nil, err
|
||||
}
|
||||
p.RequestId = this.RequestId
|
||||
p.RequestBody = []byte(this.RequestBody)
|
||||
return p, nil
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -35,12 +36,12 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
// 初始化
|
||||
// Init 初始化
|
||||
func (this *HTTPAccessLogPolicyDAO) Init() {
|
||||
_ = this.DAOObject.Init()
|
||||
}
|
||||
|
||||
// 启用条目
|
||||
// EnableHTTPAccessLogPolicy 启用条目
|
||||
func (this *HTTPAccessLogPolicyDAO) EnableHTTPAccessLogPolicy(tx *dbs.Tx, id int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -49,7 +50,7 @@ func (this *HTTPAccessLogPolicyDAO) EnableHTTPAccessLogPolicy(tx *dbs.Tx, id int
|
||||
return err
|
||||
}
|
||||
|
||||
// 禁用条目
|
||||
// DisableHTTPAccessLogPolicy 禁用条目
|
||||
func (this *HTTPAccessLogPolicyDAO) DisableHTTPAccessLogPolicy(tx *dbs.Tx, id int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -58,7 +59,7 @@ func (this *HTTPAccessLogPolicyDAO) DisableHTTPAccessLogPolicy(tx *dbs.Tx, id in
|
||||
return err
|
||||
}
|
||||
|
||||
// 查找启用中的条目
|
||||
// FindEnabledHTTPAccessLogPolicy 查找启用中的条目
|
||||
func (this *HTTPAccessLogPolicyDAO) FindEnabledHTTPAccessLogPolicy(tx *dbs.Tx, id int64) (*HTTPAccessLogPolicy, error) {
|
||||
result, err := this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -70,7 +71,7 @@ func (this *HTTPAccessLogPolicyDAO) FindEnabledHTTPAccessLogPolicy(tx *dbs.Tx, i
|
||||
return result.(*HTTPAccessLogPolicy), err
|
||||
}
|
||||
|
||||
// 根据主键查找名称
|
||||
// FindHTTPAccessLogPolicyName 根据主键查找名称
|
||||
func (this *HTTPAccessLogPolicyDAO) FindHTTPAccessLogPolicyName(tx *dbs.Tx, id int64) (string, error) {
|
||||
return this.Query(tx).
|
||||
Pk(id).
|
||||
@@ -78,51 +79,116 @@ func (this *HTTPAccessLogPolicyDAO) FindHTTPAccessLogPolicyName(tx *dbs.Tx, id i
|
||||
FindStringCol("")
|
||||
}
|
||||
|
||||
// 查找所有可用策略信息
|
||||
func (this *HTTPAccessLogPolicyDAO) FindAllEnabledAccessLogPolicies(tx *dbs.Tx) (result []*HTTPAccessLogPolicy, err error) {
|
||||
// CountAllEnabledPolicies 计算策略数量
|
||||
func (this *HTTPAccessLogPolicyDAO) CountAllEnabledPolicies(tx *dbs.Tx) (int64, error) {
|
||||
return this.Query(tx).
|
||||
State(HTTPAccessLogPolicyStateEnabled).
|
||||
Count()
|
||||
}
|
||||
|
||||
// ListEnabledPolicies 查找所有可用策略信息
|
||||
func (this *HTTPAccessLogPolicyDAO) ListEnabledPolicies(tx *dbs.Tx, offset int64, size int64) (result []*HTTPAccessLogPolicy, err error) {
|
||||
_, err = this.Query(tx).
|
||||
State(HTTPAccessLogPolicyStateEnabled).
|
||||
DescPk().
|
||||
Offset(offset).
|
||||
Limit(size).
|
||||
Slice(&result).
|
||||
FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// 组合配置
|
||||
func (this *HTTPAccessLogPolicyDAO) ComposeAccessLogPolicyConfig(tx *dbs.Tx, policyId int64) (*serverconfigs.HTTPAccessLogStoragePolicy, error) {
|
||||
policy, err := this.FindEnabledHTTPAccessLogPolicy(tx, policyId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if policy == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
config := &serverconfigs.HTTPAccessLogStoragePolicy{}
|
||||
config.Id = int64(policy.Id)
|
||||
config.IsOn = policy.IsOn == 1
|
||||
config.Name = policy.Name
|
||||
config.Type = policy.Type
|
||||
|
||||
// 选项
|
||||
if IsNotNull(policy.Options) {
|
||||
m := map[string]interface{}{}
|
||||
err = json.Unmarshal([]byte(policy.Options), &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Options = m
|
||||
}
|
||||
|
||||
// 条件
|
||||
if IsNotNull(policy.Conds) {
|
||||
condsConfig := &shared.HTTPRequestCondsConfig{}
|
||||
err = json.Unmarshal([]byte(policy.Conds), condsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Conds = condsConfig
|
||||
}
|
||||
|
||||
return config, nil
|
||||
// FindAllEnabledAndOnPolicies 获取所有的策略信息
|
||||
func (this *HTTPAccessLogPolicyDAO) FindAllEnabledAndOnPolicies(tx *dbs.Tx) (result []*HTTPAccessLogPolicy, err error) {
|
||||
_, err = this.Query(tx).
|
||||
State(HTTPAccessLogPolicyStateEnabled).
|
||||
Attr("isOn", true).
|
||||
Slice(&result).
|
||||
FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// CreatePolicy 创建策略
|
||||
func (this *HTTPAccessLogPolicyDAO) CreatePolicy(tx *dbs.Tx, name string, policyType string, optionsJSON []byte, condsJSON []byte, isPublic bool) (policyId int64, err error) {
|
||||
var op = NewHTTPAccessLogPolicyOperator()
|
||||
op.Name = name
|
||||
op.Type = policyType
|
||||
if len(optionsJSON) > 0 {
|
||||
op.Options = optionsJSON
|
||||
}
|
||||
if len(condsJSON) > 0 {
|
||||
op.Conds = condsJSON
|
||||
}
|
||||
op.IsPublic = isPublic
|
||||
op.IsOn = true
|
||||
op.State = HTTPAccessLogPolicyStateEnabled
|
||||
return this.SaveInt64(tx, op)
|
||||
}
|
||||
|
||||
// UpdatePolicy 修改策略
|
||||
func (this *HTTPAccessLogPolicyDAO) UpdatePolicy(tx *dbs.Tx, policyId int64, name string, optionsJSON []byte, condsJSON []byte, isPublic bool, isOn bool) error {
|
||||
if policyId <= 0 {
|
||||
return errors.New("invalid policyId")
|
||||
}
|
||||
|
||||
oldOne, err := this.Query(tx).
|
||||
Pk(policyId).
|
||||
Find()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if oldOne == nil {
|
||||
return nil
|
||||
}
|
||||
var oldPolicy = oldOne.(*HTTPAccessLogPolicy)
|
||||
|
||||
var op = NewHTTPAccessLogPolicyOperator()
|
||||
op.Id = policyId
|
||||
op.Name = name
|
||||
if len(optionsJSON) > 0 {
|
||||
op.Options = optionsJSON
|
||||
} else {
|
||||
op.Options = "{}"
|
||||
}
|
||||
if len(condsJSON) > 0 {
|
||||
op.Conds = condsJSON
|
||||
} else {
|
||||
op.Conds = "{}"
|
||||
}
|
||||
|
||||
// 版本号
|
||||
if len(oldPolicy.Options) == 0 || len(optionsJSON) == 0 {
|
||||
op.Version = dbs.SQL("version+1")
|
||||
} else {
|
||||
var m1 = maps.Map{}
|
||||
_ = json.Unmarshal([]byte(oldPolicy.Options), &m1)
|
||||
|
||||
var m2 = maps.Map{}
|
||||
_ = json.Unmarshal(optionsJSON, &m2)
|
||||
|
||||
if bytes.Compare(m1.AsJSON(), m2.AsJSON()) != 0 {
|
||||
op.Version = dbs.SQL("version+1")
|
||||
}
|
||||
}
|
||||
|
||||
op.IsPublic = isPublic
|
||||
op.IsOn = isOn
|
||||
return this.Save(tx, op)
|
||||
}
|
||||
|
||||
// CancelAllPublicPolicies 取消别的公用的策略
|
||||
func (this *HTTPAccessLogPolicyDAO) CancelAllPublicPolicies(tx *dbs.Tx) error {
|
||||
return this.Query(tx).
|
||||
State(HTTPAccessLogPolicyStateEnabled).
|
||||
Set("isPublic", 0).
|
||||
UpdateQuickly()
|
||||
}
|
||||
|
||||
// FindCurrentPublicPolicyId 取得当前的公用策略
|
||||
func (this *HTTPAccessLogPolicyDAO) FindCurrentPublicPolicyId(tx *dbs.Tx) (int64, error) {
|
||||
return this.Query(tx).
|
||||
State(HTTPAccessLogPolicyStateEnabled).
|
||||
Attr("isPublic", 1).
|
||||
ResultPk().
|
||||
FindInt64Col(0)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package models
|
||||
|
||||
// 访问日志策略
|
||||
// HTTPAccessLogPolicy 访问日志策略
|
||||
type HTTPAccessLogPolicy struct {
|
||||
Id uint32 `field:"id"` // ID
|
||||
TemplateId uint32 `field:"templateId"` // 模版ID
|
||||
@@ -13,6 +13,8 @@ type HTTPAccessLogPolicy struct {
|
||||
Type string `field:"type"` // 存储类型
|
||||
Options string `field:"options"` // 存储选项
|
||||
Conds string `field:"conds"` // 请求条件
|
||||
IsPublic uint8 `field:"isPublic"` // 是否为公用
|
||||
Version uint32 `field:"version"` // 版本号
|
||||
}
|
||||
|
||||
type HTTPAccessLogPolicyOperator struct {
|
||||
@@ -27,6 +29,8 @@ type HTTPAccessLogPolicyOperator struct {
|
||||
Type interface{} // 存储类型
|
||||
Options interface{} // 存储选项
|
||||
Conds interface{} // 请求条件
|
||||
IsPublic interface{} // 是否为公用
|
||||
Version interface{} // 版本号
|
||||
}
|
||||
|
||||
func NewHTTPAccessLogPolicyOperator() *HTTPAccessLogPolicyOperator {
|
||||
|
||||
150
internal/db/models/http_auth_policy_dao.go
Normal file
150
internal/db/models/http_auth_policy_dao.go
Normal file
@@ -0,0 +1,150 @@
|
||||
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"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
)
|
||||
|
||||
const (
|
||||
HTTPAuthPolicyStateEnabled = 1 // 已启用
|
||||
HTTPAuthPolicyStateDisabled = 0 // 已禁用
|
||||
)
|
||||
|
||||
type HTTPAuthPolicyDAO dbs.DAO
|
||||
|
||||
func NewHTTPAuthPolicyDAO() *HTTPAuthPolicyDAO {
|
||||
return dbs.NewDAO(&HTTPAuthPolicyDAO{
|
||||
DAOObject: dbs.DAOObject{
|
||||
DB: Tea.Env,
|
||||
Table: "edgeHTTPAuthPolicies",
|
||||
Model: new(HTTPAuthPolicy),
|
||||
PkName: "id",
|
||||
},
|
||||
}).(*HTTPAuthPolicyDAO)
|
||||
}
|
||||
|
||||
var SharedHTTPAuthPolicyDAO *HTTPAuthPolicyDAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedHTTPAuthPolicyDAO = NewHTTPAuthPolicyDAO()
|
||||
})
|
||||
}
|
||||
|
||||
// EnableHTTPAuthPolicy 启用条目
|
||||
func (this *HTTPAuthPolicyDAO) EnableHTTPAuthPolicy(tx *dbs.Tx, id int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
Set("state", HTTPAuthPolicyStateEnabled).
|
||||
Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// DisableHTTPAuthPolicy 禁用条目
|
||||
func (this *HTTPAuthPolicyDAO) DisableHTTPAuthPolicy(tx *dbs.Tx, id int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
Set("state", HTTPAuthPolicyStateDisabled).
|
||||
Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// FindEnabledHTTPAuthPolicy 查找启用中的条目
|
||||
func (this *HTTPAuthPolicyDAO) FindEnabledHTTPAuthPolicy(tx *dbs.Tx, id int64) (*HTTPAuthPolicy, error) {
|
||||
result, err := this.Query(tx).
|
||||
Pk(id).
|
||||
Attr("state", HTTPAuthPolicyStateEnabled).
|
||||
Find()
|
||||
if result == nil {
|
||||
return nil, err
|
||||
}
|
||||
return result.(*HTTPAuthPolicy), err
|
||||
}
|
||||
|
||||
// CreateHTTPAuthPolicy 创建策略
|
||||
func (this *HTTPAuthPolicyDAO) CreateHTTPAuthPolicy(tx *dbs.Tx, name string, methodType string, paramsJSON []byte) (int64, error) {
|
||||
op := NewHTTPAuthPolicyOperator()
|
||||
op.Name = name
|
||||
op.Type = methodType
|
||||
op.Params = paramsJSON
|
||||
op.IsOn = true
|
||||
op.State = HTTPAuthPolicyStateEnabled
|
||||
return this.SaveInt64(tx, op)
|
||||
}
|
||||
|
||||
// UpdateHTTPAuthPolicy 修改策略
|
||||
func (this *HTTPAuthPolicyDAO) UpdateHTTPAuthPolicy(tx *dbs.Tx, policyId int64, name string, paramsJSON []byte, isOn bool) error {
|
||||
if policyId <= 0 {
|
||||
return errors.New("invalid policyId")
|
||||
}
|
||||
op := NewHTTPAuthPolicyOperator()
|
||||
op.Id = policyId
|
||||
op.Name = name
|
||||
op.Params = paramsJSON
|
||||
op.IsOn = isOn
|
||||
err := this.Save(tx, op)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return this.NotifyUpdate(tx, policyId)
|
||||
}
|
||||
|
||||
// ComposePolicyConfig 组合配置
|
||||
func (this *HTTPAuthPolicyDAO) ComposePolicyConfig(tx *dbs.Tx, policyId int64, cacheMap *utils.CacheMap) (*serverconfigs.HTTPAuthPolicy, error) {
|
||||
if cacheMap == nil {
|
||||
cacheMap = utils.NewCacheMap()
|
||||
}
|
||||
var cacheKey = this.Table + ":config:" + types.String(policyId)
|
||||
var cache, _ = cacheMap.Get(cacheKey)
|
||||
if cache != nil {
|
||||
return cache.(*serverconfigs.HTTPAuthPolicy), nil
|
||||
}
|
||||
|
||||
policy, err := this.FindEnabledHTTPAuthPolicy(tx, policyId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if policy == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var config = &serverconfigs.HTTPAuthPolicy{
|
||||
Id: int64(policy.Id),
|
||||
Name: policy.Name,
|
||||
IsOn: policy.IsOn == 1,
|
||||
Type: policy.Type,
|
||||
}
|
||||
|
||||
var params map[string]interface{}
|
||||
if IsNotNull(policy.Params) {
|
||||
err = json.Unmarshal([]byte(policy.Params), ¶ms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Params = params
|
||||
}
|
||||
config.Params = params
|
||||
|
||||
if cacheMap != nil {
|
||||
cacheMap.Put(cacheKey, config)
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// NotifyUpdate 通知更改
|
||||
func (this *HTTPAuthPolicyDAO) NotifyUpdate(tx *dbs.Tx, policyId int64) error {
|
||||
webId, err := SharedHTTPWebDAO.FindEnabledWebIdWithHTTPAuthPolicyId(tx, policyId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if webId > 0 {
|
||||
return SharedHTTPWebDAO.NotifyUpdate(tx, webId)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
6
internal/db/models/http_auth_policy_dao_test.go
Normal file
6
internal/db/models/http_auth_policy_dao_test.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
)
|
||||
28
internal/db/models/http_auth_policy_model.go
Normal file
28
internal/db/models/http_auth_policy_model.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package models
|
||||
|
||||
// HTTPAuthPolicy HTTP认证策略
|
||||
type HTTPAuthPolicy struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
AdminId uint32 `field:"adminId"` // 管理员ID
|
||||
UserId uint32 `field:"userId"` // 用户ID
|
||||
IsOn uint8 `field:"isOn"` // 是否启用
|
||||
Name string `field:"name"` // 名称
|
||||
Type string `field:"type"` // 类型
|
||||
Params string `field:"params"` // 参数
|
||||
State uint8 `field:"state"` // 状态
|
||||
}
|
||||
|
||||
type HTTPAuthPolicyOperator struct {
|
||||
Id interface{} // ID
|
||||
AdminId interface{} // 管理员ID
|
||||
UserId interface{} // 用户ID
|
||||
IsOn interface{} // 是否启用
|
||||
Name interface{} // 名称
|
||||
Type interface{} // 类型
|
||||
Params interface{} // 参数
|
||||
State interface{} // 状态
|
||||
}
|
||||
|
||||
func NewHTTPAuthPolicyOperator() *HTTPAuthPolicyOperator {
|
||||
return &HTTPAuthPolicyOperator{}
|
||||
}
|
||||
1
internal/db/models/http_auth_policy_model_ext.go
Normal file
1
internal/db/models/http_auth_policy_model_ext.go
Normal file
@@ -0,0 +1 @@
|
||||
package models
|
||||
170
internal/db/models/http_brotli_policy_dao.go
Normal file
170
internal/db/models/http_brotli_policy_dao.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
)
|
||||
|
||||
const (
|
||||
HTTPBrotliPolicyStateEnabled = 1 // 已启用
|
||||
HTTPBrotliPolicyStateDisabled = 0 // 已禁用
|
||||
)
|
||||
|
||||
type HTTPBrotliPolicyDAO dbs.DAO
|
||||
|
||||
func NewHTTPBrotliPolicyDAO() *HTTPBrotliPolicyDAO {
|
||||
return dbs.NewDAO(&HTTPBrotliPolicyDAO{
|
||||
DAOObject: dbs.DAOObject{
|
||||
DB: Tea.Env,
|
||||
Table: "edgeHTTPBrotliPolicies",
|
||||
Model: new(HTTPBrotliPolicy),
|
||||
PkName: "id",
|
||||
},
|
||||
}).(*HTTPBrotliPolicyDAO)
|
||||
}
|
||||
|
||||
var SharedHTTPBrotliPolicyDAO *HTTPBrotliPolicyDAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedHTTPBrotliPolicyDAO = NewHTTPBrotliPolicyDAO()
|
||||
})
|
||||
}
|
||||
|
||||
// EnableHTTPBrotliPolicy 启用条目
|
||||
func (this *HTTPBrotliPolicyDAO) EnableHTTPBrotliPolicy(tx *dbs.Tx, id int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
Set("state", HTTPBrotliPolicyStateEnabled).
|
||||
Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// DisableHTTPBrotliPolicy 禁用条目
|
||||
func (this *HTTPBrotliPolicyDAO) DisableHTTPBrotliPolicy(tx *dbs.Tx, id int64) error {
|
||||
_, err := this.Query(tx).
|
||||
Pk(id).
|
||||
Set("state", HTTPBrotliPolicyStateDisabled).
|
||||
Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// FindEnabledHTTPBrotliPolicy 查找启用中的条目
|
||||
func (this *HTTPBrotliPolicyDAO) FindEnabledHTTPBrotliPolicy(tx *dbs.Tx, id int64) (*HTTPBrotliPolicy, error) {
|
||||
result, err := this.Query(tx).
|
||||
Pk(id).
|
||||
Attr("state", HTTPBrotliPolicyStateEnabled).
|
||||
Find()
|
||||
if result == nil {
|
||||
return nil, err
|
||||
}
|
||||
return result.(*HTTPBrotliPolicy), err
|
||||
}
|
||||
|
||||
// ComposeBrotliConfig 组合配置
|
||||
func (this *HTTPBrotliPolicyDAO) ComposeBrotliConfig(tx *dbs.Tx, policyId int64) (*serverconfigs.HTTPBrotliCompressionConfig, error) {
|
||||
policy, err := this.FindEnabledHTTPBrotliPolicy(tx, policyId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if policy == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
config := &serverconfigs.HTTPBrotliCompressionConfig{}
|
||||
config.Id = int64(policy.Id)
|
||||
config.IsOn = policy.IsOn == 1
|
||||
if IsNotNull(policy.MinLength) {
|
||||
minLengthConfig := &shared.SizeCapacity{}
|
||||
err = json.Unmarshal([]byte(policy.MinLength), minLengthConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.MinLength = minLengthConfig
|
||||
}
|
||||
if IsNotNull(policy.MaxLength) {
|
||||
maxLengthConfig := &shared.SizeCapacity{}
|
||||
err = json.Unmarshal([]byte(policy.MaxLength), maxLengthConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.MaxLength = maxLengthConfig
|
||||
}
|
||||
config.Level = types.Int8(policy.Level)
|
||||
|
||||
if IsNotNull(policy.Conds) {
|
||||
condsConfig := &shared.HTTPRequestCondsConfig{}
|
||||
err = json.Unmarshal([]byte(policy.Conds), condsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Conds = condsConfig
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// CreatePolicy 创建策略
|
||||
func (this *HTTPBrotliPolicyDAO) CreatePolicy(tx *dbs.Tx, level int, minLengthJSON []byte, maxLengthJSON []byte, condsJSON []byte) (int64, error) {
|
||||
op := NewHTTPBrotliPolicyOperator()
|
||||
op.State = HTTPBrotliPolicyStateEnabled
|
||||
op.IsOn = true
|
||||
op.Level = level
|
||||
if len(minLengthJSON) > 0 {
|
||||
op.MinLength = JSONBytes(minLengthJSON)
|
||||
}
|
||||
if len(maxLengthJSON) > 0 {
|
||||
op.MaxLength = JSONBytes(maxLengthJSON)
|
||||
}
|
||||
if len(condsJSON) > 0 {
|
||||
op.Conds = JSONBytes(condsJSON)
|
||||
}
|
||||
err := this.Save(tx, op)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return types.Int64(op.Id), nil
|
||||
}
|
||||
|
||||
// UpdatePolicy 修改Policy
|
||||
func (this *HTTPBrotliPolicyDAO) UpdatePolicy(tx *dbs.Tx, policyId int64, level int, minLengthJSON []byte, maxLengthJSON []byte, condsJSON []byte) error {
|
||||
if policyId <= 0 {
|
||||
return errors.New("invalid policyId")
|
||||
}
|
||||
op := NewHTTPBrotliPolicyOperator()
|
||||
op.Id = policyId
|
||||
op.Level = level
|
||||
if len(minLengthJSON) > 0 {
|
||||
op.MinLength = JSONBytes(minLengthJSON)
|
||||
}
|
||||
if len(maxLengthJSON) > 0 {
|
||||
op.MaxLength = JSONBytes(maxLengthJSON)
|
||||
}
|
||||
if len(condsJSON) > 0 {
|
||||
op.Conds = JSONBytes(condsJSON)
|
||||
}
|
||||
err := this.Save(tx, op)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return this.NotifyUpdate(tx, policyId)
|
||||
}
|
||||
|
||||
// NotifyUpdate 通知更新
|
||||
func (this *HTTPBrotliPolicyDAO) NotifyUpdate(tx *dbs.Tx, policyId int64) error {
|
||||
webId, err := SharedHTTPWebDAO.FindEnabledWebIdWithBrotliPolicyId(tx, policyId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if webId > 0 {
|
||||
return SharedHTTPWebDAO.NotifyUpdate(tx, webId)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
6
internal/db/models/http_brotli_policy_dao_test.go
Normal file
6
internal/db/models/http_brotli_policy_dao_test.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
)
|
||||
32
internal/db/models/http_brotli_policy_model.go
Normal file
32
internal/db/models/http_brotli_policy_model.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package models
|
||||
|
||||
// HTTPBrotliPolicy Gzip配置
|
||||
type HTTPBrotliPolicy struct {
|
||||
Id uint32 `field:"id"` // ID
|
||||
AdminId uint32 `field:"adminId"` // 管理员ID
|
||||
UserId uint32 `field:"userId"` // 用户ID
|
||||
IsOn uint8 `field:"isOn"` // 是否启用
|
||||
Level uint32 `field:"level"` // 压缩级别
|
||||
MinLength string `field:"minLength"` // 可压缩最小值
|
||||
MaxLength string `field:"maxLength"` // 可压缩最大值
|
||||
State uint8 `field:"state"` // 状态
|
||||
CreatedAt uint64 `field:"createdAt"` // 创建时间
|
||||
Conds string `field:"conds"` // 条件
|
||||
}
|
||||
|
||||
type HTTPBrotliPolicyOperator struct {
|
||||
Id interface{} // ID
|
||||
AdminId interface{} // 管理员ID
|
||||
UserId interface{} // 用户ID
|
||||
IsOn interface{} // 是否启用
|
||||
Level interface{} // 压缩级别
|
||||
MinLength interface{} // 可压缩最小值
|
||||
MaxLength interface{} // 可压缩最大值
|
||||
State interface{} // 状态
|
||||
CreatedAt interface{} // 创建时间
|
||||
Conds interface{} // 条件
|
||||
}
|
||||
|
||||
func NewHTTPBrotliPolicyOperator() *HTTPBrotliPolicyOperator {
|
||||
return &HTTPBrotliPolicyOperator{}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user