Compare commits
426 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbfcc8d2c0 | ||
|
|
11c2682f59 | ||
|
|
8a005850db | ||
|
|
a0acc54565 | ||
|
|
8234d0301f | ||
|
|
59de46d34f | ||
|
|
0dcdde06e0 | ||
|
|
b9b7ca4c41 | ||
|
|
5244b53270 | ||
|
|
dd460bc40d | ||
|
|
d9fb6a2c84 | ||
|
|
c05072d12e | ||
|
|
caba96bfeb | ||
|
|
a36344f3bb | ||
|
|
5db4568d7e | ||
|
|
4e219b09e0 | ||
|
|
1d7986289d | ||
|
|
710ec09b14 | ||
|
|
324d24fe5a | ||
|
|
0f060002e3 | ||
|
|
2eb19348cc | ||
|
|
a92d8887e3 | ||
|
|
f914802b06 | ||
|
|
a2f21e8d58 | ||
|
|
cf4b9a49ed | ||
|
|
da02e6df4a | ||
|
|
a092f496dd | ||
|
|
98ad80e1cb | ||
|
|
6ed4e2315f | ||
|
|
55ce6570f8 | ||
|
|
70fa03e93b | ||
|
|
8fe1480d8d | ||
|
|
f796dc5921 | ||
|
|
f725f3bb07 | ||
|
|
f3db0d5b69 | ||
|
|
b6fe3df3d6 | ||
|
|
5c885050fa | ||
|
|
b54c44bfb9 | ||
|
|
78664a9218 | ||
|
|
def814ff5c | ||
|
|
1bf13d381d | ||
|
|
2975ba8701 | ||
|
|
14759c3621 | ||
|
|
5ae65edd0a | ||
|
|
184d6f97bf | ||
|
|
8972acdfd7 | ||
|
|
a957903b98 | ||
|
|
b029c1e95a | ||
|
|
095ff97149 | ||
|
|
d00ebb31aa | ||
|
|
1c7f51c5e0 | ||
|
|
acc70669f1 | ||
|
|
2add42a284 | ||
|
|
225d03a65f | ||
|
|
f851212a55 | ||
|
|
199794a63a | ||
|
|
49129ea227 | ||
|
|
91b865dd23 | ||
|
|
0e311496b6 | ||
|
|
ed0f627ae4 | ||
|
|
569f27918a | ||
|
|
ec02d83ee6 | ||
|
|
debf4a5249 | ||
|
|
4ddc1ac89f | ||
|
|
507457dec1 | ||
|
|
525429bbb0 | ||
|
|
25e33ac1ee | ||
|
|
f428cb2de9 | ||
|
|
86070bc8aa | ||
|
|
afef00b473 | ||
|
|
812e9482af | ||
|
|
fa5dc80426 | ||
|
|
76b6a5c847 | ||
|
|
a19b738d73 | ||
|
|
526d72fd99 | ||
|
|
7ea8189c19 | ||
|
|
c453222774 | ||
|
|
905163880c | ||
|
|
ac106ca4f0 | ||
|
|
262dcf0c8d | ||
|
|
f6b3a0b829 | ||
|
|
ac7ab51dbb | ||
|
|
41ea2d0dda | ||
|
|
a4b68cd21c | ||
|
|
70d864191e | ||
|
|
9fdbaf92cc | ||
|
|
a75e57a8f7 | ||
|
|
9b187dadbe | ||
|
|
3ab783bbd0 | ||
|
|
0e95a5a4bc | ||
|
|
dcec62a1a2 | ||
|
|
9117af3021 | ||
|
|
65095d1290 | ||
|
|
380bf3e1c2 | ||
|
|
0090cf1e30 | ||
|
|
a24cbe43b6 | ||
|
|
0976b35357 | ||
|
|
84f8a7192e | ||
|
|
22479278b9 | ||
|
|
f27a55c2b6 | ||
|
|
106bd2ae87 | ||
|
|
ef4eb23226 | ||
|
|
d9a2284936 | ||
|
|
62027e95b2 | ||
|
|
551cb2f3c1 | ||
|
|
75dcaa7e7e | ||
|
|
c6952e794a | ||
|
|
7ad94bff2f | ||
|
|
20469e0487 | ||
|
|
4834cb2886 | ||
|
|
1f33bcfee7 | ||
|
|
8f8e769ead | ||
|
|
03296c7530 | ||
|
|
fecfb0f888 | ||
|
|
5e277de82b | ||
|
|
de4366bbc3 | ||
|
|
a8d38937e3 | ||
|
|
0ea04c9883 | ||
|
|
85553bf5d9 | ||
|
|
24f8ccb166 | ||
|
|
fb0b1d42ef | ||
|
|
38005406b0 | ||
|
|
6aee09b22b | ||
|
|
8c450a9b43 | ||
|
|
a660815df7 | ||
|
|
2b6d749566 | ||
|
|
dd900700e9 | ||
|
|
032b171025 | ||
|
|
ab005caa4a | ||
|
|
8e91af92ae | ||
|
|
97575e29a1 | ||
|
|
146b57216a | ||
|
|
2cdb0b9758 | ||
|
|
a6355914b2 | ||
|
|
fdcb83cc5f | ||
|
|
57bffa2cda | ||
|
|
e7189948f2 | ||
|
|
59a3a90d99 | ||
|
|
5619d0e026 | ||
|
|
21d58764cc | ||
|
|
2e17fc4368 | ||
|
|
4e921330db | ||
|
|
83423c1450 | ||
|
|
9d011f4982 | ||
|
|
6369458877 | ||
|
|
f855996d53 | ||
|
|
e75248ad6b | ||
|
|
8ccbe99fbc | ||
|
|
977358a3ab | ||
|
|
67154237fb | ||
|
|
d71b545971 | ||
|
|
7908d70750 | ||
|
|
8722c8a34a | ||
|
|
7488d0b334 | ||
|
|
d86f2bc1a4 | ||
|
|
6c1514cad7 | ||
|
|
c132bd9c1e | ||
|
|
f733282b48 | ||
|
|
4396b0fe74 | ||
|
|
04ab37ce23 | ||
|
|
01ca35f6a9 | ||
|
|
1c337ec40e | ||
|
|
aebafacda2 | ||
|
|
59b93bfbe1 | ||
|
|
eccc56369b | ||
|
|
75221eb855 | ||
|
|
b8d45ab42e | ||
|
|
bc2c6a56ed | ||
|
|
1becfdcd67 | ||
|
|
b90e684580 | ||
|
|
a3bd4b1b0a | ||
|
|
b6f4e5ce13 | ||
|
|
5fd12b809a | ||
|
|
573f1fe22f | ||
|
|
024f30ec36 | ||
|
|
87a3df3645 | ||
|
|
470314c32e | ||
|
|
6bf49e4532 | ||
|
|
b7dfd4390a | ||
|
|
454e2ad4fe | ||
|
|
9d859f3c27 | ||
|
|
25061495d0 | ||
|
|
8eca3165df | ||
|
|
97690f4dfd | ||
|
|
757d316a53 | ||
|
|
1d681d5029 | ||
|
|
9a8dc8b73e | ||
|
|
ff6b526747 | ||
|
|
4c8081c52b | ||
|
|
d93f9b84b5 | ||
|
|
c9ae4c4289 | ||
|
|
274c4fe122 | ||
|
|
6be39294d0 | ||
|
|
08b9db2f96 | ||
|
|
5a781d8d17 | ||
|
|
690ddb99b8 | ||
|
|
9ef1882a79 | ||
|
|
dc001cc06c | ||
|
|
487ecbc1a5 | ||
|
|
406d5dcc63 | ||
|
|
89e8ef41fc | ||
|
|
fa4ba857bd | ||
|
|
b52dd3a2dc | ||
|
|
94d2867129 | ||
|
|
86f10b8ec2 | ||
|
|
7aff314e72 | ||
|
|
b2d477345f | ||
|
|
b4f5f28102 | ||
|
|
1ddb90ef74 | ||
|
|
78456d2205 | ||
|
|
e6aa738435 | ||
|
|
7f53af08e6 | ||
|
|
8517eec344 | ||
|
|
5fcdc76b8c | ||
|
|
23830c977c | ||
|
|
572d41985a | ||
|
|
b03970f578 | ||
|
|
36974e3eb4 | ||
|
|
6eec55f3c3 | ||
|
|
d5e21f2c95 | ||
|
|
94e9ab5a0e | ||
|
|
30f53d50f0 | ||
|
|
2bf0c40328 | ||
|
|
4c2156a4ac | ||
|
|
a913996000 | ||
|
|
a74f930d42 | ||
|
|
4e22f4954e | ||
|
|
73d049b380 | ||
|
|
9171242c37 | ||
|
|
12985e1bd9 | ||
|
|
d5af0a740a | ||
|
|
28e93c05a2 | ||
|
|
173175a248 | ||
|
|
abd5c6dbb1 | ||
|
|
b333a90532 | ||
|
|
309f0ea85b | ||
|
|
f080088e19 | ||
|
|
15c952e6eb | ||
|
|
31a04b32b1 | ||
|
|
252b8af422 | ||
|
|
00e46c6e3d | ||
|
|
fe000f23bd | ||
|
|
fe9a942f4c | ||
|
|
50893b34c6 | ||
|
|
01c7f2b714 | ||
|
|
50bd3fd1a9 | ||
|
|
709e499de6 | ||
|
|
d56b1c2f83 | ||
|
|
0c2215ec74 | ||
|
|
acffd92fad | ||
|
|
b2af030559 | ||
|
|
effd44c53b | ||
|
|
6545dfa3e3 | ||
|
|
ac8752ec4f | ||
|
|
c396a8e7f8 | ||
|
|
6a22ee94d5 | ||
|
|
9184986717 | ||
|
|
b90115ba2e | ||
|
|
fefe52a56f | ||
|
|
f0d3cc7d4d | ||
|
|
06523bf61f | ||
|
|
615711924a | ||
|
|
211d3c5067 | ||
|
|
545aa771bf | ||
|
|
99af79c9c2 | ||
|
|
02420aacc5 | ||
|
|
f7e8345f88 | ||
|
|
5bd21861ab | ||
|
|
01d575edb0 | ||
|
|
557576cafc | ||
|
|
12a2dde2e6 | ||
|
|
ccb326603c | ||
|
|
c5d47ae35d | ||
|
|
d2c99b4db4 | ||
|
|
561150ab28 | ||
|
|
d60e0164db | ||
|
|
886969f4ee | ||
|
|
422b71d9b1 | ||
|
|
89fa27a883 | ||
|
|
a67cf6b596 | ||
|
|
80b21ea362 | ||
|
|
35b69141c1 | ||
|
|
f4edd45886 | ||
|
|
a95fe17ebc | ||
|
|
f178c19348 | ||
|
|
a46ba472bb | ||
|
|
3c10fa6b0a | ||
|
|
2d9bb319fe | ||
|
|
698d1b697c | ||
|
|
fd4dbb6fe6 | ||
|
|
a1ac68ccf0 | ||
|
|
d92e987f7a | ||
|
|
b82732af42 | ||
|
|
2fff7568b9 | ||
|
|
9c4c6cc660 | ||
|
|
ae4a8dfa5f | ||
|
|
f90c8db556 | ||
|
|
81a17aaad9 | ||
|
|
9b709515da | ||
|
|
f502309198 | ||
|
|
f5c3affc5f | ||
|
|
3a30a65264 | ||
|
|
b040e966ca | ||
|
|
36bf9e2fab | ||
|
|
5c896bbf22 | ||
|
|
4d57a12a29 | ||
|
|
cc42217c0c | ||
|
|
286174202e | ||
|
|
c708d83af0 | ||
|
|
1044cfc17c | ||
|
|
6d99535850 | ||
|
|
3a798f09ed | ||
|
|
e41ce4bb4c | ||
|
|
226051e555 | ||
|
|
887a83cf9d | ||
|
|
be5c54d903 | ||
|
|
90d71811dc | ||
|
|
3b20cea1b8 | ||
|
|
98b20164a9 | ||
|
|
cc7a64189b | ||
|
|
b5ef0d33b5 | ||
|
|
e61e81b485 | ||
|
|
4220913c28 | ||
|
|
491ad3ae44 | ||
|
|
48678e9533 | ||
|
|
45921f4b50 | ||
|
|
a95ac1599f | ||
|
|
30f84532b7 | ||
|
|
4da092eb2b | ||
|
|
f01a45a746 | ||
|
|
702c631460 | ||
|
|
bfa672dd37 | ||
|
|
04f47fe8ed | ||
|
|
b488c76204 | ||
|
|
0e8ad10f61 | ||
|
|
d660258bb6 | ||
|
|
bb20773f2a | ||
|
|
3a5ef923f4 | ||
|
|
f2a91018f6 | ||
|
|
16c729085e | ||
|
|
6b33fb52e0 | ||
|
|
96ddf1ded8 | ||
|
|
7c530680c0 | ||
|
|
e8429b8f74 | ||
|
|
175dfd06a0 | ||
|
|
b770713a28 | ||
|
|
1f6be53d3d | ||
|
|
bf57bc5a1c | ||
|
|
728061c66c | ||
|
|
1392dce1c5 | ||
|
|
4a79878dde | ||
|
|
c3bdacbf3d | ||
|
|
fe59c6850d | ||
|
|
d17d1a8ffd | ||
|
|
c73b8cef16 | ||
|
|
ff5dc41db3 | ||
|
|
1e41e3b65b | ||
|
|
3c8c4e6747 | ||
|
|
699ed00e40 | ||
|
|
6284db1bc8 | ||
|
|
8e49d4a5f3 | ||
|
|
fad5f16abf | ||
|
|
72369199e1 | ||
|
|
dba446a651 | ||
|
|
d64f8cdf0f | ||
|
|
0c014ced0c | ||
|
|
2acdb9349d | ||
|
|
61cb2f8cf8 | ||
|
|
4afe12cd46 | ||
|
|
0b91e9d084 | ||
|
|
ae85d39275 | ||
|
|
66af31330e | ||
|
|
663c3dfbd4 | ||
|
|
503c177a29 | ||
|
|
5edfd9e4c9 | ||
|
|
433df9dddb | ||
|
|
ca85f71f2b | ||
|
|
928d8c3ace | ||
|
|
9c8e740a1e | ||
|
|
e18b3a01bb | ||
|
|
1bee81bbab | ||
|
|
45c63a5831 | ||
|
|
7172ca84d6 | ||
|
|
29b25f968d | ||
|
|
ae0bd85ecc | ||
|
|
4291cf1b30 | ||
|
|
618d1060bf | ||
|
|
7d604a0ed7 | ||
|
|
f4f68ae13c | ||
|
|
9644ec0ff8 | ||
|
|
473b7432e8 | ||
|
|
d2527abd17 | ||
|
|
7caf0c2f68 | ||
|
|
9a037680cf | ||
|
|
b908a8de60 | ||
|
|
c49ec306d4 | ||
|
|
f405e42d39 | ||
|
|
5413a792d5 | ||
|
|
01a1690720 | ||
|
|
5ae053965b | ||
|
|
f14f5f0606 | ||
|
|
32d542aa6c | ||
|
|
f1da942189 | ||
|
|
2b1483f857 | ||
|
|
5ae86305f9 | ||
|
|
f83be52482 | ||
|
|
a241b9a9df | ||
|
|
b9e01b7738 | ||
|
|
587cd4484a | ||
|
|
8fd93ffbbe | ||
|
|
616b855d61 | ||
|
|
3a12813da3 | ||
|
|
66f04de8c1 | ||
|
|
b4c72dc8f4 | ||
|
|
4e84c7b376 | ||
|
|
50d8a575a6 | ||
|
|
499ec2306c | ||
|
|
aa150a1ae1 | ||
|
|
0ad1782d42 | ||
|
|
4ceac4d9c9 | ||
|
|
30f8bfe703 | ||
|
|
be9bde507a | ||
|
|
53ee50fa38 | ||
|
|
8cde433c37 | ||
|
|
fe88c581c7 | ||
|
|
32cb66db4c |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*_plus.go
|
||||
*_plus_test.go
|
||||
29
LICENSE
Normal file
29
LICENSE
Normal file
@@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2020, LiuXiangChao
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
25
README.md
Normal file
25
README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
GoEdge公共配置项。
|
||||
|
||||
目录结构:
|
||||
~~~
|
||||
pkg/
|
||||
dnsconfigs - 域名解析和NameServer相关配置
|
||||
messageconfigs - 消息通知相关配置
|
||||
monitorconfigs - 监控相关配置
|
||||
nodeconfigs - 边缘节点相关配置
|
||||
nodeutils - 边缘节点相关函数
|
||||
serverconfigs - 网站服务相关配置
|
||||
systemconfigs - 系统全局配置
|
||||
reporterconfigs - 区域监控终端配置
|
||||
userconfigs - 用户相关配置
|
||||
|
||||
configutils/ - 配置公共函数等
|
||||
errors/ - 错误处理
|
||||
rpc/ - RPC通讯
|
||||
protos/ RPC数据和接口定义
|
||||
sevice_*.proto RPC接口定义
|
||||
models/
|
||||
model_*.proto RPC数据定义
|
||||
~~~
|
||||
|
||||
开发时需要将 `rpc/protos/` 和 `rpc/protos/models/` 两个目录放入到Proto Buffer检查工具可以找到的位置。
|
||||
@@ -1 +1,3 @@
|
||||
每次 `.proto` 文件有更新的时候,请运行 `grpc.sh` 重新生成相应的源代码。
|
||||
每次 `.proto` 文件有更新的时候,请运行 `build.sh` 重新生成相应的源代码。
|
||||
|
||||
如果文件名有更改,请清空 `pkg/rpc/pb/*.go` 文件,然后再次运行 `build.sh`。
|
||||
23
build/build.sh
Executable file
23
build/build.sh
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "starting ..."
|
||||
|
||||
#rm -f ../pkg/rpc/pb/*.pb.go
|
||||
|
||||
protoc --go_out=plugins=grpc:../pkg/rpc --proto_path=../pkg/rpc/protos ../pkg/rpc/protos/*.proto
|
||||
RESULT=$?
|
||||
if [ "${RESULT}" != "0" ]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
RESULT=`protoc --go_out=plugins=grpc:../pkg/rpc --proto_path=../pkg/rpc/protos ../pkg/rpc/protos/models/*.proto`
|
||||
RESULT=$?
|
||||
if [ "${RESULT}" != "0" ]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
|
||||
# generate rpc.json
|
||||
./proto-json.sh --quiet
|
||||
|
||||
echo "ok"
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#rm -f ../pkg/rpc/pb/*.pb.go
|
||||
protoc --go_out=plugins=grpc:../pkg/rpc --proto_path=../pkg/rpc/protos ../pkg/rpc/protos/*.proto
|
||||
protoc --go_out=plugins=grpc:../pkg/rpc --proto_path=../pkg/rpc/protos ../pkg/rpc/protos/models/*.proto
|
||||
3
build/proto-json.sh
Executable file
3
build/proto-json.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
go run ../cmd/proto-json/main.go $1
|
||||
19553
build/rpc.json
Normal file
19553
build/rpc.json
Normal file
File diff suppressed because it is too large
Load Diff
407
cmd/proto-json/main.go
Normal file
407
cmd/proto-json/main.go
Normal file
@@ -0,0 +1,407 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ServiceInfo struct {
|
||||
Name string `json:"name"`
|
||||
Methods []*MethodInfo `json:"methods"`
|
||||
Filename string `json:"filename"`
|
||||
Doc string `json:"doc"`
|
||||
}
|
||||
|
||||
type MethodInfo struct {
|
||||
Name string `json:"name"`
|
||||
RequestMessageName string `json:"requestMessageName"`
|
||||
ResponseMessageName string `json:"responseMessageName"`
|
||||
Code string `json:"code"`
|
||||
Doc string `json:"doc"`
|
||||
Roles []string `json:"roles"`
|
||||
IsDeprecated bool `json:"isDeprecated"`
|
||||
}
|
||||
|
||||
type MessageInfo struct {
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
Doc string `json:"doc"`
|
||||
}
|
||||
|
||||
type LinkInfo struct {
|
||||
Name string `json:"name"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type RPCList struct {
|
||||
Services []*ServiceInfo `json:"services"`
|
||||
Messages []*MessageInfo `json:"messages"`
|
||||
Links []*LinkInfo `json:"links"`
|
||||
}
|
||||
|
||||
func readComments(data []byte) string {
|
||||
var lines = bytes.Split(data, []byte{'\n'})
|
||||
var comments = [][]byte{}
|
||||
for i := len(lines) - 1; i >= 0; i-- {
|
||||
var line = bytes.TrimLeft(lines[i], " \t")
|
||||
if len(line) == 0 {
|
||||
comments = append([][]byte{{' '}}, comments...)
|
||||
continue
|
||||
}
|
||||
|
||||
if bytes.HasPrefix(line, []byte("//")) {
|
||||
line = bytes.TrimSpace(bytes.TrimLeft(line, "/"))
|
||||
comments = append([][]byte{line}, comments...)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return string(bytes.TrimSpace(bytes.Join(comments, []byte{'\n'})))
|
||||
}
|
||||
|
||||
func removeDuplicates(s []string) []string {
|
||||
if len(s) == 0 {
|
||||
return s
|
||||
}
|
||||
var m = map[string]bool{}
|
||||
var result = []string{}
|
||||
for _, item := range s {
|
||||
_, ok := m[item]
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
result = append(result, item)
|
||||
m[item] = true
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// 生成JSON格式API列表
|
||||
func main() {
|
||||
var quiet = false
|
||||
flag.BoolVar(&quiet, "quiet", false, "")
|
||||
flag.Parse()
|
||||
|
||||
var methodRolesMap = map[string][]string{} // method => roles
|
||||
{
|
||||
var rootDir = filepath.Clean(Tea.Root + "/../../EdgeAPI/internal/rpc/services")
|
||||
entries, err := os.ReadDir(rootDir)
|
||||
if err != nil {
|
||||
logs.Println("[ERROR]read api services from '" + rootDir + "' failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var rootDirs = []string{rootDir}
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
rootDirs = append(rootDirs, rootDir+string(os.PathSeparator)+entry.Name())
|
||||
}
|
||||
}
|
||||
|
||||
for _, rootDir := range rootDirs {
|
||||
files, err := filepath.Glob(rootDir + "/service_*.go")
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]list service implementation files failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var methodNameReg = regexp.MustCompile(`func\s*\(\w+\s+\*\s*(\w+Service)\)\s*(\w+)\s*\(`) // $1: serviceName, $2 methodName
|
||||
for _, file := range files {
|
||||
data, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]read file '" + file + "' failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
var sourceCode = string(data)
|
||||
|
||||
var locList = methodNameReg.FindAllStringIndex(sourceCode, -1)
|
||||
for index, loc := range locList {
|
||||
var methodSource = ""
|
||||
if index == len(locList)-1 { // last one
|
||||
methodSource = sourceCode[loc[0]:]
|
||||
} else {
|
||||
methodSource = sourceCode[loc[0]:locList[index+1][0]]
|
||||
}
|
||||
|
||||
// 方法名
|
||||
var submatch = methodNameReg.FindStringSubmatch(methodSource)
|
||||
if len(submatch) == 0 {
|
||||
continue
|
||||
}
|
||||
var serviceName = submatch[1]
|
||||
if serviceName == "BaseService" {
|
||||
continue
|
||||
}
|
||||
var methodName = submatch[2]
|
||||
if methodName[0] < 'A' || methodName[0] > 'Z' {
|
||||
continue
|
||||
}
|
||||
var roles = []string{}
|
||||
if strings.Contains(methodSource, ".ValidateNode(") {
|
||||
roles = append(roles, "node")
|
||||
}
|
||||
if strings.Contains(methodSource, ".ValidateUserNode(") {
|
||||
roles = append(roles, "user")
|
||||
}
|
||||
if strings.Contains(methodSource, ".ValidateAdmin(") {
|
||||
roles = append(roles, "admin")
|
||||
}
|
||||
if strings.Contains(methodSource, ".ValidateAdminAndUser(") {
|
||||
roles = append(roles, "admin", "user")
|
||||
}
|
||||
if strings.Contains(methodSource, ".ValidateNSNode(") {
|
||||
roles = append(roles, "dns")
|
||||
}
|
||||
if strings.Contains(methodSource, ".ValidateMonitorNode(") {
|
||||
roles = append(roles, "monitor")
|
||||
}
|
||||
if strings.Contains(methodSource, "rpcutils.UserTypeDNS") {
|
||||
roles = append(roles, "dns")
|
||||
}
|
||||
if strings.Contains(methodSource, "rpcutils.UserTypeUser") {
|
||||
roles = append(roles, "user")
|
||||
}
|
||||
if strings.Contains(methodSource, "rpcutils.UserTypeNode") {
|
||||
roles = append(roles, "node")
|
||||
}
|
||||
if strings.Contains(methodSource, "rpcutils.UserTypeMonitor") {
|
||||
roles = append(roles, "monitor")
|
||||
}
|
||||
if strings.Contains(methodSource, "rpcutils.UserTypeReport") {
|
||||
roles = append(roles, "report")
|
||||
}
|
||||
if strings.Contains(methodSource, "rpcutils.UserTypeCluster") {
|
||||
roles = append(roles, "cluster")
|
||||
}
|
||||
if strings.Contains(methodSource, "rpcutils.UserTypeAdmin") {
|
||||
roles = append(roles, "admin")
|
||||
}
|
||||
|
||||
methodRolesMap[strings.ToLower(methodName)] = removeDuplicates(roles)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var services = []*ServiceInfo{}
|
||||
var messages = []*MessageInfo{}
|
||||
|
||||
{
|
||||
var dirs = []string{Tea.Root + "/../pkg/rpc/protos/", Tea.Root + "/../pkg/rpc/protos/models"}
|
||||
for _, dir := range dirs {
|
||||
func(dir string) {
|
||||
dir = filepath.Clean(dir)
|
||||
|
||||
files, err := filepath.Glob(dir + "/*.proto")
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]list proto files failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, path := range files {
|
||||
func(path string) {
|
||||
var filename = filepath.Base(path)
|
||||
if filename == "service_authority_key.proto" || filename == "service_authority_node.proto" {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 先将rpc代码替换成临时代码
|
||||
var methodCodeMap = map[string][]byte{} // code => method
|
||||
var methodIndex = 0
|
||||
var methodReg = regexp.MustCompile(`(?s)rpc\s+(\w+)\s*\(\s*(\w+)\s*\)\s*returns\s*\(\s*(\w+)\s*\)\s*(\{.+})?\s*;`)
|
||||
data = methodReg.ReplaceAllFunc(data, func(methodData []byte) []byte {
|
||||
methodIndex++
|
||||
var code = "METHOD" + types.String(methodIndex)
|
||||
methodCodeMap[code] = methodData
|
||||
return []byte("\n" + code)
|
||||
})
|
||||
|
||||
// 服务列表
|
||||
// TODO 这里需要改进一下,当前实现方法如果方法注释里有括号(}),就会导致部分方法解析不到
|
||||
var serviceNameReg = regexp.MustCompile(`(?sU)\n\s*service\s+(\w+)\s*\{(.+)}`)
|
||||
var serviceMatches = serviceNameReg.FindAllSubmatch(data, -1)
|
||||
var serviceNamePositions = serviceNameReg.FindAllIndex(data, -1)
|
||||
for serviceMatchIndex, serviceMatch := range serviceMatches {
|
||||
var serviceName = string(serviceMatch[1])
|
||||
var serviceNamePosition = serviceNamePositions[serviceMatchIndex][0]
|
||||
var comment = readComments(data[:serviceNamePosition])
|
||||
|
||||
// 方法列表
|
||||
var methods = []*MethodInfo{}
|
||||
var serviceData = serviceMatch[2]
|
||||
var methodCodeReg = regexp.MustCompile(`\b(METHOD\d+)\b`)
|
||||
var methodCodeMatches = methodCodeReg.FindAllSubmatch(serviceData, -1)
|
||||
var methodCodePositions = methodCodeReg.FindAllIndex(serviceData, -1)
|
||||
for methodMatchIndex, methodMatch := range methodCodeMatches {
|
||||
var methodCode = string(methodMatch[1])
|
||||
var methodData = methodCodeMap[methodCode]
|
||||
var methodPieces = methodReg.FindSubmatch(methodData)
|
||||
var methodCodePosition = methodCodePositions[methodMatchIndex]
|
||||
|
||||
var roles = methodRolesMap[strings.ToLower(string(methodPieces[1]))]
|
||||
if roles == nil {
|
||||
roles = []string{}
|
||||
}
|
||||
|
||||
methods = append(methods, &MethodInfo{
|
||||
Name: string(methodPieces[1]),
|
||||
RequestMessageName: string(methodPieces[2]),
|
||||
ResponseMessageName: string(methodPieces[3]),
|
||||
IsDeprecated: strings.Contains(string(methodPieces[4]), "deprecated"),
|
||||
Code: string(methodData),
|
||||
Doc: readComments(serviceData[:methodCodePosition[0]]),
|
||||
Roles: roles,
|
||||
})
|
||||
}
|
||||
|
||||
services = append(services, &ServiceInfo{
|
||||
Name: serviceName,
|
||||
Methods: methods,
|
||||
Filename: filename,
|
||||
Doc: comment,
|
||||
})
|
||||
}
|
||||
|
||||
// 消息列表
|
||||
var topMessageCodeMap = map[string][]byte{} // code => message
|
||||
var allMessageCodeMap = map[string][]byte{}
|
||||
var messageCodeIndex = 0
|
||||
var messagesReg = regexp.MustCompile(`(?sU)\n\s*message\s+(\w+)\s*\{([^{}]+)\n\s*}`)
|
||||
var firstMessagesReg = regexp.MustCompile(`message\s+(\w+)`)
|
||||
var messageCodeREG = regexp.MustCompile(`MESSAGE\d+`)
|
||||
for {
|
||||
var hasMessage = false
|
||||
|
||||
data = messagesReg.ReplaceAllFunc(data, func(messageData []byte) []byte {
|
||||
messageCodeIndex++
|
||||
hasMessage = true
|
||||
|
||||
// 是否包含子Message
|
||||
var subMatches = messageCodeREG.FindAllSubmatch(messageData, -1)
|
||||
for _, subMatch := range subMatches {
|
||||
var subMatchCode = string(subMatch[0])
|
||||
delete(topMessageCodeMap, subMatchCode)
|
||||
}
|
||||
|
||||
var code = "MESSAGE" + types.String(messageCodeIndex)
|
||||
topMessageCodeMap[code] = messageData
|
||||
allMessageCodeMap[code] = messageData
|
||||
return []byte("\n" + code)
|
||||
})
|
||||
if !hasMessage {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for messageCode, messageData := range topMessageCodeMap {
|
||||
// 替换其中的子Message
|
||||
for {
|
||||
if messageCodeREG.Match(messageData) {
|
||||
messageData = messageCodeREG.ReplaceAllFunc(messageData, func(messageCodeData []byte) []byte {
|
||||
return allMessageCodeMap[string(messageCodeData)]
|
||||
})
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 注释
|
||||
var index = bytes.Index(data, []byte(messageCode))
|
||||
var messageName = string(firstMessagesReg.FindSubmatch(messageData)[1])
|
||||
messages = append(messages, &MessageInfo{
|
||||
Name: messageName,
|
||||
Code: string(bytes.TrimSpace(messageData)),
|
||||
Doc: readComments(data[:index]),
|
||||
})
|
||||
}
|
||||
}(path)
|
||||
}
|
||||
}(dir)
|
||||
}
|
||||
}
|
||||
|
||||
var countServices = len(services)
|
||||
var countMethods = 0
|
||||
var countMessages = len(messages)
|
||||
for _, service := range services {
|
||||
countMethods += len(service.Methods)
|
||||
}
|
||||
|
||||
// 链接
|
||||
var links = []*LinkInfo{}
|
||||
|
||||
// json links
|
||||
{
|
||||
var dirs = []string{Tea.Root + "/../pkg/rpc/jsons"}
|
||||
for _, dir := range dirs {
|
||||
func(dir string) {
|
||||
dir = filepath.Clean(dir)
|
||||
|
||||
files, err := filepath.Glob(dir + "/*.md")
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]list .md files failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, path := range files {
|
||||
func(path string) {
|
||||
var name = strings.TrimSuffix(filepath.Base(path), ".md")
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]read '" + path + "' failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
links = append(links, &LinkInfo{
|
||||
Name: "json:" + name,
|
||||
Content: string(data),
|
||||
})
|
||||
}(path)
|
||||
}
|
||||
}(dir)
|
||||
}
|
||||
}
|
||||
|
||||
var rpcList = &RPCList{
|
||||
Services: services,
|
||||
Messages: messages,
|
||||
Links: links,
|
||||
}
|
||||
jsonData, err := json.MarshalIndent(rpcList, "", " ")
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]marshal to json failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var jsonFile = Tea.Root + "/rpc.json"
|
||||
err = os.WriteFile(jsonFile, jsonData, 0666)
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]write json to file failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if !quiet {
|
||||
fmt.Println("services:", countServices, "methods:", countMethods, "messages:", countMessages)
|
||||
fmt.Println("===")
|
||||
fmt.Println("generated " + filepath.Base(jsonFile) + " successfully")
|
||||
}
|
||||
}
|
||||
21
go.mod
21
go.mod
@@ -1,13 +1,18 @@
|
||||
module github.com/TeaOSLab/EdgeCommon
|
||||
|
||||
go 1.15
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.1.1
|
||||
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
|
||||
google.golang.org/grpc v1.32.0
|
||||
google.golang.org/protobuf v1.25.0
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/iwind/TeaGo v0.0.0-20220304043459-0dd944a5b475
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||
google.golang.org/grpc v1.45.0
|
||||
google.golang.org/protobuf v1.27.1
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e // indirect
|
||||
)
|
||||
|
||||
76
go.sum
76
go.sum
@@ -1,28 +1,35 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
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/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200624174652-8d2f3be8b2d9/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
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.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-redis/redis/v8 v8.0.0-beta.7/go.mod h1:FGJAWDWFht1sQ4qxyJHZZbVyvnVcKQN0E3u5/5lRz+g=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
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/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@@ -34,18 +41,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/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
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/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.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f h1:r2O8PONj/KiuZjJHVHn7KlCePUIjNtgAmvLfgRafQ8o=
|
||||
github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||
github.com/iwind/TeaGo v0.0.0-20220304043459-0dd944a5b475 h1:EseyfFaQOjWanGiby9KMw7PjDBMg/95tLDgIw/ns0Cw=
|
||||
github.com/iwind/TeaGo v0.0.0-20220304043459-0dd944a5b475/go.mod h1:HRHK0zoC/og3c9/hKosD9yYVMTnnzm3PgXUdhRYHaLc=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
@@ -66,12 +79,17 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
|
||||
github.com/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
go.opentelemetry.io/otel v0.7.0/go.mod h1:aZMyHG5TqDOXEgH2tyLiXSUKly1jT3yqE9PmrzIeCdo=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
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-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||
@@ -86,16 +104,22 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
|
||||
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-20190108225652-1e06a53dbb7e/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=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -108,11 +132,19 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/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-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/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.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/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=
|
||||
@@ -120,22 +152,27 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
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=
|
||||
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/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e h1:fNKDNuUyC4WH+inqDMpfXDdfvwfYILbsX+oskGZ8hxg=
|
||||
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/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.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=
|
||||
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
|
||||
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=
|
||||
@@ -144,8 +181,11 @@ 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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
@@ -153,11 +193,13 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
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.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// 拷贝同类型struct指针对象中的字段
|
||||
// CopyStructObject 拷贝同类型struct指针对象中的字段
|
||||
func CopyStructObject(destPtr, sourcePtr interface{}) {
|
||||
value := reflect.ValueOf(destPtr)
|
||||
value2 := reflect.ValueOf(sourcePtr)
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 从一组规则中匹配域名
|
||||
// MatchDomains 从一组规则中匹配域名
|
||||
// 支持的格式:example.com, www.example.com, .example.com, *.example.com, ~(\d+).example.com
|
||||
// 更多参考:http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name
|
||||
func MatchDomains(patterns []string, domain string) (isMatched bool) {
|
||||
@@ -14,19 +14,27 @@ func MatchDomains(patterns []string, domain string) (isMatched bool) {
|
||||
return
|
||||
}
|
||||
for _, pattern := range patterns {
|
||||
if matchDomain(pattern, domain) {
|
||||
if MatchDomain(pattern, domain) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 匹配单个域名规则
|
||||
func matchDomain(pattern string, domain string) (isMatched bool) {
|
||||
// MatchDomain 匹配单个域名规则
|
||||
func MatchDomain(pattern string, domain string) (isMatched bool) {
|
||||
if len(pattern) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if pattern == domain {
|
||||
return true
|
||||
}
|
||||
|
||||
if pattern == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
// 正则表达式
|
||||
if pattern[0] == '~' {
|
||||
reg, err := stringutil.RegexpCompile(strings.TrimSpace(pattern[1:]))
|
||||
@@ -57,3 +65,19 @@ func matchDomain(pattern string, domain string) (isMatched bool) {
|
||||
}
|
||||
return isMatched
|
||||
}
|
||||
|
||||
// IsFuzzyDomain 判断是否为特殊域名
|
||||
func IsFuzzyDomain(domain string) bool {
|
||||
if len(domain) == 0 {
|
||||
return true
|
||||
}
|
||||
if domain[0] == '.' || domain[0] == '~' {
|
||||
return true
|
||||
}
|
||||
for _, c := range domain {
|
||||
if c == '*' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -76,4 +76,25 @@ func TestMatchDomain(t *testing.T) {
|
||||
ok := MatchDomains([]string{"~^\\d+.example.com$"}, "123.example.com")
|
||||
a.IsTrue(ok)
|
||||
}
|
||||
{
|
||||
ok := MatchDomains([]string{"*"}, "example.com")
|
||||
a.IsTrue(ok)
|
||||
}
|
||||
|
||||
// port
|
||||
{
|
||||
ok := MatchDomains([]string{"example.com:8001"}, "example.com:8001")
|
||||
a.IsTrue(ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSpecialDomain(t *testing.T) {
|
||||
var a = assert.NewAssertion(t)
|
||||
|
||||
a.IsTrue(IsFuzzyDomain(""))
|
||||
a.IsTrue(IsFuzzyDomain(".hello.com"))
|
||||
a.IsTrue(IsFuzzyDomain("*.hello.com"))
|
||||
a.IsTrue(IsFuzzyDomain("hello.*.com"))
|
||||
a.IsTrue(IsFuzzyDomain("~^hello\\.com"))
|
||||
a.IsFalse(IsFuzzyDomain("hello.com"))
|
||||
}
|
||||
|
||||
@@ -2,25 +2,105 @@ package configutils
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"github.com/cespare/xxhash/v2"
|
||||
"math"
|
||||
"math/big"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IP2Long 将IP转换为整型
|
||||
// IPString2Long 将IP转换为整型
|
||||
// 注意IPv6没有顺序
|
||||
func IP2Long(ip string) uint64 {
|
||||
func IPString2Long(ip string) uint64 {
|
||||
if len(ip) == 0 {
|
||||
return 0
|
||||
}
|
||||
s := net.ParseIP(ip)
|
||||
if len(s) == 0 {
|
||||
var netIP = net.ParseIP(ip)
|
||||
if len(netIP) == 0 {
|
||||
return 0
|
||||
}
|
||||
return IP2Long(netIP)
|
||||
}
|
||||
|
||||
// IP2Long 将IP对象转换为整型
|
||||
func IP2Long(netIP net.IP) uint64 {
|
||||
if len(netIP) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
if strings.Contains(ip, ":") {
|
||||
return math.MaxUint32 + xxhash.Sum64(s)
|
||||
var b4 = netIP.To4()
|
||||
if b4 != nil {
|
||||
return uint64(binary.BigEndian.Uint32(b4.To4()))
|
||||
}
|
||||
return uint64(binary.BigEndian.Uint32(s.To4()))
|
||||
|
||||
var i = big.NewInt(0)
|
||||
i.SetBytes(netIP.To16())
|
||||
return i.Uint64()
|
||||
}
|
||||
|
||||
// IsIPv4 检查是否为IPv4
|
||||
func IsIPv4(netIP net.IP) bool {
|
||||
if len(netIP) == 0 {
|
||||
return false
|
||||
}
|
||||
return netIP.To4() != nil
|
||||
}
|
||||
|
||||
// IsIPv6 检查是否为IPv6
|
||||
func IsIPv6(netIP net.IP) bool {
|
||||
if len(netIP) == 0 {
|
||||
return false
|
||||
}
|
||||
return netIP.To4() == nil && netIP.To16() != nil
|
||||
}
|
||||
|
||||
// IPVersion 获取IP版本号
|
||||
func IPVersion(netIP net.IP) int {
|
||||
if len(netIP) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
if netIP.To4() != nil {
|
||||
return 4
|
||||
}
|
||||
|
||||
if netIP.To16() != nil {
|
||||
return 6
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// ParseCIDR 计算CIDR最大值
|
||||
func ParseCIDR(cidr string) (ipFrom string, ipTo string, err error) {
|
||||
_, ipNet, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
ipFrom = ipNet.IP.String()
|
||||
Loop:
|
||||
for i := len(ipNet.Mask) - 1; i >= 0; i-- {
|
||||
for j := 7; j >= 0; j-- {
|
||||
var m = ipNet.Mask[i] >> (7 - j) & 1 // 读取某位bit
|
||||
if m == 0 {
|
||||
ipNet.IP[i] |= 1 << j // 将此位置为1
|
||||
} else {
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
}
|
||||
ipTo = ipNet.IP.String()
|
||||
return
|
||||
}
|
||||
|
||||
// QuoteIP 为IPv6加上括号
|
||||
func QuoteIP(ip string) string {
|
||||
if len(ip) == 0 {
|
||||
return ip
|
||||
}
|
||||
if !strings.Contains(ip, ":") {
|
||||
return ip
|
||||
}
|
||||
if ip[0] != '[' {
|
||||
return "[" + ip + "]"
|
||||
}
|
||||
return ip
|
||||
}
|
||||
|
||||
41
pkg/configutils/ip_test.go
Normal file
41
pkg/configutils/ip_test.go
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package configutils_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseCIDR(t *testing.T) {
|
||||
t.Log(configutils.ParseCIDR("192.168.1.1/32"))
|
||||
t.Log(configutils.ParseCIDR("192.168.1.1/24"))
|
||||
t.Log(configutils.ParseCIDR("192.168.1.1/16"))
|
||||
}
|
||||
|
||||
func TestIPString2Long(t *testing.T) {
|
||||
for _, ip := range []string{"127.0.0.1", "192.168.1.100", "::1", "fd00:6868:6868:0:10ac:d056:3bf6:7452", "fd00:6868:6868:0:10ac:d056:3bf6:7453", "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "wrong ip"} {
|
||||
t.Log(fmt.Sprintf("%42s", ip), "=>", configutils.IPString2Long(ip))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIPv4(t *testing.T) {
|
||||
t.Log(configutils.IsIPv4(net.ParseIP("192.168.1.100")))
|
||||
t.Log(configutils.IsIPv4(net.ParseIP("::1")))
|
||||
}
|
||||
|
||||
func TestIsIPv6(t *testing.T) {
|
||||
t.Log(configutils.IsIPv6(net.ParseIP("192.168.1.100")))
|
||||
t.Log(configutils.IsIPv6(net.ParseIP("::1")))
|
||||
}
|
||||
|
||||
func TestIPVersion(t *testing.T) {
|
||||
var a = assert.NewAssertion(t)
|
||||
a.IsTrue(configutils.IPVersion(net.ParseIP("192.168.1.100")) == 4)
|
||||
a.IsTrue(configutils.IPVersion(net.ParseIP("1.2.3")) == 0)
|
||||
a.IsTrue(configutils.IPVersion(net.ParseIP("::1")) == 6)
|
||||
a.IsTrue(configutils.IPVersion(net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334")) == 6)
|
||||
}
|
||||
@@ -12,10 +12,20 @@ type VariableHolders = []interface{}
|
||||
|
||||
var variableMapping = map[string][]interface{}{} // source => [holder1, ...]
|
||||
var variableLocker = sync.RWMutex{}
|
||||
var regexpNamedVariable = regexp.MustCompile("\\${[\\w.-]+}")
|
||||
var regexpNamedVariable = regexp.MustCompile(`\${[\w.-]+}`)
|
||||
|
||||
var stringBuilderPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &strings.Builder{}
|
||||
},
|
||||
}
|
||||
|
||||
// ParseVariables 分析变量
|
||||
func ParseVariables(source string, replacer func(varName string) (value string)) string {
|
||||
if len(source) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
variableLocker.RLock()
|
||||
holders, found := variableMapping[source]
|
||||
variableLocker.RUnlock()
|
||||
@@ -31,17 +41,29 @@ func ParseVariables(source string, replacer func(varName string) (value string))
|
||||
return source
|
||||
}
|
||||
|
||||
// replace
|
||||
result := strings.Builder{}
|
||||
// 只有一个占位时,我们快速返回
|
||||
if len(holders) == 1 {
|
||||
var h = holders[0]
|
||||
holder, ok := h.(VariableHolder)
|
||||
if ok {
|
||||
return replacer(string(holder))
|
||||
}
|
||||
return source
|
||||
}
|
||||
|
||||
// 多个占位时,使用Builder
|
||||
var builder = stringBuilderPool.Get().(*strings.Builder)
|
||||
builder.Reset()
|
||||
defer stringBuilderPool.Put(builder)
|
||||
for _, h := range holders {
|
||||
holder, ok := h.(VariableHolder)
|
||||
if ok {
|
||||
result.WriteString(replacer(string(holder)))
|
||||
builder.WriteString(replacer(string(holder)))
|
||||
} else {
|
||||
result.Write(h.([]byte))
|
||||
builder.Write(h.([]byte))
|
||||
}
|
||||
}
|
||||
return result.String()
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
// ParseVariablesFromHolders 从占位中分析变量
|
||||
@@ -82,5 +104,8 @@ func ParseHolders(source string) (holders VariableHolders) {
|
||||
|
||||
// HasVariables 判断是否有变量
|
||||
func HasVariables(source string) bool {
|
||||
if len(source) == 0 {
|
||||
return false
|
||||
}
|
||||
return regexpNamedVariable.MatchString(source)
|
||||
}
|
||||
|
||||
@@ -1,17 +1,31 @@
|
||||
package configutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseVariables(t *testing.T) {
|
||||
v := ParseVariables("hello, ${name}, world", func(s string) string {
|
||||
return "Lu"
|
||||
})
|
||||
t.Log(v)
|
||||
{
|
||||
v := ParseVariables("hello, ${name}, world", func(s string) string {
|
||||
return "Lu"
|
||||
})
|
||||
t.Log(v)
|
||||
}
|
||||
{
|
||||
v := ParseVariables("hello, world", func(s string) string {
|
||||
return "Lu"
|
||||
})
|
||||
t.Log(v)
|
||||
}
|
||||
{
|
||||
v := ParseVariables("${name}", func(s string) string {
|
||||
return "Lu"
|
||||
})
|
||||
t.Log(v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNoVariables(t *testing.T) {
|
||||
@@ -39,6 +53,8 @@ func BenchmarkParseVariables(b *testing.B) {
|
||||
return "Lu"
|
||||
})
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = ParseVariables("hello, ${name}, ${age}, ${gender}, ${home}, world", func(s string) string {
|
||||
return "Lu"
|
||||
@@ -58,7 +74,17 @@ func BenchmarkParseVariablesFromHolders(b *testing.B) {
|
||||
|
||||
func BenchmarkParseVariablesUnique(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = ParseVariables("hello, ${name} "+strconv.Itoa(i%1000), func(s string) string {
|
||||
_ = ParseVariables("hello, ${name} "+strconv.Itoa(i), func(s string) string {
|
||||
return "Lu"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParseVariablesUnique_Single(b *testing.B) {
|
||||
runtime.GOMAXPROCS(1)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = ParseVariables("${name}", func(s string) string {
|
||||
return "Lu"
|
||||
})
|
||||
}
|
||||
@@ -66,7 +92,15 @@ func BenchmarkParseVariablesUnique(b *testing.B) {
|
||||
|
||||
func BenchmarkParseNoVariables(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = ParseVariables("hello, world, "+fmt.Sprintf("%d", i%1000), func(s string) string {
|
||||
_ = ParseVariables("hello, world", func(s string) string {
|
||||
return "Lu"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParseEmpty(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = ParseVariables("", func(s string) string {
|
||||
return "Lu"
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package configutils
|
||||
|
||||
import (
|
||||
"github.com/go-yaml/yaml"
|
||||
"io/ioutil"
|
||||
"gopkg.in/yaml.v3"
|
||||
"os"
|
||||
)
|
||||
|
||||
func UnmarshalYamlFile(file string, ptr interface{}) error {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
data, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
1
pkg/dnsconfigs/.gitignore
vendored
Normal file
1
pkg/dnsconfigs/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
ns_*
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package dnsconfigs
|
||||
|
||||
type AccessLogRef struct {
|
||||
IsPrior bool `yaml:"isPrior" json:"isPrior"` // 是否覆盖
|
||||
IsOn bool `yaml:"isOn" json:"isOn"` // 是否启用
|
||||
}
|
||||
|
||||
func (this *AccessLogRef) Init() error {
|
||||
return nil
|
||||
}
|
||||
@@ -2,6 +2,11 @@ package dnsconfigs
|
||||
|
||||
// ClusterDNSConfig 集群的DNS设置
|
||||
type ClusterDNSConfig struct {
|
||||
CNAMERecords []string `yaml:"cnameRecords" json:"cnameRecords"` // 自动加入的CNAME
|
||||
TTL int32 `yaml:"ttl" json:"ttl"` // 默认TTL,各个DNS服务商对记录的TTL的限制各有不同
|
||||
CNAMEAsDomain bool `yaml:"cnameAsDomain" json:"cnameAsDomain"` // 是否可以像域名一样直接访问CNAME
|
||||
IncludingLnNodes bool `yaml:"includingLnNodes" json:"includingLnNodes"` // 是否包含Ln节点
|
||||
|
||||
NodesAutoSync bool `yaml:"nodesAutoSync" json:"nodesAutoSync"` // 是否自动同步节点状态
|
||||
ServersAutoSync bool `yaml:"serversAutoSync" json:"serversAutoSync"` // 是否自动同步服务状态
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package dnsconfigs
|
||||
|
||||
import "fmt"
|
||||
|
||||
type NSNodeConfig struct {
|
||||
Id int64 `json:"id"`
|
||||
ClusterId int64 `json:"clusterId"`
|
||||
AccessLogRef *AccessLogRef `json:"accessLogRef"`
|
||||
|
||||
paddedId string
|
||||
}
|
||||
|
||||
func (this *NSNodeConfig) Init() error {
|
||||
this.paddedId = fmt.Sprintf("%08d", this.Id)
|
||||
|
||||
// accessLog
|
||||
if this.AccessLogRef != nil {
|
||||
err := this.AccessLogRef.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *NSNodeConfig) PaddedId() string {
|
||||
return this.paddedId
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package dnsconfigs
|
||||
|
||||
type RecordTTL struct {
|
||||
Name string `json:"name"`
|
||||
Value int `json:"value"`
|
||||
}
|
||||
|
||||
func FindAllRecordTTL() []*RecordTTL {
|
||||
return []*RecordTTL{
|
||||
{
|
||||
Name: "10分钟",
|
||||
Value: 10 * 60,
|
||||
},
|
||||
{
|
||||
Name: "30分钟",
|
||||
Value: 30 * 60,
|
||||
},
|
||||
{
|
||||
Name: "1小时",
|
||||
Value: 3600,
|
||||
},
|
||||
{
|
||||
Name: "12小时",
|
||||
Value: 12 * 3600,
|
||||
},
|
||||
{
|
||||
Name: "1天",
|
||||
Value: 86400,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -13,11 +13,13 @@ const (
|
||||
RecordTypeSRV RecordType = "SRV"
|
||||
RecordTypeTXT RecordType = "TXT"
|
||||
RecordTypeCAA RecordType = "CAA"
|
||||
RecordTypeSOA RecordType = "SOA"
|
||||
)
|
||||
|
||||
type RecordTypeDefinition struct {
|
||||
Type RecordType `json:"type"`
|
||||
Description string `json:"description"`
|
||||
CanDefine bool `json:"canDefine"` // 用户是否可以自定义
|
||||
}
|
||||
|
||||
func FindAllRecordTypeDefinitions() []*RecordTypeDefinition {
|
||||
@@ -25,34 +27,57 @@ func FindAllRecordTypeDefinitions() []*RecordTypeDefinition {
|
||||
{
|
||||
Type: RecordTypeA,
|
||||
Description: "将域名指向一个IPV4地址",
|
||||
CanDefine: true,
|
||||
},
|
||||
{
|
||||
Type: RecordTypeCNAME,
|
||||
Description: "将域名指向另外一个域名",
|
||||
CanDefine: true,
|
||||
},
|
||||
{
|
||||
Type: RecordTypeAAAA,
|
||||
Description: "将域名指向一个IPV6地址",
|
||||
CanDefine: true,
|
||||
},
|
||||
{
|
||||
Type: RecordTypeNS,
|
||||
Description: "将子域名指定其他DNS服务器解析",
|
||||
CanDefine: false,
|
||||
},
|
||||
{
|
||||
Type: RecordTypeSOA,
|
||||
Description: "起始授权机构记录",
|
||||
CanDefine: false,
|
||||
},
|
||||
{
|
||||
Type: RecordTypeMX,
|
||||
Description: "将域名指向邮件服务器地址",
|
||||
CanDefine: true,
|
||||
},
|
||||
{
|
||||
Type: RecordTypeSRV,
|
||||
Description: "记录提供特定的服务的服务器",
|
||||
CanDefine: true,
|
||||
},
|
||||
{
|
||||
Type: RecordTypeTXT,
|
||||
Description: "文本长度限制512,通常做SPF记录或者校验域名所有者",
|
||||
CanDefine: true,
|
||||
},
|
||||
{
|
||||
Type: RecordTypeCAA,
|
||||
Description: "CA证书颁发机构授权校验",
|
||||
CanDefine: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func FindAllUserRecordTypeDefinitions() []*RecordTypeDefinition {
|
||||
var result = []*RecordTypeDefinition{}
|
||||
for _, r := range FindAllRecordTypeDefinitions() {
|
||||
if r.CanDefine {
|
||||
result = append(result, r)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package dnsconfigs
|
||||
|
||||
import "github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||
|
||||
type RouteRangeType = string
|
||||
|
||||
const (
|
||||
RouteRangeTypeIP RouteRangeType = "ipRange"
|
||||
)
|
||||
|
||||
type RouteRangeInterface interface {
|
||||
Init() error
|
||||
Contains(ip uint64) bool
|
||||
}
|
||||
|
||||
// RouteRangeIPRange IP范围配置
|
||||
type RouteRangeIPRange struct {
|
||||
IPFrom string `json:"ipFrom"`
|
||||
IPTo string `json:"ipTo"`
|
||||
|
||||
ipFromLong uint64
|
||||
ipToLong uint64
|
||||
}
|
||||
|
||||
func (this *RouteRangeIPRange) Init() error {
|
||||
this.ipFromLong = configutils.IP2Long(this.IPFrom)
|
||||
this.ipToLong = configutils.IP2Long(this.IPTo)
|
||||
|
||||
if this.ipFromLong > this.ipToLong {
|
||||
this.ipFromLong, this.ipToLong = this.ipToLong, this.ipFromLong
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *RouteRangeIPRange) Contains(ip uint64) bool {
|
||||
return this.ipFromLong <= ip && this.ipToLong >= ip
|
||||
}
|
||||
1
pkg/iplibrary/.gitignore
vendored
Normal file
1
pkg/iplibrary/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
ip.db
|
||||
86
pkg/iplibrary/default_ip_library.go
Normal file
86
pkg/iplibrary/default_ip_library.go
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
_ "embed"
|
||||
"net"
|
||||
)
|
||||
|
||||
//go:embed internal-ip-library.db
|
||||
var ipLibraryData []byte
|
||||
|
||||
var defaultLibrary = NewIPLibrary()
|
||||
|
||||
func DefaultIPLibraryData() []byte {
|
||||
return ipLibraryData
|
||||
}
|
||||
|
||||
func InitDefault() error {
|
||||
defaultLibrary.reader = nil
|
||||
return defaultLibrary.InitFromData(ipLibraryData)
|
||||
}
|
||||
|
||||
func Lookup(ip net.IP) *QueryResult {
|
||||
return defaultLibrary.Lookup(ip)
|
||||
}
|
||||
|
||||
func LookupIP(ip string) *QueryResult {
|
||||
return defaultLibrary.LookupIP(ip)
|
||||
}
|
||||
|
||||
type IPLibrary struct {
|
||||
reader *Reader
|
||||
}
|
||||
|
||||
func NewIPLibrary() *IPLibrary {
|
||||
return &IPLibrary{}
|
||||
}
|
||||
|
||||
func NewIPLibraryWithReader(reader *Reader) *IPLibrary {
|
||||
return &IPLibrary{reader: reader}
|
||||
}
|
||||
|
||||
func (this *IPLibrary) InitFromData(data []byte) error {
|
||||
if len(data) == 0 || this.reader != nil {
|
||||
return nil
|
||||
}
|
||||
var reader = bytes.NewReader(data)
|
||||
gzipReader, err := gzip.NewReader(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = gzipReader.Close()
|
||||
}()
|
||||
|
||||
libReader, err := NewReader(gzipReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
this.reader = libReader
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *IPLibrary) Lookup(ip net.IP) *QueryResult {
|
||||
if this.reader == nil {
|
||||
return &QueryResult{}
|
||||
}
|
||||
|
||||
var result = this.reader.Lookup(ip)
|
||||
if result == nil {
|
||||
result = &QueryResult{}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (this *IPLibrary) LookupIP(ip string) *QueryResult {
|
||||
if this.reader == nil {
|
||||
return &QueryResult{}
|
||||
}
|
||||
return this.Lookup(net.ParseIP(ip))
|
||||
}
|
||||
91
pkg/iplibrary/default_ip_library_test.go
Normal file
91
pkg/iplibrary/default_ip_library_test.go
Normal file
@@ -0,0 +1,91 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"net"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestIPLibrary_Init(t *testing.T) {
|
||||
var lib = iplibrary.NewIPLibrary()
|
||||
|
||||
err := lib.InitFromData(iplibrary.DefaultIPLibraryData())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPLibrary_Lookup(t *testing.T) {
|
||||
var stat1 = &runtime.MemStats{}
|
||||
runtime.ReadMemStats(stat1)
|
||||
|
||||
var lib = iplibrary.NewIPLibrary()
|
||||
|
||||
var before = time.Now()
|
||||
|
||||
err := lib.InitFromData(iplibrary.DefaultIPLibraryData())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var costMs = time.Since(before).Seconds() * 1000
|
||||
runtime.GC()
|
||||
debug.FreeOSMemory()
|
||||
|
||||
var stat2 = &runtime.MemStats{}
|
||||
runtime.ReadMemStats(stat2)
|
||||
|
||||
t.Log((stat2.Alloc-stat1.Alloc)/1024/1024, "M", fmt.Sprintf("%.2f", costMs), "ms")
|
||||
|
||||
for _, ip := range []string{
|
||||
"127.0.0.1",
|
||||
"8.8.8.8",
|
||||
"4.4.4.4",
|
||||
"202.96.0.20",
|
||||
"66.249.66.69",
|
||||
"2222", // wrong ip
|
||||
"2406:8c00:0:3401:133:18:168:70", // ipv6
|
||||
} {
|
||||
var result = lib.Lookup(net.ParseIP(ip))
|
||||
t.Log(ip, "=>", result.IsOk(), "[", result.CountryName(), result.CountryId(), "][", result.ProvinceName(), result.ProvinceId(), "][", result.TownName(), result.TownId(), "][", result.ProviderName(), result.ProviderId(), "]")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPLibrary_LookupIP(t *testing.T) {
|
||||
var lib = iplibrary.NewIPLibrary()
|
||||
err := lib.InitFromData(iplibrary.DefaultIPLibraryData())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, ip := range []string{
|
||||
"66.249.66.69",
|
||||
} {
|
||||
var result = lib.LookupIP(ip)
|
||||
if result.IsOk() {
|
||||
t.Log(ip, "=>", result.IsOk(), "[", result.CountryName(), result.CountryId(), "][", result.ProvinceName(), result.ProvinceId(), "][", result.TownName(), result.TownId(), "][", result.ProviderName(), result.ProviderId(), "]")
|
||||
} else {
|
||||
t.Log(ip, "=>", result.IsOk())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIPLibrary_Lookup(b *testing.B) {
|
||||
var lib = iplibrary.NewIPLibrary()
|
||||
err := lib.InitFromData(iplibrary.DefaultIPLibraryData())
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = lib.LookupIP("66.249.66.69")
|
||||
}
|
||||
}
|
||||
BIN
pkg/iplibrary/internal-ip-library.db
Normal file
BIN
pkg/iplibrary/internal-ip-library.db
Normal file
Binary file not shown.
51
pkg/iplibrary/ip_item.go
Normal file
51
pkg/iplibrary/ip_item.go
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
)
|
||||
|
||||
type ipItem struct {
|
||||
IPFrom uint64
|
||||
IPTo uint64
|
||||
|
||||
Region *ipRegion
|
||||
}
|
||||
|
||||
type ipRegion struct {
|
||||
CountryId uint32
|
||||
ProvinceId uint32
|
||||
CityId uint32
|
||||
TownId uint32
|
||||
ProviderId uint32
|
||||
}
|
||||
|
||||
func (this *ipItem) AsBinary() ([]byte, error) {
|
||||
var buf = &bytes.Buffer{}
|
||||
err := binary.Write(buf, binary.BigEndian, this)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func HashRegion(countryId uint32, provinceId uint32, cityId uint32, townId uint32, providerId uint32) string {
|
||||
var providerHash = ""
|
||||
if providerId > 0 {
|
||||
providerHash = "_" + types.String(providerId)
|
||||
}
|
||||
|
||||
if townId > 0 {
|
||||
return "t" + types.String(townId) + providerHash
|
||||
}
|
||||
if cityId > 0 {
|
||||
return "c" + types.String(cityId) + providerHash
|
||||
}
|
||||
if provinceId > 0 {
|
||||
return "p" + types.String(provinceId) + providerHash
|
||||
}
|
||||
return "a" + types.String(countryId) + providerHash
|
||||
}
|
||||
95
pkg/iplibrary/meta.go
Normal file
95
pkg/iplibrary/meta.go
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary
|
||||
|
||||
type Country struct {
|
||||
Id uint32 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Codes []string `json:"codes"`
|
||||
}
|
||||
|
||||
type Province struct {
|
||||
Id uint32 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Codes []string `json:"codes"`
|
||||
}
|
||||
|
||||
type City struct {
|
||||
Id uint32 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Codes []string `json:"codes"`
|
||||
}
|
||||
|
||||
type Town struct {
|
||||
Id uint32 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Codes []string `json:"codes"`
|
||||
}
|
||||
|
||||
type Provider struct {
|
||||
Id uint32 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Codes []string `json:"codes"`
|
||||
}
|
||||
|
||||
type Meta struct {
|
||||
Version int `json:"version"` // IP库版本
|
||||
Code string `json:"code"` // 代号,用来区分不同的IP库
|
||||
Author string `json:"author"`
|
||||
Countries []*Country `json:"countries"`
|
||||
Provinces []*Province `json:"provinces"`
|
||||
Cities []*City `json:"cities"`
|
||||
Towns []*Town `json:"towns"`
|
||||
Providers []*Provider `json:"providers"`
|
||||
CreatedAt int64 `json:"createdAt"`
|
||||
|
||||
countryMap map[uint32]*Country // id => *Country
|
||||
provinceMap map[uint32]*Province // id => *Province
|
||||
cityMap map[uint32]*City // id => *City
|
||||
townMap map[uint32]*Town // id => *Town
|
||||
providerMap map[uint32]*Provider // id => *Provider
|
||||
}
|
||||
|
||||
func (this *Meta) Init() {
|
||||
this.countryMap = map[uint32]*Country{}
|
||||
this.provinceMap = map[uint32]*Province{}
|
||||
this.cityMap = map[uint32]*City{}
|
||||
this.townMap = map[uint32]*Town{}
|
||||
this.providerMap = map[uint32]*Provider{}
|
||||
|
||||
for _, country := range this.Countries {
|
||||
this.countryMap[country.Id] = country
|
||||
}
|
||||
for _, province := range this.Provinces {
|
||||
this.provinceMap[province.Id] = province
|
||||
}
|
||||
for _, city := range this.Cities {
|
||||
this.cityMap[city.Id] = city
|
||||
}
|
||||
for _, town := range this.Towns {
|
||||
this.townMap[town.Id] = town
|
||||
}
|
||||
for _, provider := range this.Providers {
|
||||
this.providerMap[provider.Id] = provider
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Meta) CountryWithId(countryId uint32) *Country {
|
||||
return this.countryMap[countryId]
|
||||
}
|
||||
|
||||
func (this *Meta) ProvinceWithId(provinceId uint32) *Province {
|
||||
return this.provinceMap[provinceId]
|
||||
}
|
||||
|
||||
func (this *Meta) CityWithId(cityId uint32) *City {
|
||||
return this.cityMap[cityId]
|
||||
}
|
||||
|
||||
func (this *Meta) TownWithId(townId uint32) *Town {
|
||||
return this.townMap[townId]
|
||||
}
|
||||
|
||||
func (this *Meta) ProviderWithId(providerId uint32) *Provider {
|
||||
return this.providerMap[providerId]
|
||||
}
|
||||
3
pkg/iplibrary/meta_test.go
Normal file
3
pkg/iplibrary/meta_test.go
Normal file
@@ -0,0 +1,3 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary_test
|
||||
64
pkg/iplibrary/parser.go
Normal file
64
pkg/iplibrary/parser.go
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type Parser struct {
|
||||
config *ParserConfig
|
||||
|
||||
data []byte
|
||||
}
|
||||
|
||||
func NewParser(config *ParserConfig) (*Parser, error) {
|
||||
if config == nil {
|
||||
config = &ParserConfig{}
|
||||
}
|
||||
|
||||
if config.Template == nil {
|
||||
return nil, errors.New("template must not be nil")
|
||||
}
|
||||
|
||||
return &Parser{
|
||||
config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (this *Parser) Write(data []byte) {
|
||||
this.data = append(this.data, data...)
|
||||
}
|
||||
|
||||
func (this *Parser) Parse() error {
|
||||
if len(this.data) == 0 {
|
||||
return nil
|
||||
}
|
||||
for {
|
||||
var index = bytes.IndexByte(this.data, '\n')
|
||||
if index >= 0 {
|
||||
var line = this.data[:index+1]
|
||||
values, found := this.config.Template.Extract(string(line), this.config.EmptyValues)
|
||||
if found {
|
||||
if this.config.Iterator != nil {
|
||||
err := this.config.Iterator(values)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 防止错误信息太长
|
||||
if len(line) > 256 {
|
||||
line = line[:256]
|
||||
}
|
||||
return errors.New("invalid line '" + string(line) + "'")
|
||||
}
|
||||
|
||||
this.data = this.data[index+1:]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
9
pkg/iplibrary/parser_config.go
Normal file
9
pkg/iplibrary/parser_config.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary
|
||||
|
||||
type ParserConfig struct {
|
||||
Template *Template
|
||||
EmptyValues []string
|
||||
Iterator func(values map[string]string) error
|
||||
}
|
||||
54
pkg/iplibrary/parser_reader.go
Normal file
54
pkg/iplibrary/parser_reader.go
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
type ReaderParser struct {
|
||||
reader io.Reader
|
||||
rawParser *Parser
|
||||
}
|
||||
|
||||
func NewReaderParser(reader io.Reader, config *ParserConfig) (*ReaderParser, error) {
|
||||
if config == nil {
|
||||
config = &ParserConfig{}
|
||||
}
|
||||
|
||||
if config.Template == nil {
|
||||
return nil, errors.New("template must not be nil")
|
||||
}
|
||||
|
||||
parser, err := NewParser(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ReaderParser{
|
||||
reader: reader,
|
||||
rawParser: parser,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (this *ReaderParser) Parse() error {
|
||||
var buf = make([]byte, 1024)
|
||||
for {
|
||||
n, err := this.reader.Read(buf)
|
||||
if n > 0 {
|
||||
this.rawParser.Write(buf[:n])
|
||||
parseErr := this.rawParser.Parse()
|
||||
if parseErr != nil {
|
||||
return parseErr
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
62
pkg/iplibrary/parser_reader_test.go
Normal file
62
pkg/iplibrary/parser_reader_test.go
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewReaderParser(t *testing.T) {
|
||||
template, err := iplibrary.NewTemplate("${ipFrom}|${ipTo}|${country}|${any}|${province}|${city}|${provider}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var buf = &bytes.Buffer{}
|
||||
buf.WriteString(`8.45.160.0|8.45.161.255|美国|0|华盛顿|西雅图|Level3
|
||||
8.45.162.0|8.45.162.255|美国|0|马萨诸塞|0|Level3
|
||||
8.45.163.0|8.45.164.255|美国|0|俄勒冈|0|Level3
|
||||
8.45.165.0|8.45.165.255|美国|0|华盛顿|0|Level3
|
||||
8.45.166.0|8.45.167.255|美国|0|华盛顿|西雅图|Level3
|
||||
8.45.168.0|8.127.255.255|美国|0|0|0|Level3
|
||||
8.128.0.0|8.128.3.255|中国|0|上海|上海市|阿里巴巴
|
||||
8.128.4.0|8.128.255.255|中国|0|0|0|阿里巴巴
|
||||
8.129.0.0|8.129.255.255|中国|0|广东省|深圳市|阿里云
|
||||
8.130.0.0|8.130.55.255|中国|0|北京|北京市|阿里云
|
||||
8.130.56.0|8.131.255.255|中国|0|北京|北京市|阿里巴巴
|
||||
8.132.0.0|8.133.255.255|中国|0|上海|上海市|阿里巴巴
|
||||
8.134.0.0|8.134.20.63|中国|0|0|0|阿里云
|
||||
8.134.20.64|8.134.79.255|中国|0|广东省|深圳市|阿里云
|
||||
8.134.80.0|8.191.255.255|中国|0|0|0|阿里巴巴
|
||||
8.192.0.0|8.192.0.255|美国|0|0|0|Level3
|
||||
8.192.1.0|8.192.1.255|美国|0|马萨诸塞|波士顿|Level3
|
||||
8.192.2.0|8.207.255.255|美国|0|0|0|Level3
|
||||
8.208.0.0|8.208.127.255|英国|0|伦敦|伦敦|阿里云
|
||||
8.208.128.0|8.208.255.255|英国|0|伦敦|伦敦|阿里巴巴
|
||||
8.209.0.0|8.209.15.255|俄罗斯|0|莫斯科|莫斯科|阿里云
|
||||
8.209.16.0|8.209.31.255|俄罗斯|0|莫斯科|莫斯科|阿里巴巴
|
||||
8.209.32.0|8.209.32.15|中国|0|台湾省|台北|阿里云
|
||||
8.209.32.16|8.209.32.255|中国|0|台湾省|台北|阿里巴巴
|
||||
8.209.33.0|8.209.34.255|中国|0|台湾省|台北|阿里云
|
||||
8.209.35.0|8.209.35.255|中国|0|台湾省|台北|阿里巴巴`)
|
||||
|
||||
var count int
|
||||
parser, err := iplibrary.NewReaderParser(buf, &iplibrary.ParserConfig{
|
||||
Template: template,
|
||||
EmptyValues: []string{"0"},
|
||||
Iterator: func(values map[string]string) error {
|
||||
count++
|
||||
t.Log(count, values)
|
||||
return nil
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = parser.Parse()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
44
pkg/iplibrary/parser_test.go
Normal file
44
pkg/iplibrary/parser_test.go
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewParser(t *testing.T) {
|
||||
template, err := iplibrary.NewTemplate("${ipFrom}|${ipTo}|${country}|${any}|${province}|${city}|${provider}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
parser, err := iplibrary.NewParser(&iplibrary.ParserConfig{
|
||||
Template: template,
|
||||
EmptyValues: []string{"0"},
|
||||
Iterator: func(values map[string]string) error {
|
||||
t.Log(values)
|
||||
return nil
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
parser.Write([]byte(`0.0.0.0|0.255.255.255|0|0|0|内网IP|内网IP
|
||||
1.0.0.0|1.0.0.255|澳大利亚|0|0|0|0
|
||||
1.0.1.0|1.0.3.255|中国|0|福建省|福州市|电信
|
||||
1.0.4.0|1.0.7.255|澳大利亚|0|维多利亚|墨尔本|0
|
||||
1.0.8.0|1.0.15.255|中国|0|广东省|广州市|电信
|
||||
1.0.16.0|1.0.31.255|日本|0|0|0|0
|
||||
1.0.32.0|1.0.63.255|中国|0|广东省|广州市|电信
|
||||
1.0.64.0|1.0.79.255|日本|0|广岛县|0|0
|
||||
1.0.80.0|1.0.127.255|日本|0|冈山县|0|0
|
||||
1.0.128.0|1.0.128.255|泰国|0|清莱府|0|TOT
|
||||
1.0.129.0|1.0.132.191|泰国|0|曼谷|曼谷|TOT`))
|
||||
|
||||
err = parser.Parse()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
233
pkg/iplibrary/reader.go
Normal file
233
pkg/iplibrary/reader.go
Normal file
@@ -0,0 +1,233 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"io"
|
||||
"net"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Reader struct {
|
||||
meta *Meta
|
||||
|
||||
regionMap map[string]*ipRegion
|
||||
|
||||
ipV4Items []*ipItem
|
||||
ipV6Items []*ipItem
|
||||
}
|
||||
|
||||
func NewReader(reader io.Reader) (*Reader, error) {
|
||||
var libReader = &Reader{
|
||||
regionMap: map[string]*ipRegion{},
|
||||
}
|
||||
err := libReader.load(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return libReader, nil
|
||||
}
|
||||
|
||||
func (this *Reader) load(reader io.Reader) error {
|
||||
var buf = make([]byte, 1024)
|
||||
var metaLine = []byte{}
|
||||
var metaLineFound = false
|
||||
var dataBuf = []byte{}
|
||||
for {
|
||||
n, err := reader.Read(buf)
|
||||
if n > 0 {
|
||||
var data = buf[:n]
|
||||
dataBuf = append(dataBuf, data...)
|
||||
if metaLineFound {
|
||||
left, err := this.parse(dataBuf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dataBuf = left
|
||||
} else {
|
||||
var index = bytes.IndexByte(dataBuf, '\n')
|
||||
if index > 0 {
|
||||
metaLine = dataBuf[:index]
|
||||
dataBuf = dataBuf[index+1:]
|
||||
metaLineFound = true
|
||||
var meta = &Meta{}
|
||||
err = json.Unmarshal(metaLine, &meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
meta.Init()
|
||||
this.meta = meta
|
||||
|
||||
left, err := this.parse(dataBuf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dataBuf = left
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(this.ipV4Items, func(i, j int) bool {
|
||||
var from0 = this.ipV4Items[i].IPFrom
|
||||
var to0 = this.ipV4Items[i].IPTo
|
||||
var from1 = this.ipV4Items[j].IPFrom
|
||||
var to1 = this.ipV4Items[j].IPTo
|
||||
if from0 == from1 {
|
||||
return to0 < to1
|
||||
}
|
||||
return from0 < from1
|
||||
})
|
||||
|
||||
sort.Slice(this.ipV6Items, func(i, j int) bool {
|
||||
var from0 = this.ipV6Items[i].IPFrom
|
||||
var to0 = this.ipV6Items[i].IPTo
|
||||
var from1 = this.ipV6Items[j].IPFrom
|
||||
var to1 = this.ipV6Items[j].IPTo
|
||||
if from0 == from1 {
|
||||
return to0 < to1
|
||||
}
|
||||
return from0 < from1
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *Reader) Lookup(ip net.IP) *QueryResult {
|
||||
if ip == nil {
|
||||
return &QueryResult{}
|
||||
}
|
||||
|
||||
var ipLong = configutils.IP2Long(ip)
|
||||
var isV4 = configutils.IsIPv4(ip)
|
||||
var resultItem *ipItem
|
||||
if isV4 {
|
||||
sort.Search(len(this.ipV4Items), func(i int) bool {
|
||||
var item = this.ipV4Items[i]
|
||||
if item.IPFrom <= ipLong {
|
||||
if item.IPTo >= ipLong {
|
||||
resultItem = item
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
} else {
|
||||
sort.Search(len(this.ipV6Items), func(i int) bool {
|
||||
var item = this.ipV6Items[i]
|
||||
if item.IPFrom <= ipLong {
|
||||
if item.IPTo >= ipLong {
|
||||
resultItem = item
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
return &QueryResult{
|
||||
item: resultItem,
|
||||
meta: this.meta,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Reader) Meta() *Meta {
|
||||
return this.meta
|
||||
}
|
||||
|
||||
func (this *Reader) IPv4Items() []*ipItem {
|
||||
return this.ipV4Items
|
||||
}
|
||||
|
||||
func (this *Reader) IPv6Items() []*ipItem {
|
||||
return this.ipV6Items
|
||||
}
|
||||
|
||||
func (this *Reader) parse(data []byte) (left []byte, err error) {
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
for {
|
||||
var index = bytes.IndexByte(data, '\n')
|
||||
if index >= 0 {
|
||||
var line = data[:index]
|
||||
var pieces = strings.Split(string(line), "|")
|
||||
if len(pieces) != 8 {
|
||||
return nil, errors.New("invalid ip definition '" + string(line) + "'")
|
||||
}
|
||||
|
||||
var version = pieces[0]
|
||||
if len(version) == 0 {
|
||||
version = "4"
|
||||
}
|
||||
|
||||
if version != "4" && version != "6" {
|
||||
return nil, errors.New("invalid ip version '" + string(line) + "'")
|
||||
}
|
||||
|
||||
var ipFrom uint64
|
||||
var ipTo uint64
|
||||
if len(pieces[2]) == 0 {
|
||||
pieces[2] = pieces[1]
|
||||
ipFrom = types.Uint64(pieces[1])
|
||||
ipTo = types.Uint64(pieces[2])
|
||||
} else {
|
||||
ipFrom = types.Uint64(pieces[1])
|
||||
ipTo = types.Uint64(pieces[2]) + ipFrom
|
||||
}
|
||||
|
||||
var countryId = types.Uint32(pieces[3])
|
||||
var provinceId = types.Uint32(pieces[4])
|
||||
var cityId = types.Uint32(pieces[5])
|
||||
var townId = types.Uint32(pieces[6])
|
||||
var providerId = types.Uint32(pieces[7])
|
||||
var hash = HashRegion(countryId, provinceId, cityId, townId, providerId)
|
||||
|
||||
region, ok := this.regionMap[hash]
|
||||
if !ok {
|
||||
region = &ipRegion{
|
||||
CountryId: countryId,
|
||||
ProvinceId: provinceId,
|
||||
CityId: cityId,
|
||||
TownId: townId,
|
||||
ProviderId: providerId,
|
||||
}
|
||||
this.regionMap[hash] = region
|
||||
}
|
||||
|
||||
if version == "4" {
|
||||
this.ipV4Items = append(this.ipV4Items, &ipItem{
|
||||
IPFrom: ipFrom,
|
||||
IPTo: ipTo,
|
||||
Region: region,
|
||||
})
|
||||
} else {
|
||||
this.ipV6Items = append(this.ipV6Items, &ipItem{
|
||||
IPFrom: ipFrom,
|
||||
IPTo: ipTo,
|
||||
Region: region,
|
||||
})
|
||||
}
|
||||
|
||||
data = data[index+1:]
|
||||
} else {
|
||||
left = data
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
55
pkg/iplibrary/reader_file.go
Normal file
55
pkg/iplibrary/reader_file.go
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
type FileReader struct {
|
||||
rawReader *Reader
|
||||
}
|
||||
|
||||
func NewFileReader(path string) (*FileReader, error) {
|
||||
fp, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
_ = fp.Close()
|
||||
}()
|
||||
|
||||
return NewFileDataReader(fp)
|
||||
}
|
||||
|
||||
func NewFileDataReader(dataReader io.Reader) (*FileReader, error) {
|
||||
gzReader, err := gzip.NewReader(dataReader)
|
||||
if err != nil {
|
||||
return nil, errors.New("create gzip reader failed: " + err.Error())
|
||||
}
|
||||
|
||||
reader, err := NewReader(gzReader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &FileReader{
|
||||
rawReader: reader,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (this *FileReader) Meta() *Meta {
|
||||
return this.rawReader.meta
|
||||
}
|
||||
|
||||
func (this *FileReader) Lookup(ip net.IP) *QueryResult {
|
||||
return this.rawReader.Lookup(ip)
|
||||
}
|
||||
|
||||
func (this *FileReader) RawReader() *Reader {
|
||||
return this.rawReader
|
||||
}
|
||||
51
pkg/iplibrary/reader_file_test.go
Normal file
51
pkg/iplibrary/reader_file_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewFileReader(t *testing.T) {
|
||||
reader, err := iplibrary.NewFileReader("./ip")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, ip := range []string{
|
||||
"127.0.0.1",
|
||||
"192.168.0.1",
|
||||
"192.168.0.150",
|
||||
"8.8.8.8",
|
||||
"111.197.165.199",
|
||||
"175.178.206.125",
|
||||
} {
|
||||
var result = reader.Lookup(net.ParseIP(ip))
|
||||
if result.IsOk() {
|
||||
var data = maps.Map{
|
||||
"countryId": result.CountryId(),
|
||||
"countryName": result.CountryName(),
|
||||
"provinceId": result.ProvinceId(),
|
||||
"provinceName": result.ProvinceName(),
|
||||
"cityId": result.CityId(),
|
||||
"cityName": result.CityName(),
|
||||
"townId": result.TownId(),
|
||||
"townName": result.TownName(),
|
||||
"providerId": result.ProviderId(),
|
||||
"providerName": result.ProviderName(),
|
||||
"summary": result.Summary(),
|
||||
}
|
||||
dataJSON, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(ip, "=>", string(dataJSON))
|
||||
} else {
|
||||
t.Log(ip+":", "not found")
|
||||
}
|
||||
}
|
||||
}
|
||||
191
pkg/iplibrary/reader_result.go
Normal file
191
pkg/iplibrary/reader_result.go
Normal file
@@ -0,0 +1,191 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type QueryResult struct {
|
||||
item *ipItem
|
||||
meta *Meta
|
||||
}
|
||||
|
||||
func (this *QueryResult) IsOk() bool {
|
||||
return this.item != nil
|
||||
}
|
||||
|
||||
func (this *QueryResult) CountryId() int64 {
|
||||
if this.item != nil {
|
||||
return int64(this.item.Region.CountryId)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (this *QueryResult) CountryName() string {
|
||||
if this.item == nil {
|
||||
return ""
|
||||
}
|
||||
if this.item.Region.CountryId > 0 {
|
||||
var country = this.meta.CountryWithId(this.item.Region.CountryId)
|
||||
if country != nil {
|
||||
return country.Name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (this *QueryResult) CountryCodes() []string {
|
||||
if this.item == nil {
|
||||
return nil
|
||||
}
|
||||
if this.item.Region.CountryId > 0 {
|
||||
var country = this.meta.CountryWithId(this.item.Region.CountryId)
|
||||
if country != nil {
|
||||
return country.Codes
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *QueryResult) ProvinceId() int64 {
|
||||
if this.item != nil {
|
||||
return int64(this.item.Region.ProvinceId)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (this *QueryResult) ProvinceName() string {
|
||||
if this.item == nil {
|
||||
return ""
|
||||
}
|
||||
if this.item.Region.ProvinceId > 0 {
|
||||
var province = this.meta.ProvinceWithId(this.item.Region.ProvinceId)
|
||||
if province != nil {
|
||||
return province.Name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (this *QueryResult) ProvinceCodes() []string {
|
||||
if this.item == nil {
|
||||
return nil
|
||||
}
|
||||
if this.item.Region.ProvinceId > 0 {
|
||||
var province = this.meta.ProvinceWithId(this.item.Region.ProvinceId)
|
||||
if province != nil {
|
||||
return province.Codes
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *QueryResult) CityId() int64 {
|
||||
if this.item != nil {
|
||||
return int64(this.item.Region.CityId)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (this *QueryResult) CityName() string {
|
||||
if this.item == nil {
|
||||
return ""
|
||||
}
|
||||
if this.item.Region.CityId > 0 {
|
||||
var city = this.meta.CityWithId(this.item.Region.CityId)
|
||||
if city != nil {
|
||||
return city.Name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (this *QueryResult) TownId() int64 {
|
||||
if this.item != nil {
|
||||
return int64(this.item.Region.TownId)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (this *QueryResult) TownName() string {
|
||||
if this.item == nil {
|
||||
return ""
|
||||
}
|
||||
if this.item.Region.TownId > 0 {
|
||||
var town = this.meta.TownWithId(this.item.Region.TownId)
|
||||
if town != nil {
|
||||
return town.Name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (this *QueryResult) ProviderId() int64 {
|
||||
if this.item != nil {
|
||||
return int64(this.item.Region.ProviderId)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (this *QueryResult) ProviderName() string {
|
||||
if this.item == nil {
|
||||
return ""
|
||||
}
|
||||
if this.item.Region.ProviderId > 0 {
|
||||
var provider = this.meta.ProviderWithId(this.item.Region.ProviderId)
|
||||
if provider != nil {
|
||||
return provider.Name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (this *QueryResult) ProviderCodes() []string {
|
||||
if this.item == nil {
|
||||
return nil
|
||||
}
|
||||
if this.item.Region.ProviderId > 0 {
|
||||
var provider = this.meta.ProviderWithId(this.item.Region.ProviderId)
|
||||
if provider != nil {
|
||||
return provider.Codes
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *QueryResult) Summary() string {
|
||||
if this.item == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
var pieces = []string{}
|
||||
var countryName = this.CountryName()
|
||||
var provinceName = this.ProvinceName()
|
||||
var cityName = this.CityName()
|
||||
var townName = this.TownName()
|
||||
var providerName = this.ProviderName()
|
||||
|
||||
if len(countryName) > 0 {
|
||||
pieces = append(pieces, countryName)
|
||||
}
|
||||
if len(provinceName) > 0 && !lists.ContainsString(pieces, provinceName) {
|
||||
pieces = append(pieces, provinceName)
|
||||
}
|
||||
if len(cityName) > 0 && !lists.ContainsString(pieces, cityName) && !lists.ContainsString(pieces, strings.TrimSuffix(cityName, "市")) {
|
||||
pieces = append(pieces, cityName)
|
||||
}
|
||||
if len(townName) > 0 && !lists.ContainsString(pieces, townName) && !lists.ContainsString(pieces, strings.TrimSuffix(townName, "县")) {
|
||||
pieces = append(pieces, cityName)
|
||||
}
|
||||
|
||||
if len(providerName) > 0 && !lists.ContainsString(pieces, providerName) {
|
||||
if len(pieces) > 0 {
|
||||
pieces = append(pieces, "|")
|
||||
}
|
||||
pieces = append(pieces, providerName)
|
||||
}
|
||||
|
||||
return strings.Join(pieces, " ")
|
||||
}
|
||||
149
pkg/iplibrary/reader_test.go
Normal file
149
pkg/iplibrary/reader_test.go
Normal file
@@ -0,0 +1,149 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"net"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNewReader(t *testing.T) {
|
||||
var buf = &bytes.Buffer{}
|
||||
var writer = iplibrary.NewWriter(buf, &iplibrary.Meta{
|
||||
Author: "GoEdge <https://goedge.cn>",
|
||||
})
|
||||
|
||||
err := writer.WriteMeta()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = writer.Write("192.168.1.100", "192.168.1.100", 1, 200, 300, 400, 500)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = writer.Write("192.168.2.100", "192.168.3.100", 2, 201, 301, 401, 501)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = writer.Write("192.168.3.101", "192.168.3.101", 3, 201, 301, 401, 501)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = writer.Write("192.168.0.101", "192.168.0.200", 4, 201, 301, 401, 501)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = writer.Write("::1", "::5", 5, 201, 301, 401, 501)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
/**var n = func() string {
|
||||
return types.String(rands.Int(0, 255))
|
||||
}
|
||||
|
||||
for i := 0; i < 1_000_000; i++ {
|
||||
err = writer.Write(n()+"."+n()+"."+n()+"."+n(), n()+"."+n()+"."+n()+"."+n(), int64(i)+100, 201, 301, 401, 501)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}**/
|
||||
|
||||
var stat = &runtime.MemStats{}
|
||||
runtime.ReadMemStats(stat)
|
||||
reader, err := iplibrary.NewReader(buf)
|
||||
|
||||
var stat2 = &runtime.MemStats{}
|
||||
runtime.ReadMemStats(stat2)
|
||||
t.Log((stat2.Alloc-stat.Alloc)/1024/1024, "M")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("version:", reader.Meta().Version, "author:", reader.Meta().Author, "createdTime:", timeutil.FormatTime("Y-m-d H:i:s", reader.Meta().CreatedAt))
|
||||
|
||||
if len(reader.IPv4Items()) < 10 {
|
||||
t.Log("===")
|
||||
for _, item := range reader.IPv4Items() {
|
||||
t.Logf("%+v", item)
|
||||
}
|
||||
t.Log("===")
|
||||
}
|
||||
if len(reader.IPv6Items()) < 10 {
|
||||
t.Log("===")
|
||||
for _, item := range reader.IPv6Items() {
|
||||
t.Logf("%+v", item)
|
||||
}
|
||||
t.Log("===")
|
||||
}
|
||||
|
||||
var before = time.Now()
|
||||
for _, ip := range []string{
|
||||
"192.168.0.1",
|
||||
"192.168.0.150",
|
||||
"192.168.1.100",
|
||||
"192.168.2.100",
|
||||
"192.168.3.50",
|
||||
"192.168.0.150",
|
||||
"192.168.4.80",
|
||||
"::3",
|
||||
"::8",
|
||||
} {
|
||||
var result = reader.Lookup(net.ParseIP(ip))
|
||||
if result.IsOk() {
|
||||
t.Log(ip+":", "countryId:", result.CountryId())
|
||||
} else {
|
||||
t.Log(ip+":", "not found")
|
||||
}
|
||||
}
|
||||
t.Log(time.Since(before).Seconds()*1000, "ms")
|
||||
}
|
||||
|
||||
func BenchmarkNewReader(b *testing.B) {
|
||||
runtime.GOMAXPROCS(1)
|
||||
|
||||
var buf = &bytes.Buffer{}
|
||||
var writer = iplibrary.NewWriter(buf, &iplibrary.Meta{
|
||||
Author: "GoEdge <https://goedge.cn>",
|
||||
})
|
||||
|
||||
err := writer.WriteMeta()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
var n = func() string {
|
||||
return types.String(rands.Int(0, 255))
|
||||
}
|
||||
|
||||
for i := 0; i < 1_000_000; i++ {
|
||||
err = writer.Write(n()+"."+n()+"."+n()+"."+n(), n()+"."+n()+"."+n()+"."+n(), int64(i)+100, 201, 301, 401, 501)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
reader, err := iplibrary.NewReader(buf)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
var ip = "192.168.1.100"
|
||||
reader.Lookup(net.ParseIP(ip))
|
||||
}
|
||||
}
|
||||
75
pkg/iplibrary/template.go
Normal file
75
pkg/iplibrary/template.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type Template struct {
|
||||
templateString string
|
||||
reg *regexp.Regexp
|
||||
}
|
||||
|
||||
func NewTemplate(templateString string) (*Template, error) {
|
||||
var t = &Template{
|
||||
templateString: templateString,
|
||||
}
|
||||
err := t.init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (this *Template) init() error {
|
||||
var template = regexp.QuoteMeta(this.templateString)
|
||||
var keywordReg = regexp.MustCompile(`\\\$\\{(\w+)\\}`)
|
||||
template = keywordReg.ReplaceAllStringFunc(template, func(keyword string) string {
|
||||
var matches = keywordReg.FindStringSubmatch(keyword)
|
||||
if len(matches) > 1 {
|
||||
switch matches[1] {
|
||||
case "ipFrom", "ipTo", "country", "province", "city", "town", "provider":
|
||||
return "(?P<" + matches[1] + ">.*)"
|
||||
}
|
||||
return ".*"
|
||||
}
|
||||
|
||||
return keyword
|
||||
})
|
||||
reg, err := regexp.Compile(template)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
this.reg = reg
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *Template) Extract(text string, emptyValues []string) (values map[string]string, ok bool) {
|
||||
var matches = this.reg.FindStringSubmatch(text)
|
||||
if len(matches) == 0 {
|
||||
return
|
||||
}
|
||||
values = map[string]string{}
|
||||
for index, name := range this.reg.SubexpNames() {
|
||||
if len(name) == 0 {
|
||||
continue
|
||||
}
|
||||
var v = matches[index]
|
||||
if name != "ipFrom" && name != "ipTo" && (v == "0" || v == "无" || v == "空" || lists.ContainsString(emptyValues, v)) {
|
||||
v = ""
|
||||
}
|
||||
values[name] = v
|
||||
}
|
||||
|
||||
for _, keyword := range []string{"ipFrom", "ipTo", "country", "province", "city", "town", "provider"} {
|
||||
_, hasKeyword := values[keyword]
|
||||
if !hasKeyword {
|
||||
values[keyword] = ""
|
||||
}
|
||||
}
|
||||
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
25
pkg/iplibrary/template_test.go
Normal file
25
pkg/iplibrary/template_test.go
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewTemplate(t *testing.T) {
|
||||
template, err := iplibrary.NewTemplate("${ipFrom}|${ipTo}|${country}|${any}|${province}|${city}|${provider}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, s := range []string{
|
||||
"42.0.32.0|42.0.63.255|中国|0|广东省|广州市|电信",
|
||||
"42.0.32.0|42.0.63.255|中国|0|广东省|广州市|电信\n123",
|
||||
"42.0.32.0|42.0.63.255|中国||广东省|广州市|电信",
|
||||
"42.0.32.0|42.0.63.255|中国|0||广州市|电信",
|
||||
"42.0.32.0|42.0.63.255|中国|0|广东省|广州市",
|
||||
} {
|
||||
values, _ := template.Extract(s, []string{})
|
||||
t.Log(s, "=>\n", values)
|
||||
}
|
||||
}
|
||||
268
pkg/iplibrary/updater.go
Normal file
268
pkg/iplibrary/updater.go
Normal file
@@ -0,0 +1,268 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type UpdaterSource interface {
|
||||
// DataDir 文件目录
|
||||
DataDir() string
|
||||
|
||||
// FindLatestFile 检查最新的IP库文件
|
||||
FindLatestFile() (code string, fileId int64, err error)
|
||||
|
||||
// DownloadFile 下载文件
|
||||
DownloadFile(fileId int64, writer io.Writer) error
|
||||
|
||||
// LogInfo 普通日志
|
||||
LogInfo(message string)
|
||||
|
||||
// LogError 错误日志
|
||||
LogError(err error)
|
||||
}
|
||||
|
||||
type Updater struct {
|
||||
source UpdaterSource
|
||||
|
||||
currentCode string
|
||||
ticker *time.Ticker
|
||||
|
||||
isUpdating bool
|
||||
}
|
||||
|
||||
func NewUpdater(source UpdaterSource, interval time.Duration) *Updater {
|
||||
return &Updater{
|
||||
source: source,
|
||||
ticker: time.NewTicker(interval),
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Updater) Start() {
|
||||
// 初始化
|
||||
err := this.Init()
|
||||
if err != nil {
|
||||
this.source.LogError(err)
|
||||
}
|
||||
|
||||
// 先运行一次
|
||||
err = this.Loop()
|
||||
if err != nil {
|
||||
this.source.LogError(err)
|
||||
}
|
||||
|
||||
// 开始定时运行
|
||||
for range this.ticker.C {
|
||||
err = this.Loop()
|
||||
if err != nil {
|
||||
this.source.LogError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Updater) Init() error {
|
||||
// 检查当前正在使用的IP库
|
||||
var path = this.source.DataDir() + "/ip-library.db"
|
||||
fp, err := os.Open(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("read ip library file failed '" + err.Error() + "'")
|
||||
}
|
||||
defer func() {
|
||||
_ = fp.Close()
|
||||
}()
|
||||
|
||||
return this.loadFile(fp)
|
||||
}
|
||||
|
||||
func (this *Updater) Loop() error {
|
||||
if this.isUpdating {
|
||||
return nil
|
||||
}
|
||||
|
||||
this.isUpdating = true
|
||||
|
||||
defer func() {
|
||||
this.isUpdating = false
|
||||
}()
|
||||
|
||||
code, fileId, err := this.source.FindLatestFile()
|
||||
if err != nil {
|
||||
// 不提示连接错误
|
||||
if this.isConnError(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
if len(code) == 0 || fileId <= 0 {
|
||||
// 还原到内置IP库
|
||||
if len(this.currentCode) > 0 {
|
||||
this.currentCode = ""
|
||||
this.source.LogInfo("resetting to default ip library ...")
|
||||
|
||||
var defaultPath = this.source.DataDir() + "/ip-library.db"
|
||||
_, err = os.Stat(defaultPath)
|
||||
if err == nil {
|
||||
err = os.Remove(defaultPath)
|
||||
if err != nil {
|
||||
this.source.LogError(errors.New("can not remove default 'ip-library.db'"))
|
||||
}
|
||||
}
|
||||
|
||||
err = InitDefault()
|
||||
if err != nil {
|
||||
this.source.LogError(errors.New("initialize default ip library failed: " + err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 下载
|
||||
if this.currentCode == code {
|
||||
// 不再重复下载
|
||||
return nil
|
||||
}
|
||||
|
||||
// 检查是否存在
|
||||
var dir = this.source.DataDir()
|
||||
var path = dir + "/ip-" + code + ".db"
|
||||
stat, err := os.Stat(path)
|
||||
if err == nil && !stat.IsDir() && stat.Size() > 0 {
|
||||
fp, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = fp.Close()
|
||||
}()
|
||||
|
||||
err = this.loadFile(fp)
|
||||
if err != nil {
|
||||
// 尝试删除
|
||||
_ = os.Remove(path)
|
||||
} else {
|
||||
this.currentCode = code
|
||||
|
||||
// 拷贝到 ip-library.db
|
||||
err = this.createDefaultFile(path, dir)
|
||||
if err != nil {
|
||||
this.source.LogError(err)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// write to file
|
||||
fp, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
|
||||
if err != nil {
|
||||
return errors.New("create ip library file failed: " + err.Error())
|
||||
}
|
||||
|
||||
var isOk = false
|
||||
defer func() {
|
||||
if !isOk {
|
||||
_ = os.Remove(path)
|
||||
}
|
||||
}()
|
||||
|
||||
err = this.source.DownloadFile(fileId, fp)
|
||||
if err != nil {
|
||||
_ = fp.Close()
|
||||
return err
|
||||
}
|
||||
err = fp.Close()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// load library from file
|
||||
fp, err = os.Open(path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
err = this.loadFile(fp)
|
||||
_ = fp.Close()
|
||||
if err != nil {
|
||||
return errors.New("load file failed: " + err.Error())
|
||||
}
|
||||
|
||||
isOk = true
|
||||
this.currentCode = code
|
||||
|
||||
// 拷贝到 ip-library.db
|
||||
err = this.createDefaultFile(path, dir)
|
||||
if err != nil {
|
||||
this.source.LogError(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *Updater) loadFile(fp *os.File) error {
|
||||
this.source.LogInfo("load ip library from '" + fp.Name() + "' ...")
|
||||
|
||||
fileReader, err := NewFileDataReader(fp)
|
||||
if err != nil {
|
||||
return errors.New("load ip library from reader failed: " + err.Error())
|
||||
}
|
||||
|
||||
var reader = fileReader.RawReader()
|
||||
defaultLibrary = NewIPLibraryWithReader(reader)
|
||||
this.currentCode = reader.Meta().Code
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *Updater) createDefaultFile(sourcePath string, dir string) error {
|
||||
sourceFp, err := os.Open(sourcePath)
|
||||
if err != nil {
|
||||
return errors.New("prepare to copy file to 'ip-library.db' failed: " + err.Error())
|
||||
}
|
||||
defer func() {
|
||||
_ = sourceFp.Close()
|
||||
}()
|
||||
|
||||
dstFp, err := os.Create(dir + "/ip-library.db")
|
||||
if err != nil {
|
||||
return errors.New("prepare to copy file to 'ip-library.db' failed: " + err.Error())
|
||||
}
|
||||
defer func() {
|
||||
_ = dstFp.Close()
|
||||
}()
|
||||
_, err = io.Copy(dstFp, sourceFp)
|
||||
if err != nil {
|
||||
return errors.New("copy file to 'ip-library.db' failed: " + err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isConnError 是否为连接错误
|
||||
func (this *Updater) isConnError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查是否为连接错误
|
||||
statusErr, ok := status.FromError(err)
|
||||
if ok {
|
||||
var errorCode = statusErr.Code()
|
||||
return errorCode == codes.Unavailable || errorCode == codes.Canceled
|
||||
}
|
||||
|
||||
if strings.Contains(err.Error(), "code = Canceled") {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
53
pkg/iplibrary/updater_test.go
Normal file
53
pkg/iplibrary/updater_test.go
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
"io"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type updaterSource struct {
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func (this *updaterSource) DataDir() string {
|
||||
return Tea.Root + "/data"
|
||||
}
|
||||
|
||||
func (this *updaterSource) FindLatestFile() (code string, fileId int64, err error) {
|
||||
return "CODE", 1, nil
|
||||
}
|
||||
|
||||
func (this *updaterSource) DownloadFile(fileId int64, writer io.Writer) error {
|
||||
this.t.Log("downloading file:", fileId, "writer:", writer)
|
||||
_, err := writer.Write(iplibrary.DefaultIPLibraryData())
|
||||
return err
|
||||
}
|
||||
|
||||
func (this *updaterSource) LogInfo(message string) {
|
||||
this.t.Log(message)
|
||||
}
|
||||
|
||||
func (this *updaterSource) LogError(err error) {
|
||||
this.t.Fatal(err)
|
||||
}
|
||||
|
||||
func TestNewUpdater(t *testing.T) {
|
||||
var updater = iplibrary.NewUpdater(&updaterSource{
|
||||
t: t,
|
||||
}, 1*time.Minute)
|
||||
err := updater.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = updater.Loop()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
9
pkg/iplibrary/version.go
Normal file
9
pkg/iplibrary/version.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary
|
||||
|
||||
type Version = int
|
||||
|
||||
const (
|
||||
Version1 Version = 1
|
||||
)
|
||||
161
pkg/iplibrary/writer.go
Normal file
161
pkg/iplibrary/writer.go
Normal file
@@ -0,0 +1,161 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"hash"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type hashWriter struct {
|
||||
rawWriter io.Writer
|
||||
hash hash.Hash
|
||||
}
|
||||
|
||||
func newHashWriter(writer io.Writer) *hashWriter {
|
||||
return &hashWriter{
|
||||
rawWriter: writer,
|
||||
hash: md5.New(),
|
||||
}
|
||||
}
|
||||
|
||||
func (this *hashWriter) Write(p []byte) (n int, err error) {
|
||||
n, err = this.rawWriter.Write(p)
|
||||
this.hash.Write(p)
|
||||
return
|
||||
}
|
||||
|
||||
func (this *hashWriter) Sum() string {
|
||||
return fmt.Sprintf("%x", this.hash.Sum(nil))
|
||||
}
|
||||
|
||||
type Writer struct {
|
||||
writer *hashWriter
|
||||
meta *Meta
|
||||
}
|
||||
|
||||
func NewWriter(writer io.Writer, meta *Meta) *Writer {
|
||||
if meta == nil {
|
||||
meta = &Meta{}
|
||||
}
|
||||
meta.Version = Version1
|
||||
meta.CreatedAt = time.Now().Unix()
|
||||
|
||||
var libWriter = &Writer{
|
||||
writer: newHashWriter(writer),
|
||||
meta: meta,
|
||||
}
|
||||
return libWriter
|
||||
}
|
||||
|
||||
func (this *Writer) WriteMeta() error {
|
||||
metaJSON, err := json.Marshal(this.meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = this.writer.Write(metaJSON)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = this.writer.Write([]byte("\n"))
|
||||
return err
|
||||
}
|
||||
|
||||
func (this *Writer) Write(ipFrom string, ipTo string, countryId int64, provinceId int64, cityId int64, townId int64, providerId int64) error {
|
||||
// validate IP
|
||||
var fromIP = net.ParseIP(ipFrom)
|
||||
if fromIP == nil {
|
||||
return errors.New("invalid 'ipFrom': '" + ipFrom + "'")
|
||||
}
|
||||
var fromIsIPv4 = configutils.IsIPv4(fromIP)
|
||||
var toIP = net.ParseIP(ipTo)
|
||||
if toIP == nil {
|
||||
return errors.New("invalid 'ipTo': " + ipTo)
|
||||
}
|
||||
var toIsIPv4 = configutils.IsIPv4(toIP)
|
||||
if fromIsIPv4 != toIsIPv4 {
|
||||
return errors.New("'ipFrom(" + ipFrom + ")' and 'ipTo(" + ipTo + ")' should have the same IP version")
|
||||
}
|
||||
|
||||
var pieces = []string{}
|
||||
|
||||
// 0
|
||||
if fromIsIPv4 {
|
||||
pieces = append(pieces, "")
|
||||
} else {
|
||||
pieces = append(pieces, "6")
|
||||
}
|
||||
|
||||
// 1
|
||||
var fromIPLong = configutils.IP2Long(fromIP)
|
||||
var toIPLong = configutils.IP2Long(toIP)
|
||||
|
||||
if toIPLong < fromIPLong {
|
||||
fromIPLong, toIPLong = toIPLong, fromIPLong
|
||||
}
|
||||
|
||||
pieces = append(pieces, types.String(fromIPLong))
|
||||
if ipFrom == ipTo {
|
||||
// 2
|
||||
pieces = append(pieces, "")
|
||||
} else {
|
||||
// 2
|
||||
pieces = append(pieces, types.String(toIPLong-fromIPLong))
|
||||
}
|
||||
|
||||
// 3
|
||||
if countryId > 0 {
|
||||
pieces = append(pieces, types.String(countryId))
|
||||
} else {
|
||||
pieces = append(pieces, "")
|
||||
}
|
||||
|
||||
// 4
|
||||
if provinceId > 0 {
|
||||
pieces = append(pieces, types.String(provinceId))
|
||||
} else {
|
||||
pieces = append(pieces, "")
|
||||
}
|
||||
|
||||
// 5
|
||||
if cityId > 0 {
|
||||
pieces = append(pieces, types.String(cityId))
|
||||
} else {
|
||||
pieces = append(pieces, "")
|
||||
}
|
||||
|
||||
// 6
|
||||
if townId > 0 {
|
||||
pieces = append(pieces, types.String(townId))
|
||||
} else {
|
||||
pieces = append(pieces, "")
|
||||
}
|
||||
|
||||
// 7
|
||||
if providerId > 0 {
|
||||
pieces = append(pieces, types.String(providerId))
|
||||
} else {
|
||||
pieces = append(pieces, "")
|
||||
}
|
||||
|
||||
_, err := this.writer.Write([]byte(strings.Join(pieces, "|")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = this.writer.Write([]byte("\n"))
|
||||
return err
|
||||
}
|
||||
|
||||
func (this *Writer) Sum() string {
|
||||
return this.writer.Sum()
|
||||
}
|
||||
58
pkg/iplibrary/writer_file.go
Normal file
58
pkg/iplibrary/writer_file.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"os"
|
||||
)
|
||||
|
||||
type FileWriter struct {
|
||||
fp *os.File
|
||||
gzWriter *gzip.Writer
|
||||
|
||||
rawWriter *Writer
|
||||
}
|
||||
|
||||
func NewFileWriter(path string, meta *Meta) (*FileWriter, error) {
|
||||
fp, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gzWriter, err := gzip.NewWriterLevel(fp, gzip.BestCompression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var writer = &FileWriter{
|
||||
fp: fp,
|
||||
gzWriter: gzWriter,
|
||||
rawWriter: NewWriter(gzWriter, meta),
|
||||
}
|
||||
return writer, nil
|
||||
}
|
||||
|
||||
func (this *FileWriter) WriteMeta() error {
|
||||
return this.rawWriter.WriteMeta()
|
||||
}
|
||||
|
||||
func (this *FileWriter) Write(ipFrom string, ipTo string, countryId int64, provinceId int64, cityId int64, townId int64, providerId int64) error {
|
||||
return this.rawWriter.Write(ipFrom, ipTo, countryId, provinceId, cityId, townId, providerId)
|
||||
}
|
||||
|
||||
func (this *FileWriter) Sum() string {
|
||||
return this.rawWriter.Sum()
|
||||
}
|
||||
|
||||
func (this *FileWriter) Close() error {
|
||||
err1 := this.gzWriter.Close()
|
||||
err2 := this.fp.Close()
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
return nil
|
||||
}
|
||||
56
pkg/iplibrary/writer_file_test.go
Normal file
56
pkg/iplibrary/writer_file_test.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewFileWriter(t *testing.T) {
|
||||
writer, err := iplibrary.NewFileWriter("./internal-ip-library.db", &iplibrary.Meta{
|
||||
Author: "GoEdge",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = writer.WriteMeta()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = writer.Write("192.168.1.100", "192.168.1.100", 100, 200, 300, 400, 500)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = writer.Write("192.168.2.100", "192.168.3.100", 101, 201, 301, 401, 501)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = writer.Write("192.168.3.101", "192.168.3.101", 101, 201, 301, 401, 501)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var n = func() string {
|
||||
return types.String(rands.Int(0, 255))
|
||||
}
|
||||
|
||||
for i := 0; i < 1_000_000; i++ {
|
||||
err = writer.Write(n()+"."+n()+"."+n()+"."+n(), n()+"."+n()+"."+n()+"."+n(), int64(i)+100, 201, 301, 401, 501)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("ok", writer.Sum())
|
||||
}
|
||||
44
pkg/iplibrary/writer_test.go
Normal file
44
pkg/iplibrary/writer_test.go
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package iplibrary_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewWriter(t *testing.T) {
|
||||
var buf = &bytes.Buffer{}
|
||||
var writer = iplibrary.NewWriter(buf, &iplibrary.Meta{
|
||||
Author: "GoEdge <https://goedge.cn>",
|
||||
})
|
||||
|
||||
err := writer.WriteMeta()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = writer.Write("192.168.1.100", "192.168.1.100", 100, 200, 300, 400, 500)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = writer.Write("192.168.2.100", "192.168.3.100", 101, 201, 301, 401, 501)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = writer.Write("192.168.3.101", "192.168.3.101", 101, 201, 301, 401, 501)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = writer.Write("::1", "::2", 101, 201, 301, 401, 501)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(buf.String())
|
||||
t.Log("sum:", writer.Sum())
|
||||
}
|
||||
@@ -7,19 +7,19 @@ const (
|
||||
MessageCodeWriteCache MessageCode = "writeCache" // 写入缓存
|
||||
MessageCodeReadCache MessageCode = "readCache" // 读取缓存
|
||||
MessageCodeStatCache MessageCode = "statCache" // 统计缓存
|
||||
MessageCodePurgeCache MessageCode = "purgeCache" // 删除缓存
|
||||
MessageCodeCleanCache MessageCode = "cleanCache" // 清理缓存
|
||||
MessageCodePreheatCache MessageCode = "preheatCache" // 预热缓存
|
||||
MessageCodeCheckSystemdService MessageCode = "checkSystemdService" // 检查Systemd服务
|
||||
MessageCodeNewNodeTask MessageCode = "NewNodeTask" // 有新的节点任务产生
|
||||
MessageCodeCheckLocalFirewall MessageCode = "checkLocalFirewall" // 检查本地防火墙
|
||||
MessageCodeNewNodeTask MessageCode = "newNodeTask" // 有新的节点任务产生
|
||||
MessageCodeChangeAPINode MessageCode = "changeAPINode" // 改变新的API节点
|
||||
)
|
||||
|
||||
// 连接API节点成功
|
||||
// ConnectedAPINodeMessage 连接API节点成功
|
||||
type ConnectedAPINodeMessage struct {
|
||||
APINodeId int64 `json:"apiNodeId"`
|
||||
}
|
||||
|
||||
// 写入缓存
|
||||
// WriteCacheMessage 写入缓存
|
||||
type WriteCacheMessage struct {
|
||||
CachePolicyJSON []byte `json:"cachePolicyJSON"`
|
||||
Key string `json:"key"`
|
||||
@@ -27,13 +27,13 @@ type WriteCacheMessage struct {
|
||||
LifeSeconds int64 `json:"lifeSeconds"`
|
||||
}
|
||||
|
||||
// 读取缓存
|
||||
// ReadCacheMessage 读取缓存
|
||||
type ReadCacheMessage struct {
|
||||
CachePolicyJSON []byte `json:"cachePolicyJSON"`
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
// 统计缓存
|
||||
// StatCacheMessage 统计缓存
|
||||
type StatCacheMessage struct {
|
||||
CachePolicyJSON []byte `json:"cachePolicyJSON"`
|
||||
}
|
||||
@@ -44,30 +44,20 @@ type CleanCacheMessage struct {
|
||||
CachePolicyJSON []byte `json:"cachePolicyJSON"`
|
||||
}
|
||||
|
||||
// 删除缓存
|
||||
type PurgeCacheMessageType = string
|
||||
|
||||
const (
|
||||
PurgeCacheMessageTypeFile PurgeCacheMessageType = "file"
|
||||
PurgeCacheMessageTypeDir PurgeCacheMessageType = "dir"
|
||||
)
|
||||
|
||||
type PurgeCacheMessage struct {
|
||||
CachePolicyJSON []byte `json:"cachePolicyJSON"`
|
||||
Keys []string `json:"keys"`
|
||||
Type PurgeCacheMessageType `json:"type"` // 清理类型
|
||||
}
|
||||
|
||||
// 预热缓存
|
||||
type PreheatCacheMessage struct {
|
||||
CachePolicyJSON []byte `json:"cachePolicyJSON"`
|
||||
Keys []string `json:"keys"`
|
||||
}
|
||||
|
||||
// Systemd服务
|
||||
// CheckSystemdServiceMessage Systemd服务
|
||||
type CheckSystemdServiceMessage struct {
|
||||
}
|
||||
|
||||
// 有新的节点任务
|
||||
// CheckLocalFirewallMessage 检查本地防火墙
|
||||
type CheckLocalFirewallMessage struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// NewNodeTaskMessage 有新的节点任务
|
||||
type NewNodeTaskMessage struct {
|
||||
}
|
||||
|
||||
// ChangeAPINodeMessage 修改API地址
|
||||
type ChangeAPINodeMessage struct {
|
||||
Addr string `json:"addr"`
|
||||
}
|
||||
|
||||
23
pkg/messageconfigs/ns_messages.go
Normal file
23
pkg/messageconfigs/ns_messages.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package messageconfigs
|
||||
|
||||
type NSMessageCode = string
|
||||
|
||||
// NS节点相关消息
|
||||
const (
|
||||
NSMessageCodeConnectedAPINode NSMessageCode = "connectedAPINode" // NS节点连接API节点成功
|
||||
NSMessageCodeCheckSystemdService NSMessageCode = "checkSystemdService" // 检查Systemd服务
|
||||
NSMessageCodeNewNodeTask NSMessageCode = "newNodeTask" // 有新的节点任务产生
|
||||
)
|
||||
|
||||
// NSConnectedAPINodeMessage 连接API节点成功
|
||||
type NSConnectedAPINodeMessage struct {
|
||||
APINodeId int64 `json:"apiNodeId"`
|
||||
}
|
||||
|
||||
// NSCheckSystemdServiceMessage Systemd服务
|
||||
type NSCheckSystemdServiceMessage struct {
|
||||
}
|
||||
|
||||
// NewNSNodeTaskMessage 有新的节点任务
|
||||
type NewNSNodeTaskMessage struct {
|
||||
}
|
||||
8
pkg/monitorconfigs/rate_config.go
Normal file
8
pkg/monitorconfigs/rate_config.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package monitorconfigs
|
||||
|
||||
type RateConfig struct {
|
||||
Minutes int32 `json:"minutes"` // 周期分钟
|
||||
Count int32 `json:"count"` // 数量
|
||||
}
|
||||
20
pkg/nodeconfigs/clock_config.go
Normal file
20
pkg/nodeconfigs/clock_config.go
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package nodeconfigs
|
||||
|
||||
func DefaultClockConfig() *ClockConfig {
|
||||
return &ClockConfig{
|
||||
AutoSync: true,
|
||||
Server: "",
|
||||
}
|
||||
}
|
||||
|
||||
// ClockConfig 时钟相关配置
|
||||
type ClockConfig struct {
|
||||
AutoSync bool `yaml:"autoSync" json:"autoSync"` // 自动尝试同步时钟
|
||||
Server string `yaml:"server" json:"server"` // 时钟同步服务器
|
||||
}
|
||||
|
||||
func (this *ClockConfig) Init() error {
|
||||
return nil
|
||||
}
|
||||
11
pkg/nodeconfigs/connectivity.go
Normal file
11
pkg/nodeconfigs/connectivity.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package nodeconfigs
|
||||
|
||||
import "github.com/TeaOSLab/EdgeCommon/pkg/reporterconfigs"
|
||||
|
||||
// Connectivity 连通性状态
|
||||
type Connectivity struct {
|
||||
CostMs float64 `json:"costMs"` // 平均耗时
|
||||
Level reporterconfigs.ReportLevel `json:"level"` // 级别
|
||||
Percent float64 `json:"percent"` // 连通的百分比,是一个0到100之间的小数
|
||||
UpdatedAt int64 `json:"updatedAt"` // 更新时间
|
||||
}
|
||||
37
pkg/nodeconfigs/defaults.go
Normal file
37
pkg/nodeconfigs/defaults.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package nodeconfigs
|
||||
|
||||
import "github.com/iwind/TeaGo/maps"
|
||||
|
||||
// 一组系统默认值
|
||||
// 修改单个IP相关限制值时要考虑到NAT中每个IP会代表很多个主机,并非1对1的关系
|
||||
|
||||
const (
|
||||
DefaultMaxThreads = 20000 // 单节点最大线程数
|
||||
DefaultMaxThreadsMin = 1000 // 单节点最大线程数最小值
|
||||
DefaultMaxThreadsMax = 100_000 // 单节点最大线程数最大值
|
||||
|
||||
DefaultTCPMaxConnections = 100_000 // 单节点TCP最大连接数
|
||||
DefaultTCPMaxConnectionsPerIP = 1000 // 单IP最大连接数
|
||||
DefaultTCPMinConnectionsPerIP = 5 // 单IP最小连接数
|
||||
|
||||
DefaultTCPNewConnectionsMinutelyRate = 500 // 单IP连接速率限制(按分钟)
|
||||
DefaultTCPNewConnectionsMinMinutelyRate = 3 // 单IP最小连接速率
|
||||
|
||||
DefaultTCPNewConnectionsSecondlyRate = 300 // 单IP连接速率限制(按秒)
|
||||
DefaultTCPNewConnectionsMinSecondlyRate = 3 // 单IP最小连接速率
|
||||
|
||||
DefaultTCPLinger = 3 // 单节点TCP Linger值
|
||||
DefaultTLSHandshakeTimeout = 3 // TLS握手超时时间
|
||||
)
|
||||
|
||||
var DefaultConfigs = maps.Map{
|
||||
"tcpMaxConnections": DefaultTCPMaxConnections,
|
||||
"tcpMaxConnectionsPerIP": DefaultTCPMaxConnectionsPerIP,
|
||||
"tcpMinConnectionsPerIP": DefaultTCPMinConnectionsPerIP,
|
||||
"tcpNewConnectionsMinutelyRate": DefaultTCPNewConnectionsMinutelyRate,
|
||||
"tcpNewConnectionsMinMinutelyRate": DefaultTCPNewConnectionsMinMinutelyRate,
|
||||
"tcpNewConnectionsSecondlyRate": DefaultTCPNewConnectionsSecondlyRate,
|
||||
"tcpNewConnectionsMinSecondlyRate": DefaultTCPNewConnectionsMinSecondlyRate,
|
||||
}
|
||||
25
pkg/nodeconfigs/dns_resolver.go
Normal file
25
pkg/nodeconfigs/dns_resolver.go
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package nodeconfigs
|
||||
|
||||
type DNSResolverType = string
|
||||
|
||||
const (
|
||||
DNSResolverTypeDefault = "default"
|
||||
DNSResolverTypeGoNative = "goNative"
|
||||
DNSResolverTypeCGO = "cgo"
|
||||
)
|
||||
|
||||
func DefaultDNSResolverConfig() *DNSResolverConfig {
|
||||
return &DNSResolverConfig{
|
||||
Type: DNSResolverTypeDefault,
|
||||
}
|
||||
}
|
||||
|
||||
type DNSResolverConfig struct {
|
||||
Type string `yaml:"type" json:"type"` // 使用Go语言内置的DNS解析器
|
||||
}
|
||||
|
||||
func (this *DNSResolverConfig) Init() error {
|
||||
return nil
|
||||
}
|
||||
46
pkg/nodeconfigs/image_webp_policy.go
Normal file
46
pkg/nodeconfigs/image_webp_policy.go
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package nodeconfigs
|
||||
|
||||
import "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||
|
||||
func init() {
|
||||
_ = DefaultWebPImagePolicy.Init()
|
||||
}
|
||||
|
||||
var DefaultWebPImagePolicy = &WebPImagePolicy{
|
||||
IsOn: true,
|
||||
RequireCache: true,
|
||||
MinLength: shared.NewSizeCapacity(0, shared.SizeCapacityUnitKB),
|
||||
MaxLength: shared.NewSizeCapacity(128, shared.SizeCapacityUnitMB),
|
||||
}
|
||||
|
||||
// WebPImagePolicy WebP策略
|
||||
type WebPImagePolicy struct {
|
||||
IsOn bool `yaml:"isOn" json:"isOn"` // 是否启用
|
||||
RequireCache bool `yaml:"requireCache" json:"requireCache"` // 需要在缓存条件下进行
|
||||
MinLength *shared.SizeCapacity `yaml:"minLength" json:"minLength"` // 最小压缩对象比如4m, 24k
|
||||
MaxLength *shared.SizeCapacity `yaml:"maxLength" json:"maxLength"` // 最大压缩对象
|
||||
|
||||
minLength int64
|
||||
maxLength int64
|
||||
}
|
||||
|
||||
func (this *WebPImagePolicy) Init() error {
|
||||
if this.MinLength != nil {
|
||||
this.minLength = this.MinLength.Bytes()
|
||||
}
|
||||
if this.MaxLength != nil {
|
||||
this.maxLength = this.MaxLength.Bytes()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *WebPImagePolicy) MinLengthBytes() int64 {
|
||||
return this.minLength
|
||||
}
|
||||
|
||||
func (this *WebPImagePolicy) MaxLengthBytes() int64 {
|
||||
return this.maxLength
|
||||
}
|
||||
@@ -1,48 +1,110 @@
|
||||
package nodeconfigs
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ddosconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var sharedNodeConfig *NodeConfig = nil
|
||||
|
||||
type ServerError struct {
|
||||
Id int64
|
||||
Message string
|
||||
}
|
||||
|
||||
func NewServerError(serverId int64, message string) *ServerError {
|
||||
return &ServerError{Id: serverId, Message: message}
|
||||
}
|
||||
|
||||
// NodeConfig 边缘节点配置
|
||||
type NodeConfig struct {
|
||||
Id int64 `yaml:"id" json:"id"`
|
||||
NodeId string `yaml:"nodeId" json:"nodeId"`
|
||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||
Servers []*serverconfigs.ServerConfig `yaml:"servers" json:"servers"`
|
||||
Version int64 `yaml:"version" json:"version"`
|
||||
Name string `yaml:"name" json:"name"`
|
||||
Id int64 `yaml:"id" json:"id"`
|
||||
NodeId string `yaml:"nodeId" json:"nodeId"`
|
||||
Secret string `yaml:"secret" json:"secret"`
|
||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||
Servers []*serverconfigs.ServerConfig `yaml:"servers" json:"servers"`
|
||||
SupportCNAME bool `yaml:"supportCNAME" json:"supportCNAME"`
|
||||
Version int64 `yaml:"version" json:"version"`
|
||||
Name string `yaml:"name" json:"name"`
|
||||
GroupId int64 `yaml:"groupId" json:"groupId"`
|
||||
RegionId int64 `yaml:"regionId" json:"regionId"`
|
||||
OCSPVersion int64 `yaml:"ocspVersion" json:"ocspVersion"`
|
||||
|
||||
// 性能
|
||||
MaxCPU int32 `yaml:"maxCPU" json:"maxCPU"`
|
||||
RegionId int64 `yaml:"regionId" json:"regionId"`
|
||||
MaxCacheDiskCapacity *shared.SizeCapacity `yaml:"maxCacheDiskCapacity" json:"maxCacheDiskCapacity"`
|
||||
MaxCacheMemoryCapacity *shared.SizeCapacity `yaml:"maxCacheMemoryCapacity" json:"maxCacheMemoryCapacity"`
|
||||
CacheDiskDir string `yaml:"cacheDiskDir" json:"cacheDiskDir"` // 文件缓存目录
|
||||
MaxCacheDiskCapacity *shared.SizeCapacity `yaml:"maxCacheDiskCapacity" json:"maxCacheDiskCapacity"` // 文件缓存容量
|
||||
MaxCacheMemoryCapacity *shared.SizeCapacity `yaml:"maxCacheMemoryCapacity" json:"maxCacheMemoryCapacity"` // 内容缓存容量
|
||||
MaxThreads int `yaml:"maxThreads" json:"maxThreads"` // 最大线程数
|
||||
DDoSProtection *ddosconfigs.ProtectionConfig `yaml:"ddosProtection" json:"ddosProtection"`
|
||||
|
||||
// 级别
|
||||
Level int32 `yaml:"level" json:"level"`
|
||||
ParentNodes map[int64][]*ParentNodeConfig `yaml:"parentNodes" json:"parentNodes"` // clusterId => []*ParentNodeConfig
|
||||
|
||||
// 全局配置
|
||||
GlobalConfig *serverconfigs.GlobalConfig `yaml:"globalConfig" json:"globalConfig"` // 全局配置
|
||||
GlobalConfig *serverconfigs.GlobalConfig `yaml:"globalConfig" json:"globalConfig"` // 全局配置
|
||||
GlobalServerConfig *serverconfigs.GlobalServerConfig `yaml:"globalServerConfig" json:"globalServerConfig"` // 服务全局配置,用来替代 GlobalConfig
|
||||
ProductConfig *ProductConfig `yaml:"productConfig" json:"productConfig"`
|
||||
|
||||
// 集群统一配置
|
||||
HTTPFirewallPolicy *firewallconfigs.HTTPFirewallPolicy `yaml:"httpFirewallPolicy" json:"httpFirewallPolicy"`
|
||||
HTTPCachePolicy *serverconfigs.HTTPCachePolicy `yaml:"httpCachePolicy" json:"httpCachePolicy"`
|
||||
TOA *TOAConfig `yaml:"toa" json:"toa"`
|
||||
SystemServices map[string]maps.Map `yaml:"systemServices" json:"systemServices"` // 系统服务配置 type => params
|
||||
FirewallActions []*firewallconfigs.FirewallActionConfig `yaml:"firewallActions" json:"firewallActions"`
|
||||
HTTPFirewallPolicies []*firewallconfigs.HTTPFirewallPolicy `yaml:"httpFirewallPolicies" json:"httpFirewallPolicies"`
|
||||
HTTPCachePolicies []*serverconfigs.HTTPCachePolicy `yaml:"httpCachePolicies" json:"httpCachePolicies"`
|
||||
TOA *TOAConfig `yaml:"toa" json:"toa"`
|
||||
SystemServices map[string]maps.Map `yaml:"systemServices" json:"systemServices"` // 系统服务配置 type => params
|
||||
FirewallActions []*firewallconfigs.FirewallActionConfig `yaml:"firewallActions" json:"firewallActions"` // 防火墙动作
|
||||
TimeZone string `yaml:"timeZone" json:"timeZone"` // 自动设置时区
|
||||
AutoOpenPorts bool `yaml:"autoOpenPorts" json:"autoOpenPorts"` // 自动开放所需端口
|
||||
Clock *ClockConfig `yaml:"clock" json:"clock"` // 时钟配置
|
||||
AutoInstallNftables bool `yaml:"autoInstallNftables" json:"autoInstallNftables"` // 自动安装nftables
|
||||
|
||||
// 指标
|
||||
MetricItems []*serverconfigs.MetricItemConfig `yaml:"metricItems" json:"metricItems"`
|
||||
|
||||
// 自动白名单
|
||||
AllowedIPs []string `yaml:"allowedIPs" json:"allowedIPs"`
|
||||
|
||||
// 脚本
|
||||
CommonScripts []*serverconfigs.CommonScript `yaml:"commonScripts" json:"commonScripts"`
|
||||
|
||||
// WebP
|
||||
WebPImagePolicies map[int64]*WebPImagePolicy `yaml:"webpImagePolicies" json:"webpImagePolicies"` // clusterId => *WebPImagePolicy
|
||||
|
||||
// UAM相关配置
|
||||
UAMPolicies map[int64]*UAMPolicy `yaml:"uamPolicies" yaml:"uamPolicies" json:"uamPolicies"` // clusterId => *UAMPolicy
|
||||
|
||||
// DNS
|
||||
DNSResolver *DNSResolverConfig `yaml:"dnsResolver" json:"dnsResolver"`
|
||||
|
||||
paddedId string
|
||||
|
||||
// firewall
|
||||
firewallPolicies []*firewallconfigs.HTTPFirewallPolicy
|
||||
|
||||
// metrics
|
||||
hasHTTPConnectionMetrics bool
|
||||
|
||||
// 源站集合
|
||||
originMap map[int64]*serverconfigs.OriginConfig
|
||||
|
||||
// 自动白名单
|
||||
allowedIPMap map[string]bool
|
||||
|
||||
// syn flood
|
||||
synFlood *firewallconfigs.SYNFloodConfig
|
||||
|
||||
secretHash string
|
||||
}
|
||||
|
||||
// SharedNodeConfig 取得当前节点配置单例
|
||||
@@ -54,12 +116,12 @@ func SharedNodeConfig() (*NodeConfig, error) {
|
||||
return sharedNodeConfig, nil
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(Tea.ConfigFile("node.json"))
|
||||
data, err := os.ReadFile(Tea.ConfigFile("node.json"))
|
||||
if err != nil {
|
||||
return &NodeConfig{}, err
|
||||
}
|
||||
|
||||
config := &NodeConfig{}
|
||||
var config = &NodeConfig{}
|
||||
err = json.Unmarshal(data, &config)
|
||||
if err != nil {
|
||||
return &NodeConfig{}, err
|
||||
@@ -76,87 +138,250 @@ func ResetNodeConfig(nodeConfig *NodeConfig) {
|
||||
shared.Locker.Unlock()
|
||||
}
|
||||
|
||||
// CloneNodeConfig 复制节点配置
|
||||
func CloneNodeConfig(nodeConfig *NodeConfig) (*NodeConfig, error) {
|
||||
if nodeConfig == nil {
|
||||
return nil, errors.New("node config should not be nil")
|
||||
}
|
||||
|
||||
var newConfigValue = reflect.Indirect(reflect.ValueOf(&NodeConfig{}))
|
||||
var oldValue = reflect.Indirect(reflect.ValueOf(nodeConfig))
|
||||
var valueType = oldValue.Type()
|
||||
for i := 0; i < valueType.NumField(); i++ {
|
||||
var field = valueType.Field(i)
|
||||
var fieldName = field.Name
|
||||
if !field.IsExported() {
|
||||
continue
|
||||
}
|
||||
if fieldName == "Servers" {
|
||||
continue
|
||||
}
|
||||
|
||||
newConfigValue.FieldByName(fieldName).Set(oldValue.FieldByName(fieldName))
|
||||
}
|
||||
|
||||
var newConfig = newConfigValue.Interface().(NodeConfig)
|
||||
newConfig.Servers = append([]*serverconfigs.ServerConfig{}, nodeConfig.Servers...)
|
||||
return &newConfig, nil
|
||||
}
|
||||
|
||||
// Init 初始化
|
||||
func (this *NodeConfig) Init() error {
|
||||
func (this *NodeConfig) Init() (err error, serverErrors []*ServerError) {
|
||||
this.secretHash = fmt.Sprintf("%x", sha256.Sum256([]byte(this.NodeId+"@"+this.Secret)))
|
||||
this.paddedId = fmt.Sprintf("%08d", this.Id)
|
||||
|
||||
// servers
|
||||
for _, server := range this.Servers {
|
||||
err := server.Init()
|
||||
if err != nil {
|
||||
// 避免在运行时重新初始化
|
||||
if server.IsInitialized() {
|
||||
continue
|
||||
}
|
||||
|
||||
// 初始化
|
||||
errs := server.Init()
|
||||
if len(errs) > 0 {
|
||||
// 这里不返回错误,而是继续往下,防止单个服务错误而影响其他服务
|
||||
logs.Println("[INIT]server '" + strconv.FormatInt(server.Id, 10) + "' init failed: " + err.Error())
|
||||
for _, serverErr := range errs {
|
||||
serverErrors = append(serverErrors, NewServerError(server.Id, "server '"+strconv.FormatInt(server.Id, 10)+"' init failed: "+serverErr.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
// 检查ACME支持
|
||||
if server.IsOn && server.SupportCNAME {
|
||||
this.SupportCNAME = true
|
||||
}
|
||||
}
|
||||
|
||||
// global config
|
||||
if this.GlobalConfig != nil {
|
||||
err := this.GlobalConfig.Init()
|
||||
err = this.GlobalConfig.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// cache policy
|
||||
if this.HTTPCachePolicy != nil {
|
||||
err := this.HTTPCachePolicy.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
if len(this.HTTPCachePolicies) > 0 {
|
||||
for _, policy := range this.HTTPCachePolicies {
|
||||
err = policy.Init()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// firewall policy
|
||||
if this.HTTPFirewallPolicy != nil {
|
||||
err := this.HTTPFirewallPolicy.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
if len(this.HTTPFirewallPolicies) > 0 {
|
||||
for _, policy := range this.HTTPFirewallPolicies {
|
||||
err = policy.Init()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TOA
|
||||
if this.TOA != nil {
|
||||
err := this.TOA.Init()
|
||||
err = this.TOA.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 源站
|
||||
this.originMap = map[int64]*serverconfigs.OriginConfig{}
|
||||
|
||||
// 查找FirewallPolicy
|
||||
this.synFlood = nil
|
||||
this.firewallPolicies = []*firewallconfigs.HTTPFirewallPolicy{}
|
||||
if this.HTTPFirewallPolicy != nil && this.HTTPFirewallPolicy.IsOn {
|
||||
this.firewallPolicies = append(this.firewallPolicies, this.HTTPFirewallPolicy)
|
||||
for _, policy := range this.HTTPFirewallPolicies {
|
||||
if policy.IsOn {
|
||||
this.firewallPolicies = append(this.firewallPolicies, policy)
|
||||
if policy.SYNFlood != nil && policy.SYNFlood.IsOn {
|
||||
this.synFlood = policy.SYNFlood
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, server := range this.Servers {
|
||||
if !server.IsOk() || !server.IsOn {
|
||||
continue
|
||||
}
|
||||
|
||||
// WAF策略
|
||||
if server.HTTPFirewallPolicyId > 0 {
|
||||
for _, policy := range this.HTTPFirewallPolicies {
|
||||
if server.HTTPFirewallPolicyId == policy.Id {
|
||||
server.HTTPFirewallPolicy = policy
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 缓存策略
|
||||
if server.HTTPCachePolicyId > 0 {
|
||||
for _, policy := range this.HTTPCachePolicies {
|
||||
if server.HTTPCachePolicyId == policy.Id {
|
||||
server.HTTPCachePolicy = policy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 源站
|
||||
if server.ReverseProxyRef != nil && server.ReverseProxyRef.IsOn && server.ReverseProxy != nil && server.ReverseProxy.IsOn {
|
||||
for _, origin := range server.ReverseProxy.PrimaryOrigins {
|
||||
if origin.IsOn {
|
||||
this.originMap[origin.Id] = origin
|
||||
}
|
||||
}
|
||||
for _, origin := range server.ReverseProxy.BackupOrigins {
|
||||
if origin.IsOn {
|
||||
this.originMap[origin.Id] = origin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if server.Web != nil {
|
||||
this.lookupWeb(server.Web)
|
||||
this.lookupWeb(server, server.Web)
|
||||
}
|
||||
}
|
||||
|
||||
// firewall actions
|
||||
for _, action := range this.FirewallActions {
|
||||
err := action.Init()
|
||||
err = action.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// metric items
|
||||
this.hasHTTPConnectionMetrics = false
|
||||
for _, item := range this.MetricItems {
|
||||
err := item.Init()
|
||||
err = item.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
if item.IsOn && item.HasHTTPConnectionValue() {
|
||||
this.hasHTTPConnectionMetrics = true
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
// 自动白名单
|
||||
this.allowedIPMap = map[string]bool{}
|
||||
for _, allowIP := range this.AllowedIPs {
|
||||
this.allowedIPMap[allowIP] = true
|
||||
}
|
||||
|
||||
// webp image policy
|
||||
if this.WebPImagePolicies != nil {
|
||||
for _, policy := range this.WebPImagePolicies {
|
||||
err = policy.Init()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// uam policy
|
||||
if this.UAMPolicies != nil {
|
||||
for _, policy := range this.UAMPolicies {
|
||||
err = policy.Init()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dns resolver
|
||||
if this.DNSResolver != nil {
|
||||
err = this.DNSResolver.Init()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 全局服务设置
|
||||
if this.GlobalServerConfig != nil {
|
||||
err = this.GlobalServerConfig.Init()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// AddServer 添加服务
|
||||
func (this *NodeConfig) AddServer(server *serverconfigs.ServerConfig) {
|
||||
if server == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var found = false
|
||||
for index, oldServer := range this.Servers {
|
||||
if oldServer.Id == server.Id {
|
||||
this.Servers[index] = server
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
this.Servers = append(this.Servers, server)
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveServer 删除服务
|
||||
func (this *NodeConfig) RemoveServer(serverId int64) {
|
||||
for index, oldServer := range this.Servers {
|
||||
if oldServer.Id == serverId {
|
||||
this.Servers = append(this.Servers[:index], this.Servers[index+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AvailableGroups 根据网络地址和协议分组
|
||||
func (this *NodeConfig) AvailableGroups() []*serverconfigs.ServerGroup {
|
||||
groupMapping := map[string]*serverconfigs.ServerGroup{} // protocol://addr => Server Group
|
||||
func (this *NodeConfig) AvailableGroups() []*serverconfigs.ServerAddressGroup {
|
||||
groupMapping := map[string]*serverconfigs.ServerAddressGroup{} // protocol://addr => Server Group
|
||||
for _, server := range this.Servers {
|
||||
if !server.IsOk() || !server.IsOn {
|
||||
continue
|
||||
@@ -166,13 +391,13 @@ func (this *NodeConfig) AvailableGroups() []*serverconfigs.ServerGroup {
|
||||
if ok {
|
||||
group.Add(server)
|
||||
} else {
|
||||
group = serverconfigs.NewServerGroup(addr)
|
||||
group = serverconfigs.NewServerAddressGroup(addr)
|
||||
group.Add(server)
|
||||
}
|
||||
groupMapping[addr] = group
|
||||
}
|
||||
}
|
||||
result := []*serverconfigs.ServerGroup{}
|
||||
result := []*serverconfigs.ServerAddressGroup{}
|
||||
for _, group := range groupMapping {
|
||||
result = append(result, group)
|
||||
}
|
||||
@@ -194,7 +419,7 @@ func (this *NodeConfig) Save() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(Tea.ConfigFile("node.json"), data, 0777)
|
||||
return os.WriteFile(Tea.ConfigFile("node.json"), data, 0777)
|
||||
}
|
||||
|
||||
// PaddedId 获取填充后的ID
|
||||
@@ -202,23 +427,125 @@ func (this *NodeConfig) PaddedId() string {
|
||||
return this.paddedId
|
||||
}
|
||||
|
||||
// HasHTTPConnectionMetrics 是否含有HTTP连接数的指标
|
||||
func (this *NodeConfig) HasHTTPConnectionMetrics() bool {
|
||||
return this.hasHTTPConnectionMetrics
|
||||
}
|
||||
|
||||
// FindOrigin 读取源站配置
|
||||
func (this *NodeConfig) FindOrigin(originId int64) *serverconfigs.OriginConfig {
|
||||
if this.originMap == nil {
|
||||
return nil
|
||||
}
|
||||
config, ok := this.originMap[originId]
|
||||
if ok {
|
||||
return config
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 搜索WAF策略
|
||||
func (this *NodeConfig) lookupWeb(web *serverconfigs.HTTPWebConfig) {
|
||||
func (this *NodeConfig) lookupWeb(server *serverconfigs.ServerConfig, web *serverconfigs.HTTPWebConfig) {
|
||||
if web == nil || !web.IsOn {
|
||||
return
|
||||
}
|
||||
if web.FirewallPolicy != nil && web.FirewallPolicy.IsOn {
|
||||
// 复用节点的拦截选项设置
|
||||
if web.FirewallPolicy.BlockOptions == nil && this.HTTPFirewallPolicy != nil && this.HTTPFirewallPolicy.BlockOptions != nil {
|
||||
web.FirewallPolicy.BlockOptions = this.HTTPFirewallPolicy.BlockOptions
|
||||
// 复用节点的选项设置
|
||||
if server.HTTPFirewallPolicy != nil {
|
||||
if (web.FirewallPolicy.BlockOptions == nil || !web.FirewallPolicy.BlockOptions.IsPrior) && server.HTTPFirewallPolicy.BlockOptions != nil {
|
||||
web.FirewallPolicy.BlockOptions = server.HTTPFirewallPolicy.BlockOptions
|
||||
}
|
||||
if (web.FirewallPolicy.CaptchaOptions == nil || !web.FirewallPolicy.CaptchaOptions.IsPrior) && server.HTTPFirewallPolicy.CaptchaOptions != nil {
|
||||
web.FirewallPolicy.CaptchaOptions = server.HTTPFirewallPolicy.CaptchaOptions
|
||||
}
|
||||
if (web.FirewallPolicy.SYNFlood == nil || !web.FirewallPolicy.SYNFlood.IsPrior) && server.HTTPFirewallPolicy.SYNFlood != nil {
|
||||
web.FirewallPolicy.SYNFlood = server.HTTPFirewallPolicy.SYNFlood
|
||||
}
|
||||
if (web.FirewallPolicy.Log == nil || !web.FirewallPolicy.Log.IsPrior) && server.HTTPFirewallPolicy.Log != nil {
|
||||
web.FirewallPolicy.Log = server.HTTPFirewallPolicy.Log
|
||||
}
|
||||
|
||||
web.FirewallPolicy.Mode = server.HTTPFirewallPolicy.Mode
|
||||
web.FirewallPolicy.UseLocalFirewall = server.HTTPFirewallPolicy.UseLocalFirewall
|
||||
}
|
||||
|
||||
this.firewallPolicies = append(this.firewallPolicies, web.FirewallPolicy)
|
||||
}
|
||||
if len(web.Locations) > 0 {
|
||||
for _, location := range web.Locations {
|
||||
// 源站
|
||||
if location.IsOn && location.ReverseProxyRef != nil && location.ReverseProxyRef.IsOn && location.ReverseProxy != nil && location.ReverseProxy.IsOn {
|
||||
for _, origin := range location.ReverseProxy.PrimaryOrigins {
|
||||
if origin.IsOn {
|
||||
this.originMap[origin.Id] = origin
|
||||
}
|
||||
}
|
||||
for _, origin := range location.ReverseProxy.BackupOrigins {
|
||||
if origin.IsOn {
|
||||
this.originMap[origin.Id] = origin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Web
|
||||
if location.Web != nil && location.Web.IsOn {
|
||||
this.lookupWeb(location.Web)
|
||||
this.lookupWeb(server, location.Web)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IPIsAutoAllowed 检查是否自动允许某个IP
|
||||
func (this *NodeConfig) IPIsAutoAllowed(ip string) bool {
|
||||
_, ok := this.allowedIPMap[ip]
|
||||
return ok
|
||||
}
|
||||
|
||||
// SYNFloodConfig 获取SYN Flood配置
|
||||
func (this *NodeConfig) SYNFloodConfig() *firewallconfigs.SYNFloodConfig {
|
||||
return this.synFlood
|
||||
}
|
||||
|
||||
// UpdateCertOCSP 修改证书OCSP
|
||||
func (this *NodeConfig) UpdateCertOCSP(certId int64, ocsp []byte, expiresAt int64) {
|
||||
shared.Locker.Lock()
|
||||
defer shared.Locker.Unlock()
|
||||
|
||||
var servers = this.Servers
|
||||
for _, server := range servers {
|
||||
if server.HTTPS != nil &&
|
||||
server.HTTPS.SSLPolicy != nil &&
|
||||
server.HTTPS.SSLPolicy.OCSPIsOn &&
|
||||
server.HTTPS.SSLPolicy.ContainsCert(certId) {
|
||||
server.HTTPS.SSLPolicy.UpdateCertOCSP(certId, ocsp, expiresAt)
|
||||
}
|
||||
|
||||
if server.TLS != nil &&
|
||||
server.TLS.SSLPolicy != nil &&
|
||||
server.TLS.SSLPolicy.OCSPIsOn &&
|
||||
server.TLS.SSLPolicy.ContainsCert(certId) {
|
||||
server.TLS.SSLPolicy.UpdateCertOCSP(certId, ocsp, expiresAt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FindWebPImagePolicyWithClusterId 使用集群ID查找WebP策略
|
||||
func (this *NodeConfig) FindWebPImagePolicyWithClusterId(clusterId int64) *WebPImagePolicy {
|
||||
if this.WebPImagePolicies == nil {
|
||||
return nil
|
||||
}
|
||||
return this.WebPImagePolicies[clusterId]
|
||||
}
|
||||
|
||||
// FindUAMPolicyWithClusterId 使用集群ID查找UAM策略
|
||||
func (this *NodeConfig) FindUAMPolicyWithClusterId(clusterId int64) *UAMPolicy {
|
||||
if this.UAMPolicies == nil {
|
||||
return nil
|
||||
}
|
||||
return this.UAMPolicies[clusterId]
|
||||
}
|
||||
|
||||
// SecretHash 对Id和Secret的Hash计算
|
||||
func (this *NodeConfig) SecretHash() string {
|
||||
return this.secretHash
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSharedNodeConfig(t *testing.T) {
|
||||
@@ -64,3 +65,37 @@ func TestNodeConfig_Groups(t *testing.T) {
|
||||
}
|
||||
logs.PrintAsJSON(config.AvailableGroups(), t)
|
||||
}
|
||||
|
||||
func TestCloneNodeConfig(t *testing.T) {
|
||||
var config = &NodeConfig{Id: 1, NodeId: "1", IsOn: true}
|
||||
for i := 0; i < 100_000; i++ {
|
||||
config.Servers = append(config.Servers, &serverconfigs.ServerConfig{})
|
||||
}
|
||||
var before = time.Now()
|
||||
newConfig, err := CloneNodeConfig(config)
|
||||
t.Log(time.Since(before))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
newConfig.Servers = []*serverconfigs.ServerConfig{}
|
||||
logs.PrintAsJSON(newConfig, t)
|
||||
}
|
||||
|
||||
func TestNodeConfig_AddServer(t *testing.T) {
|
||||
var config = &NodeConfig{Id: 1, NodeId: "1", IsOn: true}
|
||||
config.AddServer(&serverconfigs.ServerConfig{Id: 1})
|
||||
config.AddServer(&serverconfigs.ServerConfig{Id: 2})
|
||||
|
||||
t.Log("===before===")
|
||||
for _, s := range config.Servers {
|
||||
t.Log(s.Id)
|
||||
}
|
||||
|
||||
t.Log("===after===")
|
||||
config.AddServer(&serverconfigs.ServerConfig{Id: 3})
|
||||
config.RemoveServer(2)
|
||||
for _, s := range config.Servers {
|
||||
t.Log(s.Id)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
12
pkg/nodeconfigs/node_ip_addr.go
Normal file
12
pkg/nodeconfigs/node_ip_addr.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package nodeconfigs
|
||||
|
||||
type NodeIPAddr struct {
|
||||
Id int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
IP string `json:"ip"`
|
||||
IsOn bool `json:"isOn"`
|
||||
IsUp bool `json:"isUp"`
|
||||
CanAccess bool `json:"canAccess"`
|
||||
}
|
||||
37
pkg/nodeconfigs/node_levels.go
Normal file
37
pkg/nodeconfigs/node_levels.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package nodeconfigs
|
||||
|
||||
type NodeLevel struct {
|
||||
Name string `yaml:"name" json:"name"`
|
||||
Code int `yaml:"code" json:"code"`
|
||||
Description string `yaml:"description" json:"description"`
|
||||
}
|
||||
|
||||
func FindAllNodeLevels() []*NodeLevel {
|
||||
return []*NodeLevel{
|
||||
{
|
||||
Name: "边缘节点",
|
||||
Code: 1,
|
||||
Description: "普通的边缘节点。",
|
||||
},
|
||||
{
|
||||
Name: "L2节点",
|
||||
Code: 2,
|
||||
Description: "特殊的边缘节点,同时负责同组上一级节点的回源。",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func FindNodeLevel(level int) *NodeLevel {
|
||||
level--
|
||||
|
||||
var levels = FindAllNodeLevels()
|
||||
if level < 0 {
|
||||
return levels[0]
|
||||
}
|
||||
if level < len(levels) {
|
||||
return levels[level]
|
||||
}
|
||||
return levels[0]
|
||||
}
|
||||
12
pkg/nodeconfigs/node_log_type.go
Normal file
12
pkg/nodeconfigs/node_log_type.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package nodeconfigs
|
||||
|
||||
type NodeLogType = string
|
||||
|
||||
const (
|
||||
NodeLogTypeListenAddressFailed NodeLogType = "listenAddressFailed"
|
||||
NodeLogTypeServerConfigInitFailed NodeLogType = "serverConfigInitFailed"
|
||||
NodeLogTypeRunScriptFailed NodeLogType = "runScriptFailed"
|
||||
NodeLogTypeScriptConsoleLog NodeLogType = "scriptConsoleLog"
|
||||
)
|
||||
10
pkg/nodeconfigs/node_parent_config.go
Normal file
10
pkg/nodeconfigs/node_parent_config.go
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package nodeconfigs
|
||||
|
||||
// ParentNodeConfig 父级节点配置
|
||||
type ParentNodeConfig struct {
|
||||
Id int64 `yaml:"id" json:"id"`
|
||||
Addrs []string `yaml:"addrs" json:"addrs"`
|
||||
SecretHash string `yaml:"secretHash" json:"secretHash"`
|
||||
}
|
||||
@@ -14,4 +14,5 @@ const (
|
||||
NodeRoleNode NodeRole = "node"
|
||||
NodeRoleCluster NodeRole = "cluster"
|
||||
NodeRoleAuthority NodeRole = "authority"
|
||||
NodeRoleReport NodeRole = "report"
|
||||
)
|
||||
|
||||
@@ -20,14 +20,21 @@ type NodeStatus struct {
|
||||
DiskMaxUsagePartition string `json:"diskMaxUsagePartition"`
|
||||
DiskTotal uint64 `json:"diskTotal"`
|
||||
UpdatedAt int64 `json:"updatedAt"`
|
||||
Timestamp int64 `json:"timestamp"` // 当前节点时间戳
|
||||
Load1m float64 `json:"load1m"`
|
||||
Load5m float64 `json:"load5m"`
|
||||
Load15m float64 `json:"load15m"`
|
||||
ConnectionCount int `json:"connectionCount"` // 连接数
|
||||
ExePath string `json:"exePath"` // 可执行文件路径
|
||||
|
||||
TrafficInBytes uint64 `json:"trafficInBytes"`
|
||||
TrafficOutBytes uint64 `json:"trafficOutBytes"`
|
||||
|
||||
CacheTotalDiskSize int64 `json:"cacheTotalDiskSize"`
|
||||
CacheTotalMemorySize int64 `json:"cacheTotalMemorySize"`
|
||||
|
||||
LocalFirewallName string `json:"localFirewallName"`
|
||||
|
||||
IsActive bool `json:"isActive"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
@@ -2,19 +2,24 @@
|
||||
|
||||
package nodeconfigs
|
||||
|
||||
import "encoding/json"
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// NodeValueItem 监控项
|
||||
type NodeValueItem = string
|
||||
|
||||
const (
|
||||
NodeValueItemCPU NodeValueItem = "cpu" // CPU
|
||||
NodeValueItemMemory NodeValueItem = "memory" // 内存
|
||||
NodeValueItemLoad NodeValueItem = "load" // 负载
|
||||
NodeValueItemTrafficIn NodeValueItem = "trafficIn" // 上行流量
|
||||
NodeValueItemTrafficOut NodeValueItem = "trafficOut" // 下行流量
|
||||
NodeValueItemConnections NodeValueItem = "connections" // 连接数
|
||||
NodeValueItemDisk NodeValueItem = "disk" // 磁盘
|
||||
NodeValueItemCPU NodeValueItem = "cpu" // CPU
|
||||
NodeValueItemMemory NodeValueItem = "memory" // 内存
|
||||
NodeValueItemLoad NodeValueItem = "load" // 负载
|
||||
NodeValueItemTrafficIn NodeValueItem = "trafficIn" // 上行流量
|
||||
NodeValueItemTrafficOut NodeValueItem = "trafficOut" // 下行流量
|
||||
NodeValueItemConnections NodeValueItem = "connections" // 连接数
|
||||
NodeValueItemRequests NodeValueItem = "requests" // 请求访问量
|
||||
NodeValueItemAttackRequests NodeValueItem = "attackRequests" // 攻击请求访问量
|
||||
NodeValueItemDisk NodeValueItem = "disk" // 磁盘
|
||||
NodeValueItemCacheDir NodeValueItem = "cacheDir" // 缓存目录
|
||||
)
|
||||
|
||||
type nodeValueItemDefinition struct {
|
||||
@@ -109,6 +114,28 @@ var nodeValueItemDefinitions = []*nodeValueItemDefinition{
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Code: NodeValueItemRequests,
|
||||
Name: "请求数",
|
||||
Params: []*nodeValueItemParamDefinition{
|
||||
{
|
||||
Code: "total",
|
||||
Name: "总数",
|
||||
Description: "请求总数",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Code: NodeValueItemAttackRequests,
|
||||
Name: "攻击请求数",
|
||||
Params: []*nodeValueItemParamDefinition{
|
||||
{
|
||||
Code: "total",
|
||||
Name: "总数",
|
||||
Description: "攻击请求总数",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Code: NodeValueItemDisk,
|
||||
Name: "硬盘空间",
|
||||
|
||||
160
pkg/nodeconfigs/node_value_thresholds.go
Normal file
160
pkg/nodeconfigs/node_value_thresholds.go
Normal file
@@ -0,0 +1,160 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package nodeconfigs
|
||||
|
||||
import "github.com/iwind/TeaGo/maps"
|
||||
|
||||
type IPAddressThresholdItem = string
|
||||
|
||||
const (
|
||||
IPAddressThresholdItemNodeAvgRequests IPAddressThresholdItem = "nodeAvgRequests" // 个
|
||||
IPAddressThresholdItemNodeAvgTrafficOut IPAddressThresholdItem = "nodeAvgTrafficOut" // 节点下行流量 M
|
||||
IPAddressThresholdItemNodeAvgTrafficIn IPAddressThresholdItem = "nodeAvgTrafficIn" // 节点上行流量 M
|
||||
IPAddressThresholdItemNodeHealthCheck IPAddressThresholdItem = "nodeHealthCheck" // 节点健康检查结果
|
||||
IPAddressThresholdItemGroupAvgRequests IPAddressThresholdItem = "groupAvgRequests" // 个
|
||||
IPAddressThresholdItemGroupAvgTrafficIn IPAddressThresholdItem = "groupAvgTrafficIn" // 分组上行流量 M
|
||||
IPAddressThresholdItemGroupAvgTrafficOut IPAddressThresholdItem = "groupAvgTrafficOut" // 分组下行流量 M
|
||||
IPAddressThresholdItemClusterAvgRequests IPAddressThresholdItem = "clusterAvgRequests" // 个
|
||||
IPAddressThresholdItemClusterAvgTrafficIn IPAddressThresholdItem = "clusterAvgTrafficIn" // 集群上行流量 M
|
||||
IPAddressThresholdItemClusterAvgTrafficOut IPAddressThresholdItem = "clusterAvgTrafficOut" // 集群下行流量 M
|
||||
IPAddressThresholdItemConnectivity IPAddressThresholdItem = "connectivity" // 0-100
|
||||
)
|
||||
|
||||
// FindAllIPAddressThresholdItems IP相关阈值项目
|
||||
func FindAllIPAddressThresholdItems() []maps.Map {
|
||||
return []maps.Map{
|
||||
{
|
||||
"name": "节点平均请求数",
|
||||
"code": IPAddressThresholdItemNodeAvgRequests,
|
||||
"description": "当前节点在单位时间内接收到的平均请求数。",
|
||||
"unit": "个",
|
||||
},
|
||||
{
|
||||
"name": "节点平均下行流量",
|
||||
"code": IPAddressThresholdItemNodeAvgTrafficOut,
|
||||
"description": "当前节点在单位时间内发送的下行流量。",
|
||||
"unit": "M",
|
||||
},
|
||||
{
|
||||
"name": "节点平均上行流量",
|
||||
"code": IPAddressThresholdItemNodeAvgTrafficIn,
|
||||
"description": "当前节点在单位时间内接收的上行流量。",
|
||||
"unit": "M",
|
||||
},
|
||||
{
|
||||
"name": "节点健康检查结果",
|
||||
"code": IPAddressThresholdItemNodeHealthCheck,
|
||||
"description": "当前节点健康检查结果。",
|
||||
"unit": "",
|
||||
},
|
||||
|
||||
{
|
||||
"name": "IP连通性",
|
||||
"code": IPAddressThresholdItemConnectivity,
|
||||
"description": "通过区域监控得到的当前IP地址的连通性数值,取值在0和100之间。",
|
||||
"unit": "%",
|
||||
},
|
||||
|
||||
{
|
||||
"name": "分组平均请求数",
|
||||
"code": IPAddressThresholdItemGroupAvgRequests,
|
||||
"description": "当前节点所在分组在单位时间内接收到的平均请求数。",
|
||||
"unit": "个",
|
||||
},
|
||||
{
|
||||
"name": "分组平均下行流量",
|
||||
"code": IPAddressThresholdItemGroupAvgTrafficOut,
|
||||
"description": "当前节点所在分组在单位时间内发送的下行流量。",
|
||||
"unit": "M",
|
||||
},
|
||||
{
|
||||
"name": "分组平均上行流量",
|
||||
"code": IPAddressThresholdItemGroupAvgTrafficIn,
|
||||
"description": "当前节点所在分组在单位时间内接收的上行流量。",
|
||||
"unit": "M",
|
||||
},
|
||||
|
||||
{
|
||||
"name": "集群平均请求数",
|
||||
"code": IPAddressThresholdItemClusterAvgRequests,
|
||||
"description": "当前节点所在集群在单位时间内接收到的平均请求数。",
|
||||
"unit": "个",
|
||||
},
|
||||
{
|
||||
"name": "集群平均下行流量",
|
||||
"code": IPAddressThresholdItemClusterAvgTrafficOut,
|
||||
"description": "当前节点所在集群在单位时间内发送的下行流量。",
|
||||
"unit": "M",
|
||||
},
|
||||
{
|
||||
"name": "集群平均上行流量",
|
||||
"code": IPAddressThresholdItemClusterAvgTrafficIn,
|
||||
"description": "当前节点所在集群在单位时间内接收的上行流量。",
|
||||
"unit": "M",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// IPAddressThresholdConfig 阈值列表
|
||||
type IPAddressThresholdConfig struct {
|
||||
Id int64 `json:"id"`
|
||||
Items []*IPAddressThresholdItemConfig `json:"items"`
|
||||
Actions []*IPAddressThresholdActionConfig `json:"actions"`
|
||||
}
|
||||
|
||||
// IPAddressThresholdItemConfig 阈值项目
|
||||
type IPAddressThresholdItemConfig struct {
|
||||
Item IPAddressThresholdItem `json:"item"`
|
||||
Operator NodeValueOperator `json:"operator"`
|
||||
Value float64 `json:"value"`
|
||||
Duration int `json:"duration"`
|
||||
DurationUnit NodeValueDurationUnit `json:"durationUnit"`
|
||||
Options maps.Map `json:"options"` // 附加选项
|
||||
}
|
||||
|
||||
type IPAddressThresholdActionConfig struct {
|
||||
Action string `json:"action"`
|
||||
Options maps.Map `json:"options"`
|
||||
}
|
||||
|
||||
// IPAddressThresholdAction 动作
|
||||
type IPAddressThresholdAction = string
|
||||
|
||||
const (
|
||||
IPAddressThresholdActionUp IPAddressThresholdAction = "up" // 上线
|
||||
IPAddressThresholdActionDown IPAddressThresholdAction = "down" // 下线
|
||||
IPAddressThresholdActionNotify IPAddressThresholdAction = "notify" // 通知
|
||||
IPAddressThresholdActionSwitch IPAddressThresholdAction = "switch" // 切换到备用IP
|
||||
IPAddressThresholdActionWebHook IPAddressThresholdAction = "webHook" // 调用外部Webhook
|
||||
)
|
||||
|
||||
// FindAllIPAddressThresholdActions IP相关阈值动作
|
||||
func FindAllIPAddressThresholdActions() []maps.Map {
|
||||
return []maps.Map{
|
||||
{
|
||||
"name": "上线",
|
||||
"code": IPAddressThresholdActionUp,
|
||||
"description": "上线当前IP。",
|
||||
},
|
||||
{
|
||||
"name": "下线",
|
||||
"code": IPAddressThresholdActionDown,
|
||||
"description": "下线当前IP。",
|
||||
},
|
||||
{
|
||||
"name": "通知",
|
||||
"code": IPAddressThresholdActionNotify,
|
||||
"description": "发送已达到阈值通知。",
|
||||
},
|
||||
{
|
||||
"name": "切换",
|
||||
"code": IPAddressThresholdActionSwitch,
|
||||
"description": "在DNS中记录中将IP切换到指定的备用IP。",
|
||||
},
|
||||
{
|
||||
"name": "WebHook",
|
||||
"code": IPAddressThresholdActionWebHook,
|
||||
"description": "调用外部的WebHook。",
|
||||
},
|
||||
}
|
||||
}
|
||||
11
pkg/nodeconfigs/product_config.go
Normal file
11
pkg/nodeconfigs/product_config.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package nodeconfigs
|
||||
|
||||
const DefaultProductName = "GoEdge"
|
||||
|
||||
// ProductConfig 产品相关设置
|
||||
type ProductConfig struct {
|
||||
Name string `yaml:"name" json:"name"`
|
||||
Version string `yaml:"version" json:"version"`
|
||||
}
|
||||
105
pkg/nodeconfigs/timezones.go
Normal file
105
pkg/nodeconfigs/timezones.go
Normal file
File diff suppressed because one or more lines are too long
17
pkg/nodeconfigs/timezones_test.go
Normal file
17
pkg/nodeconfigs/timezones_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package nodeconfigs
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestFindAllTimeZoneLocations(t *testing.T) {
|
||||
var before = time.Now()
|
||||
var locations = FindAllTimeZoneLocations()
|
||||
t.Log(len(locations), "locations")
|
||||
t.Logf("%.2f %s", time.Since(before).Seconds()*1000, "ms")
|
||||
logs.PrintAsJSON(locations, t)
|
||||
}
|
||||
28
pkg/nodeconfigs/uam_policy.go
Normal file
28
pkg/nodeconfigs/uam_policy.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package nodeconfigs
|
||||
|
||||
func init() {
|
||||
_ = DefaultUAMPolicy.Init()
|
||||
}
|
||||
|
||||
var DefaultUAMPolicy = &UAMPolicy{
|
||||
IsOn: true,
|
||||
AllowSearchEngines: true,
|
||||
DenySpiders: true,
|
||||
UITitle: "",
|
||||
UIBody: "",
|
||||
}
|
||||
|
||||
type UAMPolicy struct {
|
||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||
AllowSearchEngines bool `yaml:"allowSearchEngines" json:"allowSearchEngines"` // 直接跳过常见搜索引擎
|
||||
DenySpiders bool `yaml:"denySpiders" json:"denySpiders"` // 拦截常见爬虫
|
||||
|
||||
UITitle string `yaml:"uiTitle" json:"uiTitle"` // 页面标题
|
||||
UIBody string `yaml:"uiBody" json:"uiBody"` // 页面内容
|
||||
}
|
||||
|
||||
func (this *UAMPolicy) Init() error {
|
||||
return nil
|
||||
}
|
||||
72
pkg/nodeutils/aes_256.go
Normal file
72
pkg/nodeutils/aes_256.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package nodeutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
)
|
||||
|
||||
type AES256CFBMethod struct {
|
||||
block cipher.Block
|
||||
iv []byte
|
||||
}
|
||||
|
||||
func (this *AES256CFBMethod) Init(key, iv []byte) error {
|
||||
// 判断key是否为32长度
|
||||
l := len(key)
|
||||
if l > 32 {
|
||||
key = key[:32]
|
||||
} else if l < 32 {
|
||||
key = append(key, bytes.Repeat([]byte{' '}, 32-l)...)
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
this.block = block
|
||||
|
||||
// 判断iv长度
|
||||
l2 := len(iv)
|
||||
if l2 > aes.BlockSize {
|
||||
iv = iv[:aes.BlockSize]
|
||||
} else if l2 < aes.BlockSize {
|
||||
iv = append(iv, bytes.Repeat([]byte{' '}, aes.BlockSize-l2)...)
|
||||
}
|
||||
this.iv = iv
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *AES256CFBMethod) Encrypt(src []byte) (dst []byte, err error) {
|
||||
if len(src) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = recover()
|
||||
}()
|
||||
|
||||
dst = make([]byte, len(src))
|
||||
|
||||
encrypter := cipher.NewCFBEncrypter(this.block, this.iv)
|
||||
encrypter.XORKeyStream(dst, src)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (this *AES256CFBMethod) Decrypt(dst []byte) (src []byte, err error) {
|
||||
if len(dst) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = recover()
|
||||
}()
|
||||
|
||||
src = make([]byte, len(dst))
|
||||
decrypter := cipher.NewCFBDecrypter(this.block, this.iv)
|
||||
decrypter.XORKeyStream(src, dst)
|
||||
|
||||
return
|
||||
}
|
||||
75
pkg/nodeutils/aes_utils.go
Normal file
75
pkg/nodeutils/aes_utils.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package nodeutils
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"time"
|
||||
)
|
||||
|
||||
// EncryptData 加密
|
||||
func EncryptData(nodeUniqueId string, nodeSecret string, data maps.Map, timeout int32) (string, error) {
|
||||
if data == nil {
|
||||
data = maps.Map{}
|
||||
}
|
||||
|
||||
var expiresAt int64
|
||||
if timeout > 0 {
|
||||
expiresAt = time.Now().Unix() + int64(timeout)
|
||||
}
|
||||
|
||||
dataJSON, err := json.Marshal(maps.Map{
|
||||
"expiresAt": expiresAt,
|
||||
"data": data,
|
||||
})
|
||||
if err != nil {
|
||||
return "", errors.New("marshal data to json failed: " + err.Error())
|
||||
}
|
||||
|
||||
var method = &AES256CFBMethod{}
|
||||
err = method.Init([]byte(nodeUniqueId), []byte(nodeSecret))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
result, err := method.Encrypt(dataJSON)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(result), nil
|
||||
}
|
||||
|
||||
// DecryptData 解密
|
||||
func DecryptData(nodeUniqueId string, nodeSecret string, encodedString string) (maps.Map, error) {
|
||||
var method = &AES256CFBMethod{}
|
||||
err := method.Init([]byte(nodeUniqueId), []byte(nodeSecret))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
encodedData, err := base64.StdEncoding.DecodeString(encodedString)
|
||||
if err != nil {
|
||||
return nil, errors.New("base64 decode failed: " + err.Error())
|
||||
}
|
||||
|
||||
dataJSON, err := method.Decrypt(encodedData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result = maps.Map{}
|
||||
err = json.Unmarshal(dataJSON, &result)
|
||||
if err != nil {
|
||||
return nil, errors.New("unmarshal data failed: " + err.Error())
|
||||
}
|
||||
|
||||
var expiresAt = result.GetInt64("expiresAt")
|
||||
if expiresAt > 0 && expiresAt < time.Now().Unix() {
|
||||
return nil, errors.New("data is expired")
|
||||
}
|
||||
|
||||
return result.GetMap("data"), nil
|
||||
}
|
||||
32
pkg/nodeutils/aes_utils_test.go
Normal file
32
pkg/nodeutils/aes_utils_test.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package nodeutils
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEncryptData(t *testing.T) {
|
||||
e, err := EncryptData("a", "b", maps.Map{
|
||||
"c": 1,
|
||||
}, 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("e:", e)
|
||||
|
||||
s, err := DecryptData("a", "b", e)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("s:", s)
|
||||
}
|
||||
|
||||
func BenchmarkEncryptData(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = EncryptData("a", "b", maps.Map{
|
||||
"c": 1,
|
||||
}, 5)
|
||||
}
|
||||
}
|
||||
37
pkg/nodeutils/base64_map.go
Normal file
37
pkg/nodeutils/base64_map.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package nodeutils
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
// Base64EncodeMap 对Map进行Base64编码
|
||||
func Base64EncodeMap(m maps.Map) (string, error) {
|
||||
if m == nil {
|
||||
m = maps.Map{}
|
||||
}
|
||||
data, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var result = base64.StdEncoding.EncodeToString(data)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Base64DecodeMap 对Map进行Base64解码
|
||||
func Base64DecodeMap(encodedString string) (maps.Map, error) {
|
||||
data, err := base64.StdEncoding.DecodeString(encodedString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result = maps.Map{}
|
||||
err = json.Unmarshal(data, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
55
pkg/nodeutils/base64_map_test.go
Normal file
55
pkg/nodeutils/base64_map_test.go
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package nodeutils
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBase64EncodeMap(t *testing.T) {
|
||||
{
|
||||
var m maps.Map
|
||||
encodedString, err := Base64EncodeMap(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("encoded string:", encodedString)
|
||||
|
||||
m, err = Base64DecodeMap(encodedString)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("%#v", m)
|
||||
}
|
||||
|
||||
{
|
||||
var m = maps.Map{}
|
||||
encodedString, err := Base64EncodeMap(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("encoded string:", encodedString)
|
||||
|
||||
m, err = Base64DecodeMap(encodedString)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("%#v", m)
|
||||
}
|
||||
|
||||
{
|
||||
var m = maps.Map{"userId": 1, "name": "李白"}
|
||||
encodedString, err := Base64EncodeMap(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("encoded string:", encodedString)
|
||||
|
||||
m, err = Base64DecodeMap(encodedString)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("%#v", m)
|
||||
}
|
||||
}
|
||||
15
pkg/reporterconfigs/global_setting.go
Normal file
15
pkg/reporterconfigs/global_setting.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package reporterconfigs
|
||||
|
||||
// GlobalSetting 全局设置
|
||||
type GlobalSetting struct {
|
||||
MinNotifyConnectivity float64 `json:"minNotifyConnectivity"` // 需要通知的最小连通值
|
||||
NotifyWebHookURL string `json:"notifyWebHookURL"` // WebHook通知地址
|
||||
}
|
||||
|
||||
func DefaultGlobalSetting() *GlobalSetting {
|
||||
return &GlobalSetting{
|
||||
MinNotifyConnectivity: 100,
|
||||
}
|
||||
}
|
||||
23
pkg/reporterconfigs/messages.go
Normal file
23
pkg/reporterconfigs/messages.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package reporterconfigs
|
||||
|
||||
type MessageCode = string
|
||||
|
||||
// 节点相关消息
|
||||
const (
|
||||
MessageCodeConnectedAPINode MessageCode = "connectedAPINode" // 节点连接API节点成功
|
||||
MessageCodeCheckSystemdService MessageCode = "checkSystemdService" // 检查Systemd服务
|
||||
MessageCodeNewNodeTask MessageCode = "newNodeTask" // 有新的节点任务产生
|
||||
)
|
||||
|
||||
// ConnectedAPINodeMessage 连接API节点成功
|
||||
type ConnectedAPINodeMessage struct {
|
||||
APINodeId int64 `json:"apiNodeId"`
|
||||
}
|
||||
|
||||
// CheckSystemdServiceMessage Systemd服务
|
||||
type CheckSystemdServiceMessage struct {
|
||||
}
|
||||
|
||||
// NewNodeTaskMessage 有新的节点任务
|
||||
type NewNodeTaskMessage struct {
|
||||
}
|
||||
11
pkg/reporterconfigs/node_config.go
Normal file
11
pkg/reporterconfigs/node_config.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package reporterconfigs
|
||||
|
||||
type NodeConfig struct {
|
||||
Id int64 `json:"id"`
|
||||
}
|
||||
|
||||
func (this *NodeConfig) Init() error {
|
||||
return nil
|
||||
}
|
||||
46
pkg/reporterconfigs/report_levels.go
Normal file
46
pkg/reporterconfigs/report_levels.go
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package reporterconfigs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||
)
|
||||
|
||||
type ReportLevel = string
|
||||
|
||||
const (
|
||||
ReportLevelGood ReportLevel = "good"
|
||||
ReportLevelNormal ReportLevel = "normal"
|
||||
ReportLevelBad ReportLevel = "bad"
|
||||
ReportLevelBroken ReportLevel = "broken"
|
||||
)
|
||||
|
||||
func FindAllReportLevels() []*shared.Definition {
|
||||
return []*shared.Definition{
|
||||
{
|
||||
Name: "良好",
|
||||
Code: ReportLevelGood,
|
||||
},
|
||||
{
|
||||
Name: "正常",
|
||||
Code: ReportLevelNormal,
|
||||
},
|
||||
{
|
||||
Name: "不良",
|
||||
Code: ReportLevelBad,
|
||||
},
|
||||
{
|
||||
Name: "错误",
|
||||
Code: ReportLevelBroken,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func FindReportLevelName(level ReportLevel) string {
|
||||
for _, def := range FindAllReportLevels() {
|
||||
if def.Code == level {
|
||||
return def.Name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
16
pkg/reporterconfigs/status.go
Normal file
16
pkg/reporterconfigs/status.go
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package reporterconfigs
|
||||
|
||||
type Status struct {
|
||||
IP string `json:"ip"`
|
||||
OS string `json:"os"`
|
||||
OSName string `json:"osName"`
|
||||
Username string `json:"username"`
|
||||
BuildVersion string `json:"buildVersion"` // 编译版本
|
||||
BuildVersionCode uint32 `json:"buildVersionCode"` // 版本数字
|
||||
UpdatedAt int64 `json:"updatedAt"` // 更新时间
|
||||
|
||||
Location string `json:"location"` // 从IP查询到的Location
|
||||
ISP string `json:"isp"` // 从IP查询到的ISP
|
||||
}
|
||||
23
pkg/reporterconfigs/tasks.go
Normal file
23
pkg/reporterconfigs/tasks.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package reporterconfigs
|
||||
|
||||
type TaskType = string
|
||||
|
||||
const (
|
||||
TaskTypeIPAddr TaskType = "ipAddr"
|
||||
)
|
||||
|
||||
type IPTask struct {
|
||||
AddrId int64 `json:"addrId"`
|
||||
IP string `json:"ip"`
|
||||
Port int `json:"port"`
|
||||
}
|
||||
|
||||
func FindTaskTypeName(taskType TaskType) string {
|
||||
switch taskType {
|
||||
case TaskTypeIPAddr:
|
||||
return "IP地址"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -7,16 +7,17 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ipconfigs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
var SharedHTTPFirewallPolicyDAO = new(HTTPFirewallPolicyDAO)
|
||||
|
||||
// WAF策略相关
|
||||
// HTTPFirewallPolicyDAO WAF策略相关
|
||||
type HTTPFirewallPolicyDAO struct {
|
||||
BaseDAO
|
||||
}
|
||||
|
||||
// 查找WAF策略基本信息
|
||||
// FindEnabledHTTPFirewallPolicy 查找WAF策略基本信息
|
||||
func (this *HTTPFirewallPolicyDAO) FindEnabledHTTPFirewallPolicy(ctx context.Context, policyId int64) (*pb.HTTPFirewallPolicy, error) {
|
||||
resp, err := this.RPC().HTTPFirewallPolicyRPC().FindEnabledHTTPFirewallPolicy(ctx, &pb.FindEnabledHTTPFirewallPolicyRequest{HttpFirewallPolicyId: policyId})
|
||||
if err != nil {
|
||||
@@ -25,7 +26,7 @@ func (this *HTTPFirewallPolicyDAO) FindEnabledHTTPFirewallPolicy(ctx context.Con
|
||||
return resp.HttpFirewallPolicy, nil
|
||||
}
|
||||
|
||||
// 查找WAF策略配置
|
||||
// FindEnabledHTTPFirewallPolicyConfig 查找WAF策略配置
|
||||
func (this *HTTPFirewallPolicyDAO) FindEnabledHTTPFirewallPolicyConfig(ctx context.Context, policyId int64) (*firewallconfigs.HTTPFirewallPolicy, error) {
|
||||
resp, err := this.RPC().HTTPFirewallPolicyRPC().FindEnabledHTTPFirewallPolicyConfig(ctx, &pb.FindEnabledHTTPFirewallPolicyConfigRequest{HttpFirewallPolicyId: policyId})
|
||||
if err != nil {
|
||||
@@ -42,7 +43,7 @@ func (this *HTTPFirewallPolicyDAO) FindEnabledHTTPFirewallPolicyConfig(ctx conte
|
||||
return firewallPolicy, nil
|
||||
}
|
||||
|
||||
// 查找WAF的Inbound
|
||||
// FindEnabledHTTPFirewallPolicyInboundConfig 查找WAF的Inbound
|
||||
func (this *HTTPFirewallPolicyDAO) FindEnabledHTTPFirewallPolicyInboundConfig(ctx context.Context, policyId int64) (*firewallconfigs.HTTPFirewallInboundConfig, error) {
|
||||
config, err := this.FindEnabledHTTPFirewallPolicyConfig(ctx, policyId)
|
||||
if err != nil {
|
||||
@@ -54,7 +55,7 @@ func (this *HTTPFirewallPolicyDAO) FindEnabledHTTPFirewallPolicyInboundConfig(ct
|
||||
return config.Inbound, nil
|
||||
}
|
||||
|
||||
// 根据类型查找WAF的IP名单
|
||||
// FindEnabledPolicyIPListIdWithType 根据类型查找WAF的IP名单
|
||||
func (this *HTTPFirewallPolicyDAO) FindEnabledPolicyIPListIdWithType(ctx context.Context, policyId int64, listType ipconfigs.IPListType) (int64, error) {
|
||||
switch listType {
|
||||
case ipconfigs.IPListTypeWhite:
|
||||
@@ -66,7 +67,7 @@ func (this *HTTPFirewallPolicyDAO) FindEnabledPolicyIPListIdWithType(ctx context
|
||||
}
|
||||
}
|
||||
|
||||
// 查找WAF的白名单
|
||||
// FindEnabledPolicyWhiteIPListId 查找WAF的白名单
|
||||
func (this *HTTPFirewallPolicyDAO) FindEnabledPolicyWhiteIPListId(ctx context.Context, policyId int64) (int64, error) {
|
||||
config, err := this.FindEnabledHTTPFirewallPolicyConfig(ctx, policyId)
|
||||
if err != nil {
|
||||
@@ -110,7 +111,7 @@ func (this *HTTPFirewallPolicyDAO) FindEnabledPolicyWhiteIPListId(ctx context.Co
|
||||
return config.Inbound.AllowListRef.ListId, nil
|
||||
}
|
||||
|
||||
// 查找WAF的黑名单
|
||||
// FindEnabledPolicyBlackIPListId 查找WAF的黑名单
|
||||
func (this *HTTPFirewallPolicyDAO) FindEnabledPolicyBlackIPListId(ctx context.Context, policyId int64) (int64, error) {
|
||||
config, err := this.FindEnabledHTTPFirewallPolicyConfig(ctx, policyId)
|
||||
if err != nil {
|
||||
@@ -154,7 +155,7 @@ func (this *HTTPFirewallPolicyDAO) FindEnabledPolicyBlackIPListId(ctx context.Co
|
||||
return config.Inbound.DenyListRef.ListId, nil
|
||||
}
|
||||
|
||||
// 根据服务Id查找WAF策略
|
||||
// FindEnabledHTTPFirewallPolicyWithServerId 根据服务Id查找WAF策略
|
||||
func (this *HTTPFirewallPolicyDAO) FindEnabledHTTPFirewallPolicyWithServerId(ctx context.Context, serverId int64) (*pb.HTTPFirewallPolicy, error) {
|
||||
serverResp, err := this.RPC().ServerRPC().FindEnabledServer(ctx, &pb.FindEnabledServerRequest{ServerId: serverId})
|
||||
if err != nil {
|
||||
@@ -180,3 +181,72 @@ func (this *HTTPFirewallPolicyDAO) FindEnabledHTTPFirewallPolicyWithServerId(ctx
|
||||
}
|
||||
return SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicy(ctx, cluster.HttpFirewallPolicyId)
|
||||
}
|
||||
|
||||
// FindHTTPFirewallActionConfigs 查找动作相关信息
|
||||
func (this *HTTPFirewallPolicyDAO) FindHTTPFirewallActionConfigs(ctx context.Context, actions []*firewallconfigs.HTTPFirewallActionConfig) ([]maps.Map, error) {
|
||||
var actionConfigs = []maps.Map{}
|
||||
for _, action := range actions {
|
||||
def := firewallconfigs.FindActionDefinition(action.Code)
|
||||
if def == nil {
|
||||
continue
|
||||
}
|
||||
if action.Options == nil {
|
||||
action.Options = maps.Map{}
|
||||
}
|
||||
|
||||
switch action.Code {
|
||||
case firewallconfigs.HTTPFirewallActionRecordIP:
|
||||
listId := action.Options.GetInt64("ipListId")
|
||||
listResp, err := this.RPC().IPListRPC().FindEnabledIPList(ctx, &pb.FindEnabledIPListRequest{IpListId: listId})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if listResp.IpList != nil {
|
||||
action.Options["ipListName"] = listResp.IpList.Name
|
||||
} else {
|
||||
action.Options["ipListName"] = action.Options.GetString("ipListName") + "(已删除)"
|
||||
}
|
||||
case firewallconfigs.HTTPFirewallActionGoGroup:
|
||||
groupId := action.Options.GetInt64("groupId")
|
||||
groupResp, err := this.RPC().HTTPFirewallRuleGroupRPC().FindEnabledHTTPFirewallRuleGroup(ctx, &pb.FindEnabledHTTPFirewallRuleGroupRequest{FirewallRuleGroupId: groupId})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if groupResp.FirewallRuleGroup != nil {
|
||||
action.Options["groupName"] = groupResp.FirewallRuleGroup.Name
|
||||
} else {
|
||||
action.Options["groupName"] = action.Options.GetString("groupName") + "(已删除)"
|
||||
}
|
||||
case firewallconfigs.HTTPFirewallActionGoSet:
|
||||
groupId := action.Options.GetInt64("groupId")
|
||||
groupResp, err := this.RPC().HTTPFirewallRuleGroupRPC().FindEnabledHTTPFirewallRuleGroup(ctx, &pb.FindEnabledHTTPFirewallRuleGroupRequest{FirewallRuleGroupId: groupId})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if groupResp.FirewallRuleGroup != nil {
|
||||
action.Options["groupName"] = groupResp.FirewallRuleGroup.Name
|
||||
} else {
|
||||
action.Options["groupName"] = action.Options.GetString("groupName") + "(已删除)"
|
||||
}
|
||||
|
||||
setId := action.Options.GetInt64("setId")
|
||||
setResp, err := this.RPC().HTTPFirewallRuleSetRPC().FindEnabledHTTPFirewallRuleSet(ctx, &pb.FindEnabledHTTPFirewallRuleSetRequest{FirewallRuleSetId: setId})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if setResp.FirewallRuleSet != nil {
|
||||
action.Options["setName"] = setResp.FirewallRuleSet.Name
|
||||
} else {
|
||||
action.Options["setName"] = action.Options.GetString("setName") + "(已删除)"
|
||||
}
|
||||
}
|
||||
|
||||
actionConfigs = append(actionConfigs, maps.Map{
|
||||
"name": def.Name,
|
||||
"code": def.Code,
|
||||
"category": def.Category,
|
||||
"options": action.Options,
|
||||
})
|
||||
}
|
||||
return actionConfigs, nil
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ type HTTPWebDAO struct {
|
||||
BaseDAO
|
||||
}
|
||||
|
||||
// 根据ServerId查找Web配置
|
||||
// FindWebConfigWithServerId 根据ServerId查找Web配置
|
||||
func (this *HTTPWebDAO) FindWebConfigWithServerId(ctx context.Context, serverId int64) (*serverconfigs.HTTPWebConfig, error) {
|
||||
resp, err := this.RPC().ServerRPC().FindAndInitServerWebConfig(ctx, &pb.FindAndInitServerWebConfigRequest{ServerId: serverId})
|
||||
if err != nil {
|
||||
@@ -29,7 +29,7 @@ func (this *HTTPWebDAO) FindWebConfigWithServerId(ctx context.Context, serverId
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// 根据LocationId查找Web配置
|
||||
// FindWebConfigWithLocationId 根据LocationId查找Web配置
|
||||
func (this *HTTPWebDAO) FindWebConfigWithLocationId(ctx context.Context, locationId int64) (*serverconfigs.HTTPWebConfig, error) {
|
||||
resp, err := this.RPC().HTTPLocationRPC().FindAndInitHTTPLocationWebConfig(ctx, &pb.FindAndInitHTTPLocationWebConfigRequest{LocationId: locationId})
|
||||
if err != nil {
|
||||
@@ -43,9 +43,9 @@ func (this *HTTPWebDAO) FindWebConfigWithLocationId(ctx context.Context, locatio
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// 根据WebId查找Web配置
|
||||
func (this *HTTPWebDAO) FindWebConfigWithId(ctx context.Context, webId int64) (*serverconfigs.HTTPWebConfig, error) {
|
||||
resp, err := this.RPC().HTTPWebRPC().FindEnabledHTTPWebConfig(ctx, &pb.FindEnabledHTTPWebConfigRequest{WebId: webId})
|
||||
// FindWebConfigWithServerGroupId 根据ServerGroupId查找Web配置
|
||||
func (this *HTTPWebDAO) FindWebConfigWithServerGroupId(ctx context.Context, serverGroupId int64) (*serverconfigs.HTTPWebConfig, error) {
|
||||
resp, err := this.RPC().ServerGroupRPC().FindAndInitServerGroupWebConfig(ctx, &pb.FindAndInitServerGroupWebConfigRequest{ServerGroupId: serverGroupId})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -57,14 +57,29 @@ func (this *HTTPWebDAO) FindWebConfigWithId(ctx context.Context, webId int64) (*
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// 初始化防火墙设置
|
||||
func (this *HTTPWebDAO) InitEmptyHTTPFirewallPolicy(ctx context.Context, serverId int64, webId int64, isOn bool) (int64, error) {
|
||||
// FindWebConfigWithId 根据WebId查找Web配置
|
||||
func (this *HTTPWebDAO) FindWebConfigWithId(ctx context.Context, webId int64) (*serverconfigs.HTTPWebConfig, error) {
|
||||
resp, err := this.RPC().HTTPWebRPC().FindEnabledHTTPWebConfig(ctx, &pb.FindEnabledHTTPWebConfigRequest{HttpWebId: webId})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config := &serverconfigs.HTTPWebConfig{}
|
||||
err = json.Unmarshal(resp.HttpWebJSON, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// InitEmptyHTTPFirewallPolicy 初始化防火墙设置
|
||||
func (this *HTTPWebDAO) InitEmptyHTTPFirewallPolicy(ctx context.Context, serverGroupId int64, serverId int64, webId int64, isOn bool) (int64, error) {
|
||||
// 创建FirewallPolicy
|
||||
firewallPolicyIdResp, err := this.RPC().HTTPFirewallPolicyRPC().CreateEmptyHTTPFirewallPolicy(ctx, &pb.CreateEmptyHTTPFirewallPolicyRequest{
|
||||
ServerId: serverId,
|
||||
IsOn: true,
|
||||
Name: "用户自定义",
|
||||
Description: "",
|
||||
ServerGroupId: serverGroupId,
|
||||
ServerId: serverId,
|
||||
IsOn: true,
|
||||
Name: "用户自定义",
|
||||
Description: "",
|
||||
})
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err)
|
||||
@@ -83,7 +98,7 @@ func (this *HTTPWebDAO) InitEmptyHTTPFirewallPolicy(ctx context.Context, serverI
|
||||
}
|
||||
|
||||
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebFirewall(ctx, &pb.UpdateHTTPWebFirewallRequest{
|
||||
WebId: webId,
|
||||
HttpWebId: webId,
|
||||
FirewallJSON: firewallRefJSON,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -15,7 +15,7 @@ type IPListDAO struct {
|
||||
BaseDAO
|
||||
}
|
||||
|
||||
// 查找服务的允许IP列表
|
||||
// FindAllowIPListIdWithServerId 查找服务的允许IP列表
|
||||
func (this *IPListDAO) FindAllowIPListIdWithServerId(ctx context.Context, serverId int64) (int64, error) {
|
||||
webConfig, err := SharedHTTPWebDAO.FindWebConfigWithServerId(ctx, serverId)
|
||||
if err != nil {
|
||||
@@ -30,7 +30,7 @@ func (this *IPListDAO) FindAllowIPListIdWithServerId(ctx context.Context, server
|
||||
return webConfig.FirewallPolicy.Inbound.AllowListRef.ListId, nil
|
||||
}
|
||||
|
||||
// 查找服务的禁止IP列表
|
||||
// FindDenyIPListIdWithServerId 查找服务的禁止IP列表
|
||||
func (this *IPListDAO) FindDenyIPListIdWithServerId(ctx context.Context, serverId int64) (int64, error) {
|
||||
webConfig, err := SharedHTTPWebDAO.FindWebConfigWithServerId(ctx, serverId)
|
||||
if err != nil {
|
||||
@@ -45,7 +45,7 @@ func (this *IPListDAO) FindDenyIPListIdWithServerId(ctx context.Context, serverI
|
||||
return webConfig.FirewallPolicy.Inbound.DenyListRef.ListId, nil
|
||||
}
|
||||
|
||||
// 为服务创建IP名单
|
||||
// CreateIPListForServerId 为服务创建IP名单
|
||||
func (this *IPListDAO) CreateIPListForServerId(ctx context.Context, serverId int64, listType string) (int64, error) {
|
||||
webConfig, err := SharedHTTPWebDAO.FindWebConfigWithServerId(ctx, serverId)
|
||||
if err != nil {
|
||||
@@ -56,7 +56,7 @@ func (this *IPListDAO) CreateIPListForServerId(ctx context.Context, serverId int
|
||||
}
|
||||
if webConfig.FirewallPolicy == nil || webConfig.FirewallPolicy.Id == 0 {
|
||||
isOn := webConfig.FirewallRef != nil && webConfig.FirewallRef.IsOn
|
||||
_, err = SharedHTTPWebDAO.InitEmptyHTTPFirewallPolicy(ctx, serverId, webConfig.Id, isOn)
|
||||
_, err = SharedHTTPWebDAO.InitEmptyHTTPFirewallPolicy(ctx, 0, serverId, webConfig.Id, isOn)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err)
|
||||
}
|
||||
@@ -102,6 +102,7 @@ func (this *IPListDAO) CreateIPListForServerId(ctx context.Context, serverId int
|
||||
Type: listType,
|
||||
Name: "IP名单",
|
||||
Code: listType,
|
||||
ServerId: serverId,
|
||||
TimeoutJSON: nil,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -47,5 +47,4 @@ type RPCClient interface {
|
||||
ACMETaskRPC() pb.ACMETaskServiceClient
|
||||
UserRPC() pb.UserServiceClient
|
||||
UserBillRPC() pb.UserBillServiceClient
|
||||
UserNodeRPC() pb.UserNodeServiceClient
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ type ServerDAO struct {
|
||||
BaseDAO
|
||||
}
|
||||
|
||||
// 查找服务配置
|
||||
// FindEnabledServerConfig 查找服务配置
|
||||
func (this *ServerDAO) FindEnabledServerConfig(ctx context.Context, serverId int64) (*serverconfigs.ServerConfig, error) {
|
||||
resp, err := this.RPC().ServerRPC().FindEnabledServerConfig(ctx, &pb.FindEnabledServerConfigRequest{ServerId: serverId})
|
||||
if err != nil {
|
||||
@@ -30,7 +30,7 @@ func (this *ServerDAO) FindEnabledServerConfig(ctx context.Context, serverId int
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// 查找服务
|
||||
// FindEnabledServer 查找服务
|
||||
func (this *ServerDAO) FindEnabledServer(ctx context.Context, serverId int64) (*pb.Server, error) {
|
||||
resp, err := this.RPC().ServerRPC().FindEnabledServer(ctx, &pb.FindEnabledServerRequest{ServerId: serverId})
|
||||
if err != nil {
|
||||
|
||||
@@ -6,27 +6,28 @@ import (
|
||||
"errors"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HumanError 格式化GRPC相关错误
|
||||
func HumanError(err error) error {
|
||||
func HumanError(err error, endpoints []string, configFile string) (resultErr error, isConnError bool) {
|
||||
if err == nil {
|
||||
return err
|
||||
return err, false
|
||||
}
|
||||
errStatus, ok := status.FromError(err)
|
||||
if !ok {
|
||||
return err
|
||||
return err, false
|
||||
}
|
||||
switch errStatus.Code() {
|
||||
case codes.InvalidArgument:
|
||||
return errors.New("错误的RPC参数:" + err.Error())
|
||||
return errors.New("错误的RPC参数:" + err.Error()), false
|
||||
case codes.DeadlineExceeded:
|
||||
return errors.New("RPC操作超时,请重试:" + err.Error())
|
||||
return errors.New("RPC操作超时,请重试:" + err.Error()), false
|
||||
case codes.Unimplemented:
|
||||
return errors.New("请求的RPC服务或方法不存在,可能是没有升级API节点或者当前节点没有升级:" + err.Error())
|
||||
return errors.New("请求的RPC服务或方法不存在,可能是没有升级API节点或者当前节点没有升级:" + err.Error()), false
|
||||
case codes.Unavailable:
|
||||
return errors.New("RPC当前不可用,请确保API节点已启动,并检查当前节点和API节点之间的网络连接是正常的:" + err.Error())
|
||||
return errors.New("RPC当前不可用:<br/>1、请确认当前节点的api.yaml(<em>" + configFile + "</em>)配置中的地址(<em>" + strings.Join(endpoints, ", ") + "</em>)是否已填写正确;<br/>2、请确保API节点已启动,并检查当前节点和API节点之间的网络连接是正常的。<hr/>错误信息:" + err.Error()), true
|
||||
}
|
||||
|
||||
return err
|
||||
return err, false
|
||||
}
|
||||
|
||||
43
pkg/rpc/jsons/http_access_log_ref.md
Normal file
43
pkg/rpc/jsons/http_access_log_ref.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# 访问日志引用
|
||||
## 定义
|
||||
~~~json
|
||||
{
|
||||
"isPrior": "是否覆盖父级应用",
|
||||
"isOn": "是否启用配置",
|
||||
"fields": ["字段1", "字段2", ...] // 可以留空
|
||||
"status1": "是否启用状态1xx",
|
||||
"status2": "是否启用状态2xx",
|
||||
"status3": "是否启用状态3xx",
|
||||
"status4": "是否启用状态4xx",
|
||||
"status5": "是否启用状态5xx",
|
||||
"enableClientClosed": "是否记录客户端关闭事件",
|
||||
"firewallOnly": "是否只记录防火墙(WAF)相关日志"
|
||||
}
|
||||
~~~
|
||||
|
||||
### 字段值
|
||||
* `1` - 请求Header
|
||||
* `2` - 响应Header
|
||||
* `3` - 请求URL参数
|
||||
* `4` - Cookie
|
||||
* `5` - 扩展信息
|
||||
* `6` - Referer
|
||||
* `7` - UserAgent
|
||||
* `8` - 请求Body
|
||||
* `9` - 响应Body(目前不支持)
|
||||
|
||||
## 示例
|
||||
~~~json
|
||||
{
|
||||
"isPrior": true,
|
||||
"isOn": true,
|
||||
"fields": [],
|
||||
"status1": true,
|
||||
"status2": true,
|
||||
"status3": true,
|
||||
"status4": true,
|
||||
"status5": true,
|
||||
"enableClientClosed": true,
|
||||
"firewallOnly": true
|
||||
}
|
||||
~~~
|
||||
92
pkg/rpc/jsons/http_cache_config.md
Normal file
92
pkg/rpc/jsons/http_cache_config.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# HTTP缓存配置
|
||||
## 定义
|
||||
~~~json
|
||||
{
|
||||
"isPrior": "是否覆盖上级配置",
|
||||
"isOn": "是否启用配置",
|
||||
"addStatusHeader": "是否增加命中状态Header(X-Cache)",
|
||||
"addAgeHeader": "是否增加Age Header",
|
||||
"enableCacheControlMaxAge": "是否支持Cache-Control: max-age=...",
|
||||
"disablePolicyRefs": "是否停用策略中定义的条件",
|
||||
"purgeIsOn": "是否允许使用Purge方法清理",
|
||||
"purgeKey": "Purge时使用的X-Edge-Purge-Key",
|
||||
"stale": "陈旧缓存使用策略",
|
||||
"cacheRefs": ["缓存条件1", "缓存条件2", ...]
|
||||
}
|
||||
~~~
|
||||
其中:
|
||||
* `缓存条件` - 参考 {json:http_cache_ref}
|
||||
|
||||
## 示例
|
||||
### 无缓存条件
|
||||
~~~json
|
||||
{
|
||||
"isPrior": true,
|
||||
"isOn": true,
|
||||
"addStatusHeader": true,
|
||||
"addAgeHeader": true,
|
||||
"enableCacheControlMaxAge": true,
|
||||
"disablePolicyRefs": false,
|
||||
"purgeIsOn": false,
|
||||
"purgeKey": "",
|
||||
"stale": null,
|
||||
"cacheRefs": []
|
||||
}
|
||||
~~~
|
||||
|
||||
### 加入缓存条件
|
||||
~~~json
|
||||
{
|
||||
"isPrior": true,
|
||||
"isOn": true,
|
||||
"addStatusHeader": true,
|
||||
"addAgeHeader": true,
|
||||
"enableCacheControlMaxAge": true,
|
||||
"disablePolicyRefs": false,
|
||||
"purgeIsOn": false,
|
||||
"purgeKey": "",
|
||||
"stale": null,
|
||||
"cacheRefs": [
|
||||
{
|
||||
"id": 0,
|
||||
"isOn": true,
|
||||
"key": "${scheme}://${host}${requestPath}${isArgs}${args}",
|
||||
"life": {"count": 2, "unit": "hour"},
|
||||
"status": [200],
|
||||
"maxSize": {"count": 32, "unit": "mb"},
|
||||
"minSize": {"count": 0, "unit": "kb"},
|
||||
"skipCacheControlValues": ["private", "no-cache", "no-store"],
|
||||
"skipSetCookie": true,
|
||||
"enableRequestCachePragma": false,
|
||||
"conds": {
|
||||
"isOn": true,
|
||||
"connector": "or",
|
||||
"groups": [
|
||||
{
|
||||
"isOn": true,
|
||||
"connector": "and",
|
||||
"conds": [
|
||||
{
|
||||
"type": "url-extension",
|
||||
"isRequest": true,
|
||||
"param": "${requestPathExtension}",
|
||||
"operator": "in",
|
||||
"value": "[\".css\",\".png\",\".js\",\".woff2\"]",
|
||||
"isReverse": false,
|
||||
"isCaseInsensitive": false,
|
||||
"typeName": "URL扩展名"
|
||||
}
|
||||
],
|
||||
"isReverse": false,
|
||||
"description": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
"allowChunkedEncoding": true,
|
||||
"allowPartialContent": false,
|
||||
"isReverse": false,
|
||||
"methods": []
|
||||
}
|
||||
]
|
||||
}
|
||||
~~~
|
||||
91
pkg/rpc/jsons/http_cache_ref.md
Normal file
91
pkg/rpc/jsons/http_cache_ref.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# 缓存条件设置
|
||||
## 定义
|
||||
~~~json
|
||||
{
|
||||
"isOn": "是否启用配置",
|
||||
"key": "每个缓存的Key规则,里面可以有变量",
|
||||
"life": "缓存时长",
|
||||
"expiresTime": "客户端过期时间",
|
||||
"status": ["缓存的状态码1", "缓存的状态码2", ...],
|
||||
"minSize": "能够缓存的最小尺寸",
|
||||
"maxSize": "能够缓存的最大尺寸",
|
||||
"methods": ["支持的请求方法1", "支持的请求方法2", ...],
|
||||
"skipCacheControlValues": "可以跳过的响应的Cache-Control值",
|
||||
"skipSetCookie": "是否跳过响应的Set-Cookie Header",
|
||||
"enableRequestCachePragma": "是否支持客户端的Pragma: no-cache",
|
||||
"allowChunkedEncoding": "是否允许分片内容",
|
||||
"allowPartialContent": "支持分段内容缓存",
|
||||
"conds": "请求条件",
|
||||
"isReverse": "是否为反向条件,反向条件的不缓存"
|
||||
}
|
||||
~~~
|
||||
|
||||
## 示例
|
||||
~~~json
|
||||
{
|
||||
"isOn": true,
|
||||
"key": "${scheme}://${host}${requestURI}",
|
||||
"life": {
|
||||
"count": 1,
|
||||
"unit": "day"
|
||||
},
|
||||
"expiresTime": {
|
||||
"isPrior": true,
|
||||
"isOn": true,
|
||||
"overwrite": true,
|
||||
"autoCalculate": false,
|
||||
"duration": {
|
||||
"count": 1,
|
||||
"unit": "day"
|
||||
}
|
||||
},
|
||||
"status": [
|
||||
200
|
||||
],
|
||||
"minSize": {
|
||||
"count": 0,
|
||||
"unit": "kb"
|
||||
},
|
||||
"maxSize": {
|
||||
"count": 32,
|
||||
"unit": "mb"
|
||||
},
|
||||
"methods": [],
|
||||
"skipCacheControlValues": [
|
||||
"private",
|
||||
"no-cache",
|
||||
"no-store"
|
||||
],
|
||||
"skipSetCookie": true,
|
||||
"enableRequestCachePragma": false,
|
||||
"allowChunkedEncoding": true,
|
||||
"allowPartialContent": false,
|
||||
"conds": {
|
||||
"isOn": true,
|
||||
"connector": "or",
|
||||
"groups": [
|
||||
{
|
||||
"isOn": true,
|
||||
"connector": "and",
|
||||
"conds": [
|
||||
{
|
||||
"type": "url-extension",
|
||||
"isRequest": true,
|
||||
"param": "${requestPathExtension}",
|
||||
"operator": "in",
|
||||
"value": "[\".css\",\".png\",\".js\",\".woff2\"]",
|
||||
"isReverse": false,
|
||||
"isCaseInsensitive": false,
|
||||
"typeName": "URL扩展名"
|
||||
}
|
||||
],
|
||||
"isReverse": false,
|
||||
"description": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
"cachePolicy": null,
|
||||
"isReverse": false,
|
||||
"id": 1
|
||||
}
|
||||
~~~
|
||||
18
pkg/rpc/jsons/http_firewall_ref.md
Normal file
18
pkg/rpc/jsons/http_firewall_ref.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# HTTP防火墙(即WAF)引用
|
||||
## 定义
|
||||
~~~json
|
||||
{
|
||||
"isPrior": "是否覆盖上级配置",
|
||||
"isOn": "是否启用配置",
|
||||
"firewallPolicyId": "WAF策略ID"
|
||||
}
|
||||
~~~
|
||||
|
||||
## 示例
|
||||
~~~json
|
||||
{
|
||||
"isPrior": true,
|
||||
"isOn": true,
|
||||
"firewallPolicyId": 123
|
||||
}
|
||||
~~~
|
||||
31
pkg/rpc/jsons/http_remote_addr_config.md
Normal file
31
pkg/rpc/jsons/http_remote_addr_config.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# HTTP获取客户端IP地址方式配置
|
||||
## 定义
|
||||
~~~json
|
||||
{
|
||||
"isPrior": "是否覆盖父级应用",
|
||||
"isOn": "是否启用配置",
|
||||
"value": "自定义值变量",
|
||||
"isCustomized": "是否自定义"
|
||||
}
|
||||
~~~
|
||||
|
||||
## 示例
|
||||
### 不启用自定义
|
||||
~~~json
|
||||
{
|
||||
"isPrior": false,
|
||||
"isOn": false,
|
||||
"value": "",
|
||||
"isCustomized": false
|
||||
}
|
||||
~~~
|
||||
|
||||
### 启用自定义
|
||||
~~~json
|
||||
{
|
||||
"isPrior": true,
|
||||
"isOn": true,
|
||||
"value": "${remoteAddr}",
|
||||
"isCustomized": true
|
||||
}
|
||||
~~~
|
||||
16
pkg/rpc/jsons/http_stat_stat_ref.md
Normal file
16
pkg/rpc/jsons/http_stat_stat_ref.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# 统计引用
|
||||
## 定义
|
||||
~~~json
|
||||
{
|
||||
"isPrior": "是否覆盖父级配置",
|
||||
"isOn": "是否启用配置"
|
||||
}
|
||||
~~~
|
||||
|
||||
## 示例
|
||||
~~~json
|
||||
{
|
||||
"isPrior": true,
|
||||
"isOn": true
|
||||
}
|
||||
~~~
|
||||
21
pkg/rpc/jsons/http_websocket_ref.md
Normal file
21
pkg/rpc/jsons/http_websocket_ref.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# WebSocket引用
|
||||
|
||||
## 定义
|
||||
~~~json
|
||||
{
|
||||
"isPrior": "是否覆盖上级配置,true|false",
|
||||
"isOn": "是否启用,true|false",
|
||||
"websocketId": "Websocket配置ID"
|
||||
}
|
||||
~~~
|
||||
其中:
|
||||
* `Websocket配置ID` - 需要调用 `HTTPWebsocketService.CreateHTTPWebsocketRequest()` 生成
|
||||
|
||||
## 示例
|
||||
~~~json
|
||||
{
|
||||
"isPrior": true,
|
||||
"isOn": true,
|
||||
"websocketId": 123
|
||||
}
|
||||
~~~
|
||||
9
pkg/rpc/jsons/server_name.md
Normal file
9
pkg/rpc/jsons/server_name.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# 域名信息
|
||||
|
||||
## 示例
|
||||
~~~json
|
||||
{
|
||||
"name": "example.com",
|
||||
"type": "full"
|
||||
}
|
||||
~~~
|
||||
48
pkg/rpc/jsons/server_names.md
Normal file
48
pkg/rpc/jsons/server_names.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# 域名信息列表
|
||||
|
||||
## 定义
|
||||
~~~
|
||||
[ 域名信息1, 域名信息2, ... ]
|
||||
~~~
|
||||
其中 `域名信息N` 等是单个域名信息定义,具体请参考 {json:server_name}
|
||||
|
||||
## 示例
|
||||
### 示例1:单个域名
|
||||
~~~json
|
||||
[
|
||||
{
|
||||
"name": "example.com",
|
||||
"type": "full"
|
||||
}
|
||||
]
|
||||
~~~
|
||||
|
||||
### 示例2:多个域名
|
||||
~~~json
|
||||
[
|
||||
{
|
||||
"name": "example.com",
|
||||
"type": "full"
|
||||
},
|
||||
{
|
||||
"name": "google.com",
|
||||
"type": "full"
|
||||
},
|
||||
{
|
||||
"name": "facebook.com",
|
||||
"type": "full"
|
||||
}
|
||||
]
|
||||
~~~
|
||||
|
||||
### 示例3:域名合集
|
||||
域名合集效果跟多个域名是一样的,只不过在界面上以一个目录的形式呈现。
|
||||
~~~json
|
||||
[
|
||||
{
|
||||
"name": "",
|
||||
"type": "full",
|
||||
"subNames": ["example.com", "google.com", "facebook.com"]
|
||||
}
|
||||
]
|
||||
~~~
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user