Initial commit
This commit is contained in:
@@ -0,0 +1,875 @@
|
||||
package domainser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"stat/common/log"
|
||||
"stat/common/utils"
|
||||
"stat/models/domainmod"
|
||||
"stat/statg"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
|
||||
"github.com/ddliu/go-httpclient"
|
||||
)
|
||||
|
||||
const (
|
||||
ApiKey = "a45d5d2c6c231f28f9bcccf4092eb246"
|
||||
ApiDomain = "https://api.boce.com"
|
||||
ApiCheckDuration = time.Second * 10
|
||||
ApiCheckTimeout = time.Minute * 2
|
||||
ApiCheckTryCount = 6
|
||||
)
|
||||
|
||||
type NodeCache struct {
|
||||
List NodeList
|
||||
Last time.Time
|
||||
Lock sync.Mutex
|
||||
}
|
||||
|
||||
type MutexResult struct {
|
||||
List domainmod.CheckResultList
|
||||
Lock sync.Mutex
|
||||
}
|
||||
|
||||
var (
|
||||
Cache = &NodeCache{
|
||||
List: make(NodeList, 0),
|
||||
Last: time.Time{},
|
||||
Lock: sync.Mutex{},
|
||||
}
|
||||
)
|
||||
|
||||
func (c *NodeCache) NodeList() (NodeList, error) {
|
||||
ret := make(NodeList, 0)
|
||||
|
||||
c.Lock.Lock()
|
||||
if len(c.List) > 0 && c.Last.After(time.Now().Add(-24*time.Hour)) {
|
||||
for _, node := range c.List {
|
||||
ret = append(ret, node)
|
||||
}
|
||||
c.Lock.Unlock()
|
||||
return ret, nil
|
||||
}
|
||||
c.Lock.Unlock()
|
||||
|
||||
list, err := GetNodeList()
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
if len(list) > 0 {
|
||||
c.Lock.Lock()
|
||||
c.List = list
|
||||
c.Last = time.Now()
|
||||
for _, node := range c.List {
|
||||
ret = append(ret, node)
|
||||
}
|
||||
c.Lock.Unlock()
|
||||
return ret, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("node list is empty")
|
||||
}
|
||||
}
|
||||
|
||||
func GetNodeList() (NodeList, error) {
|
||||
addr := fmt.Sprintf("https://task.boce.com/v3/node/list?key=%s", ApiKey)
|
||||
res := &CommonResp{}
|
||||
tryCount := 0
|
||||
for {
|
||||
err := Get(addr, res, url.Values{})
|
||||
if err != nil {
|
||||
tryCount++
|
||||
if tryCount > ApiCheckTryCount {
|
||||
return nil, err
|
||||
}
|
||||
time.Sleep(ApiCheckDuration)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if res.ErrorCode != 0 {
|
||||
return nil, fmt.Errorf("获取检测节点列表失败:%s", GetApiErrorMessage(res.ErrorCode))
|
||||
}
|
||||
return *res.Data.List, nil
|
||||
}
|
||||
|
||||
func (s *MutexResult) OnFinish(result *domainmod.CheckResult) {
|
||||
s.Lock.Lock()
|
||||
defer s.Lock.Unlock()
|
||||
|
||||
s.List = append(s.List, result)
|
||||
}
|
||||
|
||||
func (s *MutexResult) RealErrors() []error {
|
||||
ret := make([]error, 0)
|
||||
for _, item := range s.List {
|
||||
if item.CheckSucc && item.Error != nil {
|
||||
ret = append(ret, item.Error)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *MutexResult) AllSuccess() bool {
|
||||
for _, item := range s.List {
|
||||
if item.CheckSucc && item.Error != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *MutexResult) JumpErrors() []error {
|
||||
ret := make([]error, 0)
|
||||
for _, item := range s.List {
|
||||
if !item.CheckSucc && item.Error != nil {
|
||||
ret = append(ret, item.Error)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *MutexResult) Reports() string {
|
||||
var ret string
|
||||
for _, item := range s.List {
|
||||
if item.Error != nil {
|
||||
ret += "\n" + item.Error.Error() + "\n"
|
||||
}
|
||||
if len(item.NodeErrors) > 0 {
|
||||
ret += strings.Join(item.NodeErrors, "\n\n")
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *MutexResult) CheckStatus() (passCount int, jumpCount int, errorCount int) {
|
||||
for _, item := range s.List {
|
||||
if item.CheckSucc {
|
||||
if item.Error != nil {
|
||||
errorCount++
|
||||
} else {
|
||||
passCount++
|
||||
}
|
||||
} else {
|
||||
jumpCount++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var ApiErrorMessage = map[int]string{
|
||||
0: "成功",
|
||||
1: "鉴权失败",
|
||||
2: "参数错误",
|
||||
3: "波点不足或波点未配置",
|
||||
4: "生成任务id失败或任务id失效",
|
||||
}
|
||||
|
||||
func GetApiErrorMessage(code int) string {
|
||||
ret := ApiErrorMessage[code]
|
||||
if ret == "" {
|
||||
return "未知错误"
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
var ApiPathMap = map[domainmod.CheckType]ApiPath{
|
||||
domainmod.CheckTypeWeb: {
|
||||
Create: "/v3/task/create/curl",
|
||||
Query: "/v3/task/curl/",
|
||||
Sleep: 5 * time.Minute,
|
||||
NeedCreate: true,
|
||||
},
|
||||
domainmod.CheckTypeDNS: {
|
||||
Create: "/v3/task/create/dig",
|
||||
Query: "/v3/task/dig/",
|
||||
Sleep: 5 * time.Minute,
|
||||
NeedCreate: true,
|
||||
},
|
||||
domainmod.CheckTypePing: {
|
||||
Create: "/v3/task/create/ping",
|
||||
Query: "/v3/task/ping/",
|
||||
Sleep: 5 * time.Minute,
|
||||
NeedCreate: true,
|
||||
},
|
||||
domainmod.CheckTypeHijack: {
|
||||
Create: "/v3/task/create/hijack",
|
||||
Query: "/v3/task/hijack/",
|
||||
Sleep: 5 * time.Minute,
|
||||
NeedCreate: true,
|
||||
},
|
||||
domainmod.CheckTypePollute: {
|
||||
Create: "",
|
||||
Query: "/v3/task/create/pollute",
|
||||
NeedCreate: false,
|
||||
},
|
||||
domainmod.CheckTypeQQ: {
|
||||
Create: "",
|
||||
Query: "/v3/task/create/qq",
|
||||
NeedCreate: false,
|
||||
},
|
||||
domainmod.CheckTypeWeChat: {
|
||||
Create: "",
|
||||
Query: "/v3/task/create/wechat",
|
||||
NeedCreate: false,
|
||||
},
|
||||
domainmod.CheckTypeBlack: {
|
||||
Create: "",
|
||||
Query: "/v3/task/create/blacklist",
|
||||
NeedCreate: false,
|
||||
},
|
||||
domainmod.CheckTypeGFW: {
|
||||
Create: "",
|
||||
Query: "/v3/task/create/wall",
|
||||
NeedCreate: false,
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
httpclient.Defaults(httpclient.Map{
|
||||
"OPT_CONNECTTIMEOUT": 30 * time.Second,
|
||||
"OPT_TIMEOUT": 10 * time.Second,
|
||||
"Accept-Encoding": "gzip,deflate",
|
||||
})
|
||||
}
|
||||
|
||||
func GetChecker(item *domainmod.Domain, mode domainmod.CheckType, onFinish FnCheckFinish, nodes ...Node) Checker {
|
||||
nodeList := make(NodeList, 0)
|
||||
nodeList = append(nodeList, nodes...)
|
||||
switch mode {
|
||||
case domainmod.CheckTypeWeb:
|
||||
return NewWebChecker(item, mode, onFinish, nodeList)
|
||||
case domainmod.CheckTypeDNS:
|
||||
return NewWebChecker(item, mode, onFinish, nodeList)
|
||||
case domainmod.CheckTypePing:
|
||||
return NewWebChecker(item, mode, onFinish, nodeList)
|
||||
case domainmod.CheckTypeHijack:
|
||||
return NewHijackChecker(item, mode, onFinish, nodeList)
|
||||
case domainmod.CheckTypePollute:
|
||||
return NewPolluteChecker(item, mode, onFinish)
|
||||
case domainmod.CheckTypeQQ:
|
||||
return NewQQChecker(item, mode, onFinish)
|
||||
case domainmod.CheckTypeWeChat:
|
||||
return NewWeChatChecker(item, mode, onFinish)
|
||||
case domainmod.CheckTypeBlack:
|
||||
return NewBlackChecker(item, mode, onFinish)
|
||||
case domainmod.CheckTypeGFW:
|
||||
return NewGFWChecker(item, mode, onFinish)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type DomainChecker struct {
|
||||
Items domainmod.DomainList
|
||||
}
|
||||
|
||||
func (c *DomainChecker) StartCheck() error {
|
||||
nodes, err := Cache.NodeList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wg := utils.NewWaitGroup()
|
||||
for _, domain := range c.Items {
|
||||
_domain := domain
|
||||
wg.Add(func() {
|
||||
CheckDomain(_domain, nodes)
|
||||
})
|
||||
}
|
||||
wg.RunWaits()
|
||||
return nil
|
||||
}
|
||||
|
||||
func CheckDomain(domain *domainmod.Domain, nodes NodeList) {
|
||||
ctx := context.Background()
|
||||
begin := time.Now()
|
||||
status := domainmod.CheckStatusChecking
|
||||
doc := &domainmod.DomainDoc{
|
||||
Status: &status,
|
||||
CheckTime: &begin,
|
||||
UpdatedAt: &begin,
|
||||
}
|
||||
if err := domainmod.UpdateOneDomain(ctx, bson.M{"_id": domain.ID}, bson.M{"$set": doc}); err != nil {
|
||||
log.Warn("CheckDomain failed", log.E(err))
|
||||
return
|
||||
}
|
||||
rets := MutexResult{
|
||||
List: make(domainmod.CheckResultList, 0),
|
||||
Lock: sync.Mutex{},
|
||||
}
|
||||
wg := utils.NewWaitGroup()
|
||||
for _, check := range domain.Checks {
|
||||
_check := check
|
||||
wg.Add(func() {
|
||||
if checker := GetChecker(domain, _check, rets.OnFinish, nodes...); checker != nil {
|
||||
time.Sleep(time.Duration(rand.Intn(10)) * time.Second) // 防并发访问
|
||||
checker.Check()
|
||||
}
|
||||
})
|
||||
}
|
||||
wg.RunWaits()
|
||||
end := time.Now()
|
||||
doc = &domainmod.DomainDoc{
|
||||
CheckFinish: &end,
|
||||
UpdatedAt: &end,
|
||||
}
|
||||
passCount, jumpCount, errCount := rets.CheckStatus()
|
||||
doc.Results = &rets.List
|
||||
if errCount > 0 {
|
||||
report := rets.Reports()
|
||||
doc.Report = &report
|
||||
status0 := domainmod.CheckStatusFailed
|
||||
//doc.Status = utils.PValue(domainmod.CheckStatusFailed)
|
||||
if passCount > 0 {
|
||||
//doc.Status = utils.PValue(domainmod.CheckStatusPartPass)
|
||||
status0 = domainmod.CheckStatusPartPass
|
||||
}
|
||||
doc.Status = &status0
|
||||
} else {
|
||||
if passCount > 0 {
|
||||
if jumpCount > 0 {
|
||||
//doc.Status = utils.PValue(domainmod.CheckStatusPartPass)
|
||||
//doc.Report = utils.PValue(rets.Reports())
|
||||
status0 := domainmod.CheckStatusPartPass
|
||||
report := rets.Reports()
|
||||
doc.Status = &status0
|
||||
doc.Report = &report
|
||||
} else {
|
||||
//doc.Status = utils.PValue(domainmod.CheckStatusSucc)
|
||||
//doc.Report = utils.PValue("完全通过")
|
||||
status0 := domainmod.CheckStatusSucc
|
||||
report := "完全通过"
|
||||
doc.Status = &status0
|
||||
doc.Report = &report
|
||||
}
|
||||
} else {
|
||||
//doc.Status = utils.PValue(domainmod.CheckStatusNone)
|
||||
//doc.Report = utils.PValue(rets.Reports())
|
||||
status0 := domainmod.CheckStatusNone
|
||||
report := rets.Reports()
|
||||
doc.Status = &status0
|
||||
doc.Report = &report
|
||||
}
|
||||
}
|
||||
|
||||
if err := domainmod.UpdateOneDomain(ctx, bson.M{"_id": domain.ID}, bson.M{"$set": doc}); err != nil {
|
||||
log.Warn("CheckDomain failed", log.E(err))
|
||||
return
|
||||
}
|
||||
|
||||
if errCount > 0 {
|
||||
text := fmt.Sprintf("域名检测:【%s】\n", domain.Name)
|
||||
text += fmt.Sprintf("开始时间:【%s】\n", begin.Format("2006-01-02T15:04:05"))
|
||||
text += fmt.Sprintf("结束时间:【%s】\n", end.Format("2006-01-02T15:04:05"))
|
||||
text += fmt.Sprintf("检测用时:【%s】\n", end.Sub(begin))
|
||||
text += fmt.Sprintf("检测结果:【%s】\n", *doc.Status)
|
||||
for _, result := range rets.List {
|
||||
if result.CheckSucc {
|
||||
if result.Error == nil {
|
||||
text += fmt.Sprintf("%s: %s\n", result.CheckType.Name(), "\U0001F7E2 通过")
|
||||
} else {
|
||||
if result.NodeCount == 0 {
|
||||
text += fmt.Sprintf("%s: %s\n", result.CheckType.Name(), "🔴 未通过")
|
||||
} else {
|
||||
if result.SuccRate < result.MinRate {
|
||||
if result.SuccRate < 25 {
|
||||
text += fmt.Sprintf("%s: %s\n", result.CheckType.Name(), "🔴 未通过")
|
||||
} else {
|
||||
text += fmt.Sprintf("%s: %s\n", result.CheckType.Name(), "\U0001F7E0 通过")
|
||||
}
|
||||
} else {
|
||||
text += fmt.Sprintf("%s: %s\n", result.CheckType.Name(), "\U0001F7E2 通过")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
text += fmt.Sprintf("%s: %s\n", result.CheckType.Name(), "🕓 超时")
|
||||
}
|
||||
}
|
||||
if domain.Redirected {
|
||||
text += fmt.Sprintf("域名跳转:【已做301跳转✅】\n")
|
||||
text += fmt.Sprintf("跳转地址:【==>%s】\n", domain.RedirectDomain)
|
||||
} else {
|
||||
text += fmt.Sprintf("域名跳转:【未做301跳转🈚】\n")
|
||||
}
|
||||
|
||||
statg.Bot.SendDomainMessage(text)
|
||||
}
|
||||
}
|
||||
|
||||
type WebChecker struct {
|
||||
Item *domainmod.Domain
|
||||
Mode domainmod.CheckType
|
||||
Finish FnCheckFinish
|
||||
Nodes NodeList
|
||||
}
|
||||
|
||||
func NewWebChecker(item *domainmod.Domain, mode domainmod.CheckType, onFinish FnCheckFinish, nodes NodeList) *WebChecker {
|
||||
return &WebChecker{
|
||||
Item: item,
|
||||
Mode: mode,
|
||||
Finish: onFinish,
|
||||
Nodes: nodes,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *WebChecker) Check() error {
|
||||
ret := &domainmod.CheckResult{
|
||||
Domain: s.Item.Name,
|
||||
CheckType: s.Mode,
|
||||
BeginTime: time.Now(),
|
||||
MinRate: s.Item.MinRate,
|
||||
}
|
||||
defer s.Finish(ret)
|
||||
api, ok := ApiPathMap[s.Mode]
|
||||
if !ok {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("域名%s的检测类型%d不支持", s.Item.Name, s.Mode)
|
||||
return ret.Error
|
||||
}
|
||||
addr, err := api.GetCreateUrl(url.Values{
|
||||
"node_ids": []string{s.Nodes.SIdList(s.Item.MaxNodes)},
|
||||
"host": []string{s.Item.Name},
|
||||
})
|
||||
if err != nil {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = err
|
||||
return ret.Error
|
||||
}
|
||||
tryCount := 0
|
||||
res := &CommonResp{}
|
||||
for {
|
||||
err = Get(addr, res, url.Values{})
|
||||
if err != nil {
|
||||
tryCount++
|
||||
if tryCount > ApiCheckTryCount {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("创建【%s】的【%s】检测任务超时:%s", s.Item.Name, s.Mode.Name(), err.Error())
|
||||
return ret.Error
|
||||
}
|
||||
time.Sleep(ApiCheckDuration)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if res.ErrorCode != 0 {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("创建【%s】的【%s】检测任务失败:%s", s.Item.Name, s.Mode.Name(), GetApiErrorMessage(res.ErrorCode))
|
||||
return ret.Error
|
||||
}
|
||||
// 获取查询ID
|
||||
if res.Data.Id == nil || *res.Data.Id == "" {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("创建【%s】的【%s】检测任务失败:任务ID未空", s.Item.Name, s.Mode.Name())
|
||||
return ret.Error
|
||||
}
|
||||
TaskId := *res.Data.Id
|
||||
// 去查询结果
|
||||
tryCount = 0
|
||||
addr, err = api.GetQueryUrlWithTaskId(TaskId, url.Values{})
|
||||
end := time.Now().Add(ApiCheckTimeout)
|
||||
for {
|
||||
time.Sleep(ApiCheckDuration)
|
||||
webResp := &WebResp{}
|
||||
err = Get(addr, webResp, url.Values{})
|
||||
if err != nil {
|
||||
tryCount++
|
||||
if tryCount > ApiCheckTryCount {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("查询【%s】的【%s】检测结果超时:%s", s.Item.Name, s.Mode.Name(), err.Error())
|
||||
return ret.Error
|
||||
}
|
||||
continue
|
||||
}
|
||||
ret.CheckSucc = webResp.Done
|
||||
if webResp.Done {
|
||||
for _, node := range webResp.List {
|
||||
if node.ErrorCode != 0 {
|
||||
ret.FailedCount++
|
||||
ret.NodeErrors = append(ret.NodeErrors, node.ErrorString())
|
||||
} else {
|
||||
ret.NodeCount++
|
||||
if node.HttpCode == http.StatusOK {
|
||||
ret.SuccCount++
|
||||
} else {
|
||||
ret.NodeErrors = append(ret.NodeErrors, node.ErrorString())
|
||||
}
|
||||
}
|
||||
}
|
||||
if ret.NodeCount > 0 {
|
||||
ret.SuccRate = float64(ret.SuccCount*100) / float64(ret.NodeCount)
|
||||
if ret.SuccRate < ret.MinRate {
|
||||
ret.Error = fmt.Errorf("节点通过率%.1f%%<%.1f%%,未通过!", ret.SuccRate, ret.MinRate)
|
||||
}
|
||||
}
|
||||
break
|
||||
} else {
|
||||
if end.Before(time.Now()) {
|
||||
ret.Error = fmt.Errorf("查询【%s】的【%s】检测超时", s.Item.Name, s.Mode.Name())
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
ret.EndTime = time.Now()
|
||||
return ret.Error
|
||||
}
|
||||
|
||||
type HijackChecker struct {
|
||||
Item *domainmod.Domain
|
||||
Mode domainmod.CheckType
|
||||
Finish FnCheckFinish
|
||||
Nodes NodeList
|
||||
}
|
||||
|
||||
func NewHijackChecker(item *domainmod.Domain, mode domainmod.CheckType, onFinish FnCheckFinish, nodes NodeList) *HijackChecker {
|
||||
return &HijackChecker{
|
||||
Item: item,
|
||||
Mode: mode,
|
||||
Finish: onFinish,
|
||||
Nodes: nodes,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *HijackChecker) Check() error {
|
||||
ret := &domainmod.CheckResult{
|
||||
Domain: s.Item.Name,
|
||||
CheckType: s.Mode,
|
||||
BeginTime: time.Now(),
|
||||
MinRate: s.Item.MinRate,
|
||||
}
|
||||
defer s.Finish(ret)
|
||||
api, ok := ApiPathMap[s.Mode]
|
||||
if !ok {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("域名%s的检测类型%d不支持", s.Item.Name, s.Mode)
|
||||
return ret.Error
|
||||
}
|
||||
addr, err := api.GetCreateUrl(url.Values{
|
||||
"node_ids": []string{s.Nodes.SIdList(s.Item.MaxNodes)},
|
||||
"host": []string{s.Item.Name},
|
||||
})
|
||||
if err != nil {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = err
|
||||
return ret.Error
|
||||
}
|
||||
tryCount := 0
|
||||
res := &CommonResp{}
|
||||
for {
|
||||
err = Get(addr, res, url.Values{})
|
||||
if err != nil {
|
||||
tryCount++
|
||||
if tryCount > ApiCheckTryCount {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("创建【%s】的【%s】检测任务超时:%s", s.Item.Name, s.Mode.Name(), err.Error())
|
||||
return ret.Error
|
||||
}
|
||||
time.Sleep(ApiCheckDuration)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if res.ErrorCode != 0 {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("创建【%s】的【%s】检测任务失败:%s", s.Item.Name, s.Mode.Name(), GetApiErrorMessage(res.ErrorCode))
|
||||
return ret.Error
|
||||
}
|
||||
// 获取查询ID
|
||||
if res.Data.Id == nil || *res.Data.Id == "" {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("创建【%s】的【%s】检测任务失败:任务ID未空", s.Item.Name, s.Mode.Name())
|
||||
return ret.Error
|
||||
}
|
||||
TaskId := *res.Data.Id
|
||||
// 去查询结果
|
||||
tryCount = 0
|
||||
addr, err = api.GetQueryUrlWithTaskId(TaskId, url.Values{})
|
||||
end := time.Now().Add(ApiCheckTimeout)
|
||||
for {
|
||||
time.Sleep(ApiCheckDuration)
|
||||
webResp := &HijackResp{}
|
||||
err = Get(addr, webResp, url.Values{})
|
||||
if err != nil {
|
||||
tryCount++
|
||||
if tryCount > ApiCheckTryCount {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("查询【%s】的【%s】接口错误【%d】次,超过最大重试次数:%s", s.Item.Name, s.Mode.Name(), tryCount, err.Error())
|
||||
return ret.Error
|
||||
}
|
||||
continue
|
||||
}
|
||||
ret.CheckSucc = webResp.Done
|
||||
if webResp.Done {
|
||||
for _, node := range webResp.List {
|
||||
ret.NodeCount++
|
||||
if node.Hijack {
|
||||
ret.FailedCount++
|
||||
ret.NodeErrors = append(ret.NodeErrors, node.ErrorString())
|
||||
} else {
|
||||
ret.SuccCount++
|
||||
}
|
||||
}
|
||||
if ret.NodeCount > 0 {
|
||||
ret.SuccRate = float64(ret.SuccCount*100) / float64(ret.NodeCount)
|
||||
if ret.SuccRate < ret.MinRate {
|
||||
ret.Error = fmt.Errorf("节点通过率%.1f%%<%.1f%%,未通过!", ret.SuccRate, ret.MinRate)
|
||||
}
|
||||
}
|
||||
break
|
||||
} else {
|
||||
if end.Before(time.Now()) {
|
||||
ret.Error = fmt.Errorf("查询【%s】的【%s】检测超时", s.Item.Name, s.Mode.Name())
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
ret.EndTime = time.Now()
|
||||
return ret.Error
|
||||
}
|
||||
|
||||
type FuncCheckResult func(ret *domainmod.CheckResult, data map[string]int)
|
||||
|
||||
func QueryCheck(item *domainmod.Domain, mode domainmod.CheckType, onFinish FnCheckFinish, onCheck FuncCheckResult) error {
|
||||
ret := &domainmod.CheckResult{
|
||||
Domain: item.Name,
|
||||
CheckType: mode,
|
||||
BeginTime: time.Now(),
|
||||
MinRate: item.MinRate,
|
||||
}
|
||||
defer onFinish(ret)
|
||||
api, ok := ApiPathMap[mode]
|
||||
if !ok {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("查询接口地址【%s】的【%s】出错", item.Name, mode.Name())
|
||||
return ret.Error
|
||||
}
|
||||
addr, err := api.GetQueryUrl(url.Values{"host": []string{item.Name}})
|
||||
polluteResp := &PolluteResp{}
|
||||
tryCount := 0
|
||||
for {
|
||||
tryCount++
|
||||
err = Get(addr, polluteResp, url.Values{})
|
||||
if err != nil {
|
||||
if tryCount > ApiCheckTryCount {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("查询【%s】的【%s】接口错误【%d】次,超过最大重试次数:%s", item.Name, mode.Name(), tryCount, err.Error())
|
||||
return ret.Error
|
||||
}
|
||||
time.Sleep(ApiCheckDuration)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if polluteResp.ErrorCode != 0 {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("查询【%s】的【%s】返回值不是成功:%s", item.Name, mode.Name(), GetApiErrorMessage(polluteResp.ErrorCode))
|
||||
return ret.Error
|
||||
}
|
||||
ret.CheckSucc = true
|
||||
onCheck(ret, polluteResp.Data)
|
||||
ret.EndTime = time.Now()
|
||||
return ret.Error
|
||||
}
|
||||
|
||||
type PolluteChecker struct {
|
||||
Item *domainmod.Domain
|
||||
Mode domainmod.CheckType
|
||||
Finish FnCheckFinish
|
||||
}
|
||||
|
||||
func NewPolluteChecker(item *domainmod.Domain, mode domainmod.CheckType, onFinish FnCheckFinish) *PolluteChecker {
|
||||
return &PolluteChecker{
|
||||
Item: item,
|
||||
Mode: mode,
|
||||
Finish: onFinish,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PolluteChecker) Check() error {
|
||||
return QueryCheck(s.Item, s.Mode, s.Finish, func(ret *domainmod.CheckResult, data map[string]int) {
|
||||
if code, ok := data[s.Item.Name]; ok { //(-1 未注册,0 正常, 1 污染)
|
||||
if ok {
|
||||
if code == 0 {
|
||||
ret.SuccRate = 100.0
|
||||
} else {
|
||||
if code == 1 {
|
||||
ret.Error = fmt.Errorf("域名被污染")
|
||||
} else if code == -1 {
|
||||
ret.Error = fmt.Errorf("域名未注册")
|
||||
} else {
|
||||
ret.Error = fmt.Errorf("未知错误码%d", code)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret.Error = fmt.Errorf("返回值中未检测到域名")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type QQChecker struct {
|
||||
Item *domainmod.Domain
|
||||
Mode domainmod.CheckType
|
||||
Finish FnCheckFinish
|
||||
}
|
||||
|
||||
func NewQQChecker(item *domainmod.Domain, mode domainmod.CheckType, onFinish FnCheckFinish) *QQChecker {
|
||||
return &QQChecker{
|
||||
Item: item,
|
||||
Mode: mode,
|
||||
Finish: onFinish,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *QQChecker) Check() error {
|
||||
return QueryCheck(s.Item, s.Mode, s.Finish, func(ret *domainmod.CheckResult, data map[string]int) {
|
||||
if code, ok := data[s.Item.Name]; ok { //1正常, 2拦截, 3-检测失败
|
||||
if ok {
|
||||
if code == 1 {
|
||||
ret.CheckSucc = true
|
||||
ret.SuccRate = 100.0
|
||||
} else {
|
||||
if code == 2 {
|
||||
ret.CheckSucc = true
|
||||
ret.Error = fmt.Errorf("已被拦截")
|
||||
} else if code == 3 {
|
||||
ret.Error = fmt.Errorf("检测失败")
|
||||
} else {
|
||||
ret.Error = fmt.Errorf("检测值【%d】不知道意义", code)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret.Error = fmt.Errorf("返回值中未检测到域名")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type WeChatChecker struct {
|
||||
Item *domainmod.Domain
|
||||
Mode domainmod.CheckType
|
||||
Finish FnCheckFinish
|
||||
}
|
||||
|
||||
func NewWeChatChecker(item *domainmod.Domain, mode domainmod.CheckType, onFinish FnCheckFinish) *WeChatChecker {
|
||||
return &WeChatChecker{
|
||||
Item: item,
|
||||
Mode: mode,
|
||||
Finish: onFinish,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *WeChatChecker) Check() error {
|
||||
return QueryCheck(s.Item, s.Mode, s.Finish, func(ret *domainmod.CheckResult, data map[string]int) {
|
||||
if code, ok := data[s.Item.Name]; ok { //1正常, 2拦截(域名被封), 3-检测失败,4-拦截(非微信官方链接,需跳转访问)
|
||||
if ok {
|
||||
if code == 1 || code == 4 {
|
||||
ret.CheckSucc = true
|
||||
ret.SuccRate = 100.0
|
||||
} else {
|
||||
if code == 2 {
|
||||
ret.CheckSucc = true
|
||||
ret.Error = fmt.Errorf("已被拦截")
|
||||
} else if code == 3 {
|
||||
ret.Error = fmt.Errorf("检测失败")
|
||||
} else {
|
||||
ret.Error = fmt.Errorf("检测值【%d】不知道意义", code)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret.Error = fmt.Errorf("返回值中未检测到域名")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type BlackChecker struct {
|
||||
Item *domainmod.Domain
|
||||
Mode domainmod.CheckType
|
||||
Finish FnCheckFinish
|
||||
}
|
||||
|
||||
func NewBlackChecker(item *domainmod.Domain, mode domainmod.CheckType, onFinish FnCheckFinish) *BlackChecker {
|
||||
return &BlackChecker{
|
||||
Item: item,
|
||||
Mode: mode,
|
||||
Finish: onFinish,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *BlackChecker) Check() error {
|
||||
return QueryCheck(s.Item, s.Mode, s.Finish, func(ret *domainmod.CheckResult, data map[string]int) {
|
||||
if code, ok := data[s.Item.Name]; ok { //1-正常, 2-在黑名单
|
||||
if ok {
|
||||
if code == 1 {
|
||||
ret.CheckSucc = true
|
||||
ret.SuccRate = 100.0
|
||||
} else {
|
||||
if code == 2 {
|
||||
ret.CheckSucc = true
|
||||
ret.Error = fmt.Errorf("在黑名单")
|
||||
} else {
|
||||
ret.Error = fmt.Errorf("检测值【%d】不知道意义", code)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret.Error = fmt.Errorf("返回值中未检测到域名")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type GFWChecker struct {
|
||||
Item *domainmod.Domain
|
||||
Mode domainmod.CheckType
|
||||
Finish FnCheckFinish
|
||||
}
|
||||
|
||||
func NewGFWChecker(item *domainmod.Domain, mode domainmod.CheckType, onFinish FnCheckFinish) *GFWChecker {
|
||||
return &GFWChecker{
|
||||
Item: item,
|
||||
Mode: mode,
|
||||
Finish: onFinish,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *GFWChecker) Check() error {
|
||||
return QueryCheck(s.Item, s.Mode, s.Finish, func(ret *domainmod.CheckResult, data map[string]int) {
|
||||
if code, ok := data[s.Item.Name]; ok { //0 正常, 1 被墙, 2 疑似被墙, 3 无资源请重试,4 域名格式错误
|
||||
if ok {
|
||||
if code == 0 { // 正常
|
||||
ret.CheckSucc = true
|
||||
ret.SuccRate = 100.0
|
||||
} else {
|
||||
if code == 1 {
|
||||
ret.CheckSucc = true
|
||||
ret.Error = fmt.Errorf("域名被墙")
|
||||
} else if code == 2 {
|
||||
ret.CheckSucc = true
|
||||
ret.Error = fmt.Errorf("疑似被墙")
|
||||
} else if code == 3 {
|
||||
ret.Error = fmt.Errorf("无资源请重试")
|
||||
} else if code == 4 {
|
||||
ret.Error = fmt.Errorf("域名格式错误")
|
||||
} else {
|
||||
ret.Error = fmt.Errorf("检测值【%d】不知道意义", code)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret.Error = fmt.Errorf("返回值中未检测到域名")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package domainser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"stat/models/domainmod"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FnCheckFinish func(result *domainmod.CheckResult)
|
||||
|
||||
type Checker interface {
|
||||
Check() error
|
||||
}
|
||||
|
||||
type ApiPath struct {
|
||||
Create string
|
||||
Query string
|
||||
Sleep time.Duration
|
||||
NeedCreate bool
|
||||
}
|
||||
|
||||
type Node struct {
|
||||
Id int `json:"id"`
|
||||
NodeName string `json:"node_name"`
|
||||
IspName string `json:"isp_name"`
|
||||
IspCode int `json:"isp_code"`
|
||||
Rand int
|
||||
}
|
||||
|
||||
type NodeList []Node
|
||||
|
||||
func (n NodeList) Len() int { return len(n) }
|
||||
func (n NodeList) Less(i, j int) bool {
|
||||
return n[i].Rand < n[j].Rand
|
||||
}
|
||||
func (n NodeList) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
|
||||
|
||||
func (n NodeList) SIdList(max int32) string {
|
||||
if max < 1 {
|
||||
max = 1
|
||||
}
|
||||
for _, node := range n {
|
||||
node.Rand = rand.Int()
|
||||
}
|
||||
sort.Sort(n)
|
||||
ss := make([]string, 0)
|
||||
for _, node := range n {
|
||||
if strings.Contains(node.IspName, "电信") || strings.Contains(node.IspName, "移动") || strings.Contains(node.IspName, "联通") {
|
||||
ss = append(ss, fmt.Sprintf("%d", node.Id))
|
||||
}
|
||||
}
|
||||
if len(ss) > int(max) {
|
||||
ss = ss[:max]
|
||||
}
|
||||
return strings.Join(ss, ",")
|
||||
}
|
||||
|
||||
type CommonResp struct {
|
||||
ErrorCode int `json:"error_code"`
|
||||
Error string `json:"error"`
|
||||
Data struct {
|
||||
Id *string `json:"id"`
|
||||
List *NodeList `json:"list"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type DomainResp struct {
|
||||
ErrorCode int `json:"error_code"`
|
||||
Error string `json:"error"`
|
||||
Data map[string]int `json:"data"`
|
||||
}
|
||||
|
||||
type WebTestNode struct {
|
||||
NodeId int `json:"node_id"`
|
||||
NodeName string `json:"node_name"`
|
||||
Host string `json:"host"`
|
||||
OriginIp string `json:"origin_ip"`
|
||||
HttpCode int `json:"http_code"`
|
||||
RemoteIp string `json:"remote_ip"`
|
||||
SizeDownload int `json:"size_download"`
|
||||
SpeedDownload int `json:"speed_download"`
|
||||
TimeConnect float64 `json:"time_connect"`
|
||||
TimeNamelookup float64 `json:"time_namelookup"`
|
||||
TimeStarttransfer float64 `json:"time_starttransfer"`
|
||||
TimeTotal float64 `json:"time_total"`
|
||||
ReportSource string `json:"report_source"`
|
||||
SessionId string `json:"session_id"`
|
||||
IpRegion string `json:"ip_region"`
|
||||
IpIsp string `json:"ip_isp"`
|
||||
ErrorCode int `json:"error_code"`
|
||||
Error string `json:"error"`
|
||||
TimeId string `json:"time_id"`
|
||||
}
|
||||
type WebTestNodeList []*WebTestNode
|
||||
|
||||
func (n *WebTestNode) ErrorString() string {
|
||||
return fmt.Sprintf("节点【%s】检测失败", n.NodeName)
|
||||
}
|
||||
|
||||
type WebResp struct {
|
||||
Id string `json:"id"`
|
||||
MaxNode int `json:"max_node"`
|
||||
Done bool `json:"done"`
|
||||
List WebTestNodeList `json:"list"`
|
||||
}
|
||||
|
||||
type HijackTestNode struct {
|
||||
NodeId int `json:"node_id"`
|
||||
Hijack bool `json:"hijack"`
|
||||
NodeName string `json:"node_name"`
|
||||
RemoteIp string `json:"remote_ip"`
|
||||
Url string `json:"url"`
|
||||
ReportSource string `json:"report_source"`
|
||||
}
|
||||
|
||||
func (n *HijackTestNode) ErrorString() string {
|
||||
return fmt.Sprintf("节点【%s】已劫持", n.NodeName)
|
||||
}
|
||||
|
||||
type HijackResp struct {
|
||||
Done bool `json:"done"`
|
||||
HijackNum int `json:"hijack_num"`
|
||||
Id string `json:"id"`
|
||||
List []*HijackTestNode `json:"list"`
|
||||
MaxNode int `json:"max_node"`
|
||||
}
|
||||
|
||||
type PolluteResp struct {
|
||||
ErrorCode int `json:"error_code"`
|
||||
Error string `json:"error"`
|
||||
Data map[string]int `json:"data"`
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package domainser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/common/enum/consts"
|
||||
"stat/common/log"
|
||||
"stat/common/statutils"
|
||||
"stat/models/domainmod"
|
||||
"stat/statg"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
var (
|
||||
checkCron *cron.Cron
|
||||
)
|
||||
|
||||
func init() {
|
||||
checkCron = cron.New(cron.WithSeconds(), cron.WithChain(cron.SkipIfStillRunning(&log.Logger{}))) // 秒级定时任务
|
||||
}
|
||||
|
||||
// Start 定时器任务启动
|
||||
func Start() {
|
||||
log.Info("domain checker start...")
|
||||
ctx := context.Background()
|
||||
if statg.Conf.RunMod != consts.RunModRelease {
|
||||
return
|
||||
}
|
||||
_, _ = checkCron.AddFunc("0 0/5 * * * ?", func() {
|
||||
statutils.ServerMon.RunWithName(func() error {
|
||||
return DomainCheck(ctx)
|
||||
}, statutils.GetFuncName(DomainCheck))
|
||||
})
|
||||
checkCron.Start()
|
||||
}
|
||||
|
||||
func DomainCheck(ctx context.Context) error {
|
||||
list, err := domainmod.FindManyDomain(ctx, bson.M{
|
||||
"enable": true,
|
||||
"checkTime": bson.M{"$lt": time.Now().Add(-10 * time.Minute)},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
redirectedChecks := []domainmod.CheckType{domainmod.CheckTypePollute}
|
||||
unRedirectedChecks := []domainmod.CheckType{domainmod.CheckTypePollute, domainmod.CheckTypeGFW}
|
||||
|
||||
domains := make(domainmod.DomainList, 0)
|
||||
for _, item := range list {
|
||||
if item.Redirected {
|
||||
item.Checks = redirectedChecks
|
||||
} else {
|
||||
item.Checks = unRedirectedChecks
|
||||
}
|
||||
if item.Status == domainmod.CheckStatusNone {
|
||||
if item.CheckTime.Before(time.Now().Add(-10 * time.Minute)) {
|
||||
domains = append(domains, item)
|
||||
}
|
||||
} else {
|
||||
if item.CheckTime.Before(time.Now().Add(time.Duration(-item.CycleHour) * time.Hour)) {
|
||||
domains = append(domains, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(domains) == 0 {
|
||||
return nil
|
||||
}
|
||||
domainChecker := &DomainChecker{
|
||||
Items: domains,
|
||||
}
|
||||
return domainChecker.StartCheck()
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package domainser
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/ddliu/go-httpclient"
|
||||
)
|
||||
|
||||
func ParseDomain(name string) (string, error) {
|
||||
name = strings.TrimSpace(name)
|
||||
if strings.Contains(strings.ToLower(name), "http:") || strings.Contains(strings.ToLower(name), "https:") {
|
||||
u, err := url.Parse(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if u.Host == "" {
|
||||
return "", fmt.Errorf("没有检测到有效的域名")
|
||||
}
|
||||
if net.ParseIP(u.Host) != nil {
|
||||
return "", fmt.Errorf("只能填写域名,不能填写IP地址")
|
||||
}
|
||||
host, _, err := net.SplitHostPort(u.Host)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("域名无效")
|
||||
}
|
||||
return host, nil
|
||||
} else {
|
||||
re, err := regexp.Compile(`^(?:[_a-z0-9](?:[_a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?)?$`)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !re.MatchString(name) {
|
||||
return "", fmt.Errorf("请输入正确的域名")
|
||||
}
|
||||
return name, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (n *ApiPath) GetCreateUrl(param url.Values) (string, error) {
|
||||
param.Set("key", ApiKey)
|
||||
if n.NeedCreate {
|
||||
base, err := url.Parse(ApiDomain + n.Create)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
base.RawQuery = param.Encode()
|
||||
return base.String(), nil
|
||||
} else {
|
||||
return "", fmt.Errorf("do not need create url")
|
||||
}
|
||||
}
|
||||
|
||||
func (n *ApiPath) GetQueryUrlWithTaskId(taskId string, param url.Values) (string, error) {
|
||||
param.Set("key", ApiKey)
|
||||
base, err := url.Parse(ApiDomain + n.Query + taskId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
base.RawQuery = param.Encode()
|
||||
return base.String(), nil
|
||||
}
|
||||
|
||||
func (n *ApiPath) GetQueryUrl(param url.Values) (string, error) {
|
||||
param.Set("key", ApiKey)
|
||||
base, err := url.Parse(ApiDomain + n.Query)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
base.RawQuery = param.Encode()
|
||||
return base.String(), nil
|
||||
}
|
||||
|
||||
func Get(url string, resp interface{}, param url.Values) error {
|
||||
res, err := httpclient.Get(url, param)
|
||||
if err != nil {
|
||||
return fmt.Errorf("访问检测网站失败:%s", err.Error())
|
||||
}
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("访问检测网站失败, HTTP Code:%d, Error:%s", res.StatusCode, res.Status)
|
||||
}
|
||||
data, err := res.ReadAll()
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取检测网站返回数据失败:%s", err.Error())
|
||||
}
|
||||
if err = json.Unmarshal(data, resp); err != nil {
|
||||
return fmt.Errorf("解码检测网站返回数据失败:%s", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package domainser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"stat/api/proto"
|
||||
"stat/common/stdLog"
|
||||
"stat/common/utils"
|
||||
"stat/models/appInfoMod"
|
||||
"stat/models/domainmod"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
// 获取DomainInfo列表
|
||||
func GetDomainInfoList(ctx context.Context, param proto.ListDomainInfoReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.ListDomainInfoRes{
|
||||
Total: 0,
|
||||
List: make(domainmod.DomainList, 0),
|
||||
}
|
||||
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
|
||||
filter := bson.M{}
|
||||
if param.Name != nil {
|
||||
filter["name"] = bson.M{"$regex": fmt.Sprintf("^%s", *param.Name)}
|
||||
}
|
||||
if param.AppId != nil {
|
||||
filter["appId"] = *param.AppId
|
||||
}
|
||||
if param.Enable != nil {
|
||||
filter["enable"] = *param.Enable
|
||||
}
|
||||
if param.Status != nil {
|
||||
filter["status"] = *param.Status
|
||||
}
|
||||
if param.Category != nil {
|
||||
filter["category"] = *param.Category
|
||||
}
|
||||
if param.Check != nil {
|
||||
filter["checks"] = bson.M{"$in": *param.Check}
|
||||
}
|
||||
opts := options.Find().SetSkip(skip).SetLimit(limit)
|
||||
count, err := domainmod.CountDomains(ctx, filter)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
aList, err := domainmod.FindManyDomain(ctx, filter, opts)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
appMap, _ := appInfoMod.GetAllAppIdNameMap(ctx)
|
||||
for _, item := range aList {
|
||||
item.AppName = appMap[item.AppId]
|
||||
}
|
||||
|
||||
resp.Total = count
|
||||
resp.List = aList
|
||||
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 添加DomainInfo
|
||||
func AddDomainInfo(ctx context.Context, param proto.AddDomainInfoReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
name, err := ParseDomain(param.Name)
|
||||
if err != nil {
|
||||
code = stdLog.ParamError
|
||||
return
|
||||
}
|
||||
param.Name = name
|
||||
now := time.Now()
|
||||
param.UpdatedAt = now
|
||||
param.CreatedAt = now
|
||||
|
||||
if param.CycleHour < 1 {
|
||||
param.CycleHour = 1
|
||||
}
|
||||
|
||||
if err = domainmod.InsertOneDomain(ctx, ¶m.Domain); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 更新DomainInfo
|
||||
func UpdateDomainInfo(ctx context.Context, param proto.UpdateDomainInfoReq) (code stdLog.CodeType,
|
||||
data interface{}, err error) {
|
||||
now := time.Now()
|
||||
param.Update.UpdatedAt = &now
|
||||
if param.Update.Name != nil {
|
||||
var name string
|
||||
name, err = ParseDomain(*param.Update.Name)
|
||||
if err != nil {
|
||||
code = stdLog.ParamError
|
||||
return
|
||||
}
|
||||
param.Update.Name = &name
|
||||
}
|
||||
if param.Update.CycleHour != nil {
|
||||
if *param.Update.CycleHour < 1 {
|
||||
hour := int64(1)
|
||||
param.Update.CycleHour = &hour
|
||||
}
|
||||
}
|
||||
|
||||
if err = domainmod.UpdateOneDomain(ctx, bson.M{"_id": param.Id}, bson.M{"$set": param.Update}); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 删除DomainInfo
|
||||
func DelDomainInfo(ctx context.Context, param proto.DelDomainInfoReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
if _, err = domainmod.DelOneDomain(ctx, bson.M{"_id": param.Id}); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user