mirror of
https://github.com/crawlab-team/crawlab.git
synced 2026-01-21 17:21:09 +01:00
refactor: removed unnecessary code
This commit is contained in:
@@ -1,91 +0,0 @@
|
|||||||
package apps
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/apex/log"
|
|
||||||
"github.com/crawlab-team/crawlab/core/color"
|
|
||||||
"github.com/crawlab-team/crawlab/core/config"
|
|
||||||
"github.com/crawlab-team/crawlab/core/container"
|
|
||||||
grpcclient "github.com/crawlab-team/crawlab/core/grpc/client"
|
|
||||||
grpcserver "github.com/crawlab-team/crawlab/core/grpc/server"
|
|
||||||
modelsclient "github.com/crawlab-team/crawlab/core/models/client"
|
|
||||||
modelsservice "github.com/crawlab-team/crawlab/core/models/service"
|
|
||||||
nodeconfig "github.com/crawlab-team/crawlab/core/node/config"
|
|
||||||
"github.com/crawlab-team/crawlab/core/schedule"
|
|
||||||
"github.com/crawlab-team/crawlab/core/spider/admin"
|
|
||||||
"github.com/crawlab-team/crawlab/core/stats"
|
|
||||||
"github.com/crawlab-team/crawlab/core/task/handler"
|
|
||||||
"github.com/crawlab-team/crawlab/core/task/scheduler"
|
|
||||||
taskstats "github.com/crawlab-team/crawlab/core/task/stats"
|
|
||||||
"github.com/crawlab-team/crawlab/core/user"
|
|
||||||
"github.com/crawlab-team/crawlab/core/utils"
|
|
||||||
"github.com/crawlab-team/crawlab/trace"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Start(app App) {
|
|
||||||
start(app)
|
|
||||||
}
|
|
||||||
|
|
||||||
func start(app App) {
|
|
||||||
app.Init()
|
|
||||||
go app.Start()
|
|
||||||
app.Wait()
|
|
||||||
app.Stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
func DefaultWait() {
|
|
||||||
utils.DefaultWait()
|
|
||||||
}
|
|
||||||
|
|
||||||
func initModule(name string, fn func() error) (err error) {
|
|
||||||
if err := fn(); err != nil {
|
|
||||||
log.Error(fmt.Sprintf("init %s error: %s", name, err.Error()))
|
|
||||||
_ = trace.TraceError(err)
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
log.Info(fmt.Sprintf("initialized %s successfully", name))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func initApp(name string, app App) {
|
|
||||||
_ = initModule(name, func() error {
|
|
||||||
app.Init()
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var injectors = []interface{}{
|
|
||||||
modelsservice.GetService,
|
|
||||||
modelsclient.NewServiceDelegate,
|
|
||||||
modelsclient.NewNodeServiceDelegate,
|
|
||||||
modelsclient.NewSpiderServiceDelegate,
|
|
||||||
modelsclient.NewTaskServiceDelegate,
|
|
||||||
modelsclient.NewTaskStatServiceDelegate,
|
|
||||||
modelsclient.NewEnvironmentServiceDelegate,
|
|
||||||
grpcclient.NewClient,
|
|
||||||
grpcclient.NewPool,
|
|
||||||
grpcserver.NewModelDelegateServer,
|
|
||||||
grpcserver.NewModelBaseServiceServer,
|
|
||||||
grpcserver.NewNodeServer,
|
|
||||||
grpcserver.NewTaskServer,
|
|
||||||
grpcserver.NewMessageServer,
|
|
||||||
config.NewConfigPathService,
|
|
||||||
user.GetUserService,
|
|
||||||
schedule.GetScheduleService,
|
|
||||||
admin.GetSpiderAdminService,
|
|
||||||
stats.GetStatsService,
|
|
||||||
nodeconfig.GetNodeConfigService,
|
|
||||||
taskstats.GetTaskStatsService,
|
|
||||||
color.NewService,
|
|
||||||
scheduler.GetTaskSchedulerService,
|
|
||||||
handler.GetTaskHandlerService,
|
|
||||||
}
|
|
||||||
|
|
||||||
func injectModules() {
|
|
||||||
c := container.GetContainer()
|
|
||||||
for _, injector := range injectors {
|
|
||||||
if err := c.Provide(injector); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,238 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/apex/log"
|
|
||||||
"github.com/crawlab-team/crawlab/core/constants"
|
|
||||||
"github.com/crawlab-team/crawlab/core/container"
|
|
||||||
"github.com/crawlab-team/crawlab/core/entity"
|
|
||||||
"github.com/crawlab-team/crawlab/core/errors"
|
|
||||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
|
||||||
"github.com/crawlab-team/crawlab/core/models/delegate"
|
|
||||||
"github.com/crawlab-team/crawlab/core/models/models"
|
|
||||||
"github.com/crawlab-team/crawlab/core/models/service"
|
|
||||||
"github.com/crawlab-team/crawlab/core/notification"
|
|
||||||
"github.com/crawlab-team/crawlab/core/utils"
|
|
||||||
"github.com/crawlab-team/crawlab/db/mongo"
|
|
||||||
grpc "github.com/crawlab-team/crawlab/grpc"
|
|
||||||
"github.com/crawlab-team/crawlab/trace"
|
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
|
||||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
||||||
mongo2 "go.mongodb.org/mongo-driver/mongo"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TaskServer struct {
|
|
||||||
grpc.UnimplementedTaskServiceServer
|
|
||||||
|
|
||||||
// dependencies
|
|
||||||
modelSvc service.ModelService
|
|
||||||
cfgSvc interfaces.NodeConfigService
|
|
||||||
statsSvc interfaces.TaskStatsService
|
|
||||||
|
|
||||||
// internals
|
|
||||||
server interfaces.GrpcServer
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe to task stream when a task runner in a node starts
|
|
||||||
func (svr TaskServer) Subscribe(stream grpc.TaskService_SubscribeServer) (err error) {
|
|
||||||
for {
|
|
||||||
msg, err := stream.Recv()
|
|
||||||
utils.LogDebug(msg.String())
|
|
||||||
if err == io.EOF {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
if strings.HasSuffix(err.Error(), "context canceled") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
trace.PrintError(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
switch msg.Code {
|
|
||||||
case grpc.StreamMessageCode_INSERT_DATA:
|
|
||||||
err = svr.handleInsertData(msg)
|
|
||||||
case grpc.StreamMessageCode_INSERT_LOGS:
|
|
||||||
err = svr.handleInsertLogs(msg)
|
|
||||||
default:
|
|
||||||
err = errors.ErrorGrpcInvalidCode
|
|
||||||
log.Errorf("invalid stream message code: %d", msg.Code)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("grpc error[%d]: %v", msg.Code, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch tasks to be executed by a task handler
|
|
||||||
func (svr TaskServer) Fetch(ctx context.Context, request *grpc.Request) (response *grpc.Response, err error) {
|
|
||||||
nodeKey := request.GetNodeKey()
|
|
||||||
if nodeKey == "" {
|
|
||||||
return nil, trace.TraceError(errors.ErrorGrpcInvalidNodeKey)
|
|
||||||
}
|
|
||||||
n, err := svr.modelSvc.GetNodeByKey(nodeKey, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, trace.TraceError(err)
|
|
||||||
}
|
|
||||||
var tid primitive.ObjectID
|
|
||||||
opts := &mongo.FindOptions{
|
|
||||||
Sort: bson.D{
|
|
||||||
{"p", 1},
|
|
||||||
{"_id", 1},
|
|
||||||
},
|
|
||||||
Limit: 1,
|
|
||||||
}
|
|
||||||
if err := mongo.RunTransactionWithContext(ctx, func(sc mongo2.SessionContext) (err error) {
|
|
||||||
// get task queue item assigned to this node
|
|
||||||
tid, err = svr.getTaskQueueItemIdAndDequeue(bson.M{"nid": n.Id}, opts, n.Id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !tid.IsZero() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// get task queue item assigned to any node (random mode)
|
|
||||||
tid, err = svr.getTaskQueueItemIdAndDequeue(bson.M{"nid": nil}, opts, n.Id)
|
|
||||||
if !tid.IsZero() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return HandleSuccessWithData(tid)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svr TaskServer) SendNotification(ctx context.Context, request *grpc.Request) (response *grpc.Response, err error) {
|
|
||||||
svc := notification.GetService()
|
|
||||||
var t = new(models.Task)
|
|
||||||
if err := json.Unmarshal(request.Data, t); err != nil {
|
|
||||||
return nil, trace.TraceError(err)
|
|
||||||
}
|
|
||||||
t, err = svr.modelSvc.GetTaskById(t.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, trace.TraceError(err)
|
|
||||||
}
|
|
||||||
td, err := json.Marshal(t)
|
|
||||||
if err != nil {
|
|
||||||
return nil, trace.TraceError(err)
|
|
||||||
}
|
|
||||||
var e bson.M
|
|
||||||
if err := json.Unmarshal(td, &e); err != nil {
|
|
||||||
return nil, trace.TraceError(err)
|
|
||||||
}
|
|
||||||
ts, err := svr.modelSvc.GetTaskStatById(t.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, trace.TraceError(err)
|
|
||||||
}
|
|
||||||
settings, _, err := svc.GetSettingList(bson.M{
|
|
||||||
"enabled": true,
|
|
||||||
}, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, trace.TraceError(err)
|
|
||||||
}
|
|
||||||
for _, s := range settings {
|
|
||||||
switch s.TaskTrigger {
|
|
||||||
case constants.NotificationTriggerTaskFinish:
|
|
||||||
if t.Status != constants.TaskStatusPending && t.Status != constants.TaskStatusRunning {
|
|
||||||
_ = svc.Send(s, e)
|
|
||||||
}
|
|
||||||
case constants.NotificationTriggerTaskError:
|
|
||||||
if t.Status == constants.TaskStatusError || t.Status == constants.TaskStatusAbnormal {
|
|
||||||
_ = svc.Send(s, e)
|
|
||||||
}
|
|
||||||
case constants.NotificationTriggerTaskEmptyResults:
|
|
||||||
if t.Status != constants.TaskStatusPending && t.Status != constants.TaskStatusRunning {
|
|
||||||
if ts.ResultCount == 0 {
|
|
||||||
_ = svc.Send(s, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case constants.NotificationTriggerTaskNever:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svr TaskServer) handleInsertData(msg *grpc.StreamMessage) (err error) {
|
|
||||||
data, err := svr.deserialize(msg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var records []interface{}
|
|
||||||
for _, d := range data.Records {
|
|
||||||
res, ok := d[constants.TaskKey]
|
|
||||||
if ok {
|
|
||||||
switch res.(type) {
|
|
||||||
case string:
|
|
||||||
id, err := primitive.ObjectIDFromHex(res.(string))
|
|
||||||
if err == nil {
|
|
||||||
d[constants.TaskKey] = id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
records = append(records, d)
|
|
||||||
}
|
|
||||||
return svr.statsSvc.InsertData(data.TaskId, records...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svr TaskServer) handleInsertLogs(msg *grpc.StreamMessage) (err error) {
|
|
||||||
data, err := svr.deserialize(msg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return svr.statsSvc.InsertLogs(data.TaskId, data.Logs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svr TaskServer) getTaskQueueItemIdAndDequeue(query bson.M, opts *mongo.FindOptions, nid primitive.ObjectID) (tid primitive.ObjectID, err error) {
|
|
||||||
var tq models.TaskQueueItem
|
|
||||||
if err := mongo.GetMongoCol(interfaces.ModelColNameTaskQueue).Find(query, opts).One(&tq); err != nil {
|
|
||||||
if err == mongo2.ErrNoDocuments {
|
|
||||||
return tid, nil
|
|
||||||
}
|
|
||||||
return tid, trace.TraceError(err)
|
|
||||||
}
|
|
||||||
t, err := svr.modelSvc.GetTaskById(tq.Id)
|
|
||||||
if err == nil {
|
|
||||||
t.NodeId = nid
|
|
||||||
_ = delegate.NewModelDelegate(t).Save()
|
|
||||||
}
|
|
||||||
_ = delegate.NewModelDelegate(&tq).Delete()
|
|
||||||
return tq.Id, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svr TaskServer) deserialize(msg *grpc.StreamMessage) (data entity.StreamMessageTaskData, err error) {
|
|
||||||
if err := json.Unmarshal(msg.Data, &data); err != nil {
|
|
||||||
return data, trace.TraceError(err)
|
|
||||||
}
|
|
||||||
if data.TaskId.IsZero() {
|
|
||||||
return data, trace.TraceError(errors.ErrorGrpcInvalidType)
|
|
||||||
}
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTaskServer() (res *TaskServer, err error) {
|
|
||||||
// task server
|
|
||||||
svr := &TaskServer{}
|
|
||||||
|
|
||||||
// dependency injection
|
|
||||||
if err := container.GetContainer().Invoke(func(
|
|
||||||
modelSvc service.ModelService,
|
|
||||||
statsSvc interfaces.TaskStatsService,
|
|
||||||
cfgSvc interfaces.NodeConfigService,
|
|
||||||
) {
|
|
||||||
svr.modelSvc = modelSvc
|
|
||||||
svr.statsSvc = statsSvc
|
|
||||||
svr.cfgSvc = cfgSvc
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return svr, nil
|
|
||||||
}
|
|
||||||
@@ -1,368 +0,0 @@
|
|||||||
package service
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/apex/log"
|
|
||||||
config2 "github.com/crawlab-team/crawlab/core/config"
|
|
||||||
"github.com/crawlab-team/crawlab/core/constants"
|
|
||||||
"github.com/crawlab-team/crawlab/core/container"
|
|
||||||
"github.com/crawlab-team/crawlab/core/errors"
|
|
||||||
"github.com/crawlab-team/crawlab/core/grpc/server"
|
|
||||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
|
||||||
"github.com/crawlab-team/crawlab/core/models/common"
|
|
||||||
"github.com/crawlab-team/crawlab/core/models/delegate"
|
|
||||||
"github.com/crawlab-team/crawlab/core/models/models"
|
|
||||||
"github.com/crawlab-team/crawlab/core/models/service"
|
|
||||||
"github.com/crawlab-team/crawlab/core/node/config"
|
|
||||||
"github.com/crawlab-team/crawlab/core/notification"
|
|
||||||
"github.com/crawlab-team/crawlab/core/system"
|
|
||||||
"github.com/crawlab-team/crawlab/core/utils"
|
|
||||||
"github.com/crawlab-team/crawlab/db/mongo"
|
|
||||||
grpc "github.com/crawlab-team/crawlab/grpc"
|
|
||||||
"github.com/crawlab-team/crawlab/trace"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
|
||||||
mongo2 "go.mongodb.org/mongo-driver/mongo"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MasterService struct {
|
|
||||||
// dependencies
|
|
||||||
modelSvc service.ModelService
|
|
||||||
cfgSvc interfaces.NodeConfigService
|
|
||||||
server interfaces.GrpcServer
|
|
||||||
schedulerSvc interfaces.TaskSchedulerService
|
|
||||||
handlerSvc interfaces.TaskHandlerService
|
|
||||||
scheduleSvc interfaces.ScheduleService
|
|
||||||
notificationSvc *notification.Service
|
|
||||||
spiderAdminSvc interfaces.SpiderAdminService
|
|
||||||
systemSvc *system.Service
|
|
||||||
|
|
||||||
// settings
|
|
||||||
cfgPath string
|
|
||||||
address interfaces.Address
|
|
||||||
monitorInterval time.Duration
|
|
||||||
stopOnError bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) Init() (err error) {
|
|
||||||
// do nothing
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) Start() {
|
|
||||||
// create indexes
|
|
||||||
common.CreateIndexes()
|
|
||||||
|
|
||||||
// start grpc server
|
|
||||||
if err := svc.server.Start(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// register to db
|
|
||||||
if err := svc.Register(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// start monitoring worker nodes
|
|
||||||
go svc.Monitor()
|
|
||||||
|
|
||||||
// start task handler
|
|
||||||
go svc.handlerSvc.Start()
|
|
||||||
|
|
||||||
// start task scheduler
|
|
||||||
go svc.schedulerSvc.Start()
|
|
||||||
|
|
||||||
// start schedule service
|
|
||||||
go svc.scheduleSvc.Start()
|
|
||||||
|
|
||||||
// start notification service
|
|
||||||
go svc.notificationSvc.Start()
|
|
||||||
|
|
||||||
// start spider admin service
|
|
||||||
go svc.spiderAdminSvc.Start()
|
|
||||||
|
|
||||||
// wait for quit signal
|
|
||||||
svc.Wait()
|
|
||||||
|
|
||||||
// stop
|
|
||||||
svc.Stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) Wait() {
|
|
||||||
utils.DefaultWait()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) Stop() {
|
|
||||||
_ = svc.server.Stop()
|
|
||||||
log.Infof("master[%s] service has stopped", svc.GetConfigService().GetNodeKey())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) Monitor() {
|
|
||||||
log.Infof("master[%s] monitoring started", svc.GetConfigService().GetNodeKey())
|
|
||||||
for {
|
|
||||||
if err := svc.monitor(); err != nil {
|
|
||||||
trace.PrintError(err)
|
|
||||||
if svc.stopOnError {
|
|
||||||
log.Errorf("master[%s] monitor error, now stopping...", svc.GetConfigService().GetNodeKey())
|
|
||||||
svc.Stop()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(svc.monitorInterval)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) GetConfigService() (cfgSvc interfaces.NodeConfigService) {
|
|
||||||
return svc.cfgSvc
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) GetConfigPath() (path string) {
|
|
||||||
return svc.cfgPath
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) SetConfigPath(path string) {
|
|
||||||
svc.cfgPath = path
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) GetAddress() (address interfaces.Address) {
|
|
||||||
return svc.address
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) SetAddress(address interfaces.Address) {
|
|
||||||
svc.address = address
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) SetMonitorInterval(duration time.Duration) {
|
|
||||||
svc.monitorInterval = duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) Register() (err error) {
|
|
||||||
nodeKey := svc.GetConfigService().GetNodeKey()
|
|
||||||
nodeName := svc.GetConfigService().GetNodeName()
|
|
||||||
node, err := svc.modelSvc.GetNodeByKey(nodeKey, nil)
|
|
||||||
if err != nil && err.Error() == mongo2.ErrNoDocuments.Error() {
|
|
||||||
// not exists
|
|
||||||
log.Infof("master[%s] does not exist in db", nodeKey)
|
|
||||||
node := &models.Node{
|
|
||||||
Key: nodeKey,
|
|
||||||
Name: nodeName,
|
|
||||||
MaxRunners: config.DefaultConfigOptions.MaxRunners,
|
|
||||||
IsMaster: true,
|
|
||||||
Status: constants.NodeStatusOnline,
|
|
||||||
Enabled: true,
|
|
||||||
Active: true,
|
|
||||||
ActiveTs: time.Now(),
|
|
||||||
}
|
|
||||||
if viper.GetInt("task.handler.maxRunners") > 0 {
|
|
||||||
node.MaxRunners = viper.GetInt("task.handler.maxRunners")
|
|
||||||
}
|
|
||||||
nodeD := delegate.NewModelNodeDelegate(node)
|
|
||||||
if err := nodeD.Add(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Infof("added master[%s] in db. id: %s", nodeKey, nodeD.GetModel().GetId().Hex())
|
|
||||||
return nil
|
|
||||||
} else if err == nil {
|
|
||||||
// exists
|
|
||||||
log.Infof("master[%s] exists in db", nodeKey)
|
|
||||||
nodeD := delegate.NewModelNodeDelegate(node)
|
|
||||||
if err := nodeD.UpdateStatusOnline(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Infof("updated master[%s] in db. id: %s", nodeKey, nodeD.GetModel().GetId().Hex())
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
// error
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) StopOnError() {
|
|
||||||
svc.stopOnError = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) GetServer() (svr interfaces.GrpcServer) {
|
|
||||||
return svc.server
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) monitor() (err error) {
|
|
||||||
// update master node status in db
|
|
||||||
if err := svc.updateMasterNodeStatus(); err != nil {
|
|
||||||
if err.Error() == mongo2.ErrNoDocuments.Error() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// all worker nodes
|
|
||||||
nodes, err := svc.getAllWorkerNodes()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// error flag
|
|
||||||
isErr := false
|
|
||||||
|
|
||||||
// iterate all nodes
|
|
||||||
for _, n := range nodes {
|
|
||||||
// subscribe
|
|
||||||
if err := svc.subscribeNode(&n); err != nil {
|
|
||||||
isErr = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// ping client
|
|
||||||
if err := svc.pingNodeClient(&n); err != nil {
|
|
||||||
isErr = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// update node available runners
|
|
||||||
if err := svc.updateNodeAvailableRunners(&n); err != nil {
|
|
||||||
isErr = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isErr {
|
|
||||||
return trace.TraceError(errors.ErrorNodeMonitorError)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) getAllWorkerNodes() (nodes []models.Node, err error) {
|
|
||||||
query := bson.M{
|
|
||||||
"key": bson.M{"$ne": svc.cfgSvc.GetNodeKey()}, // not self
|
|
||||||
"active": true, // active
|
|
||||||
}
|
|
||||||
nodes, err = svc.modelSvc.GetNodeList(query, nil)
|
|
||||||
if err != nil {
|
|
||||||
if err == mongo2.ErrNoDocuments {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nil, trace.TraceError(err)
|
|
||||||
}
|
|
||||||
return nodes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) updateMasterNodeStatus() (err error) {
|
|
||||||
nodeKey := svc.GetConfigService().GetNodeKey()
|
|
||||||
node, err := svc.modelSvc.GetNodeByKey(nodeKey, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
nodeD := delegate.NewModelNodeDelegate(node)
|
|
||||||
return nodeD.UpdateStatusOnline()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) setWorkerNodeOffline(n interfaces.Node) (err error) {
|
|
||||||
return delegate.NewModelNodeDelegate(n).UpdateStatusOffline()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) subscribeNode(n interfaces.Node) (err error) {
|
|
||||||
_, err = svc.server.GetSubscribe("node:" + n.GetKey())
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("cannot subscribe worker node[%s]: %v", n.GetKey(), err)
|
|
||||||
if err := svc.setWorkerNodeOffline(n); err != nil {
|
|
||||||
return trace.TraceError(err)
|
|
||||||
}
|
|
||||||
return trace.TraceError(err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) pingNodeClient(n interfaces.Node) (err error) {
|
|
||||||
if err := svc.server.SendStreamMessage("node:"+n.GetKey(), grpc.StreamMessageCode_PING); err != nil {
|
|
||||||
log.Errorf("cannot ping worker node client[%s]: %v", n.GetKey(), err)
|
|
||||||
if err := svc.setWorkerNodeOffline(n); err != nil {
|
|
||||||
return trace.TraceError(err)
|
|
||||||
}
|
|
||||||
return trace.TraceError(err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MasterService) updateNodeAvailableRunners(n interfaces.Node) (err error) {
|
|
||||||
query := bson.M{
|
|
||||||
"node_id": n.GetId(),
|
|
||||||
"status": constants.TaskStatusRunning,
|
|
||||||
}
|
|
||||||
runningTasksCount, err := mongo.GetMongoCol(interfaces.ModelColNameTask).Count(query)
|
|
||||||
if err != nil {
|
|
||||||
return trace.TraceError(err)
|
|
||||||
}
|
|
||||||
n.SetAvailableRunners(n.GetMaxRunners() - runningTasksCount)
|
|
||||||
return delegate.NewModelDelegate(n).Save()
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMasterService(opts ...Option) (res interfaces.NodeMasterService, err error) {
|
|
||||||
// master service
|
|
||||||
svc := &MasterService{
|
|
||||||
cfgPath: config2.GetConfigPath(),
|
|
||||||
monitorInterval: 15 * time.Second,
|
|
||||||
stopOnError: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
// apply options
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(svc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// server options
|
|
||||||
var serverOpts []server.Option
|
|
||||||
if svc.address != nil {
|
|
||||||
serverOpts = append(serverOpts, server.WithAddress(svc.address))
|
|
||||||
}
|
|
||||||
|
|
||||||
// dependency injection
|
|
||||||
if err := container.GetContainer().Invoke(func(
|
|
||||||
cfgSvc interfaces.NodeConfigService,
|
|
||||||
modelSvc service.ModelService,
|
|
||||||
server interfaces.GrpcServer,
|
|
||||||
schedulerSvc interfaces.TaskSchedulerService,
|
|
||||||
handlerSvc interfaces.TaskHandlerService,
|
|
||||||
scheduleSvc interfaces.ScheduleService,
|
|
||||||
spiderAdminSvc interfaces.SpiderAdminService,
|
|
||||||
) {
|
|
||||||
svc.cfgSvc = cfgSvc
|
|
||||||
svc.modelSvc = modelSvc
|
|
||||||
svc.server = server
|
|
||||||
svc.schedulerSvc = schedulerSvc
|
|
||||||
svc.handlerSvc = handlerSvc
|
|
||||||
svc.scheduleSvc = scheduleSvc
|
|
||||||
svc.spiderAdminSvc = spiderAdminSvc
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// notification service
|
|
||||||
svc.notificationSvc = notification.GetService()
|
|
||||||
|
|
||||||
// system service
|
|
||||||
svc.systemSvc = system.GetService()
|
|
||||||
|
|
||||||
// init
|
|
||||||
if err := svc.Init(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return svc, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProvideMasterService(path string, opts ...Option) func() (interfaces.NodeMasterService, error) {
|
|
||||||
// path
|
|
||||||
if path == "" || path == config2.GetConfigPath() {
|
|
||||||
if viper.GetString("config.path") != "" {
|
|
||||||
path = viper.GetString("config.path")
|
|
||||||
} else {
|
|
||||||
path = config2.GetConfigPath()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
opts = append(opts, WithConfigPath(path))
|
|
||||||
|
|
||||||
return func() (interfaces.NodeMasterService, error) {
|
|
||||||
return NewMasterService(opts...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package notification
|
|
||||||
|
|
||||||
import "go.mongodb.org/mongo-driver/bson/primitive"
|
|
||||||
|
|
||||||
type NotificationSetting struct {
|
|
||||||
Id primitive.ObjectID `json:"_id" bson:"_id"`
|
|
||||||
Type string `json:"type" bson:"type"`
|
|
||||||
Name string `json:"name" bson:"name"`
|
|
||||||
Description string `json:"description" bson:"description"`
|
|
||||||
Enabled bool `json:"enabled" bson:"enabled"`
|
|
||||||
Global bool `json:"global" bson:"global"`
|
|
||||||
Title string `json:"title,omitempty" bson:"title,omitempty"`
|
|
||||||
Template string `json:"template,omitempty" bson:"template,omitempty"`
|
|
||||||
TaskTrigger string `json:"task_trigger" bson:"task_trigger"`
|
|
||||||
Mail NotificationSettingMail `json:"mail,omitempty" bson:"mail,omitempty"`
|
|
||||||
Mobile NotificationSettingMobile `json:"mobile,omitempty" bson:"mobile,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type NotificationSettingMail struct {
|
|
||||||
Server string `json:"server" bson:"server"`
|
|
||||||
Port string `json:"port,omitempty" bson:"port,omitempty"`
|
|
||||||
User string `json:"user,omitempty" bson:"user,omitempty"`
|
|
||||||
Password string `json:"password,omitempty" bson:"password,omitempty"`
|
|
||||||
SenderEmail string `json:"sender_email,omitempty" bson:"sender_email,omitempty"`
|
|
||||||
SenderIdentity string `json:"sender_identity,omitempty" bson:"sender_identity,omitempty"`
|
|
||||||
To string `json:"to,omitempty" bson:"to,omitempty"`
|
|
||||||
Cc string `json:"cc,omitempty" bson:"cc,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type NotificationSettingMobile struct {
|
|
||||||
Webhook string `json:"webhook" bson:"webhook"`
|
|
||||||
}
|
|
||||||
@@ -1,395 +0,0 @@
|
|||||||
package notification
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/apex/log"
|
|
||||||
"github.com/crawlab-team/crawlab/core/constants"
|
|
||||||
"github.com/crawlab-team/crawlab/core/entity"
|
|
||||||
"github.com/crawlab-team/crawlab/core/models/models/v2"
|
|
||||||
"github.com/crawlab-team/crawlab/core/models/service"
|
|
||||||
"github.com/crawlab-team/crawlab/core/utils"
|
|
||||||
mongo2 "github.com/crawlab-team/crawlab/db/mongo"
|
|
||||||
parser "github.com/crawlab-team/crawlab/template-parser"
|
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
|
||||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Service struct {
|
|
||||||
col *mongo2.Col // notification settings
|
|
||||||
modelSvc service.ModelService
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *Service) Init() (err error) {
|
|
||||||
if !utils.IsPro() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *Service) Start() (err error) {
|
|
||||||
// initialize data
|
|
||||||
if err := svc.initData(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *Service) Stop() (err error) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *Service) initData() (err error) {
|
|
||||||
total, err := svc.col.Count(nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if total > 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// data to initialize
|
|
||||||
settings := []NotificationSetting{
|
|
||||||
{
|
|
||||||
Id: primitive.NewObjectID(),
|
|
||||||
Type: TypeMail,
|
|
||||||
Enabled: true,
|
|
||||||
Name: "任务通知(邮件)",
|
|
||||||
Description: "这是默认的邮件通知。您可以使用您自己的设置进行编辑。",
|
|
||||||
TaskTrigger: constants.NotificationTriggerTaskFinish,
|
|
||||||
Title: "[Crawlab] 爬虫任务更新: {{$.status}}",
|
|
||||||
Template: `尊敬的 {{$.user.username}},
|
|
||||||
|
|
||||||
请查看下面的任务数据。
|
|
||||||
|
|
||||||
|键|值|
|
|
||||||
|:-:|:--|
|
|
||||||
|任务状态|{{$.status}}|
|
|
||||||
|任务优先级|{{$.priority}}|
|
|
||||||
|任务模式|{{$.mode}}|
|
|
||||||
|执行命令|{{$.cmd}}|
|
|
||||||
|执行参数|{{$.param}}|
|
|
||||||
|错误信息|{{$.error}}|
|
|
||||||
|节点|{{$.node.name}}|
|
|
||||||
|爬虫|{{$.spider.name}}|
|
|
||||||
|项目|{{$.spider.project.name}}|
|
|
||||||
|定时任务|{{$.schedule.name}}|
|
|
||||||
|结果数|{{$.:task_stat.result_count}}|
|
|
||||||
|等待时间(秒)|{#{{$.:task_stat.wait_duration}}/1000#}|
|
|
||||||
|运行时间(秒)|{#{{$.:task_stat.runtime_duration}}/1000#}|
|
|
||||||
|总时间(秒)|{#{{$.:task_stat.total_duration}}/1000#}|
|
|
||||||
|平均结果数/秒|{#{{$.:task_stat.result_count}}/({{$.:task_stat.total_duration}}/1000)#}|
|
|
||||||
`,
|
|
||||||
Mail: NotificationSettingMail{
|
|
||||||
Server: "smtp.163.com",
|
|
||||||
Port: "465",
|
|
||||||
To: "{{$.user[create].email}}",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Id: primitive.NewObjectID(),
|
|
||||||
Type: TypeMail,
|
|
||||||
Enabled: true,
|
|
||||||
Name: "Task Change (Mail)",
|
|
||||||
Description: "This is the default mail notification. You can edit it with your own settings",
|
|
||||||
TaskTrigger: constants.NotificationTriggerTaskFinish,
|
|
||||||
Title: "[Crawlab] Task Update: {{$.status}}",
|
|
||||||
Template: `Dear {{$.user.username}},
|
|
||||||
|
|
||||||
Please find the task data as below.
|
|
||||||
|
|
||||||
|Key|Value|
|
|
||||||
|:-:|:--|
|
|
||||||
|Task Status|{{$.status}}|
|
|
||||||
|Task Priority|{{$.priority}}|
|
|
||||||
|Task Mode|{{$.mode}}|
|
|
||||||
|Task Command|{{$.cmd}}|
|
|
||||||
|Task Params|{{$.param}}|
|
|
||||||
|Error Message|{{$.error}}|
|
|
||||||
|Node|{{$.node.name}}|
|
|
||||||
|Spider|{{$.spider.name}}|
|
|
||||||
|Project|{{$.spider.project.name}}|
|
|
||||||
|Schedule|{{$.schedule.name}}|
|
|
||||||
|Result Count|{{$.:task_stat.result_count}}|
|
|
||||||
|Wait Duration (sec)|{#{{$.:task_stat.wait_duration}}/1000#}|
|
|
||||||
|Runtime Duration (sec)|{#{{$.:task_stat.runtime_duration}}/1000#}|
|
|
||||||
|Total Duration (sec)|{#{{$.:task_stat.total_duration}}/1000#}|
|
|
||||||
|Avg Results / Sec|{#{{$.:task_stat.result_count}}/({{$.:task_stat.total_duration}}/1000)#}|
|
|
||||||
`,
|
|
||||||
Mail: NotificationSettingMail{
|
|
||||||
Server: "smtp.163.com",
|
|
||||||
Port: "465",
|
|
||||||
To: "{{$.user[create].email}}",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Id: primitive.NewObjectID(),
|
|
||||||
Type: TypeMobile,
|
|
||||||
Enabled: true,
|
|
||||||
Name: "任务通知(移动端)",
|
|
||||||
Description: "这是默认的手机通知。您可以使用您自己的设置进行编辑。",
|
|
||||||
TaskTrigger: constants.NotificationTriggerTaskFinish,
|
|
||||||
Title: "[Crawlab] 任务更新: {{$.status}}",
|
|
||||||
Template: `尊敬的 {{$.user.username}},
|
|
||||||
|
|
||||||
请查看下面的任务数据。
|
|
||||||
|
|
||||||
- **任务状态**: {{$.status}}
|
|
||||||
- **任务优先级**: {{$.priority}}
|
|
||||||
- **任务模式**: {{$.mode}}
|
|
||||||
- **执行命令**: {{$.cmd}}
|
|
||||||
- **执行参数**: {{$.param}}
|
|
||||||
- **错误信息**: {{$.error}}
|
|
||||||
- **节点**: {{$.node.name}}
|
|
||||||
- **爬虫**: {{$.spider.name}}
|
|
||||||
- **项目**: {{$.spider.project.name}}
|
|
||||||
- **定时任务**: {{$.schedule.name}}
|
|
||||||
- **结果数**: {{$.:task_stat.result_count}}
|
|
||||||
- **等待时间(秒)**: {#{{$.:task_stat.wait_duration}}/1000#}
|
|
||||||
- **运行时间(秒)**: {#{{$.:task_stat.runtime_duration}}/1000#}
|
|
||||||
- **总时间(秒)**: {#{{$.:task_stat.total_duration}}/1000#}
|
|
||||||
- **平均结果数/秒**: {#{{$.:task_stat.result_count}}/({{$.:task_stat.total_duration}}/1000)#}`,
|
|
||||||
Mobile: NotificationSettingMobile{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Id: primitive.NewObjectID(),
|
|
||||||
Type: TypeMobile,
|
|
||||||
Enabled: true,
|
|
||||||
Name: "Task Change (Mobile)",
|
|
||||||
Description: "This is the default mobile notification. You can edit it with your own settings",
|
|
||||||
TaskTrigger: constants.NotificationTriggerTaskError,
|
|
||||||
Title: "[Crawlab] Task Update: {{$.status}}",
|
|
||||||
Template: `Dear {{$.user.username}},
|
|
||||||
|
|
||||||
Please find the task data as below.
|
|
||||||
|
|
||||||
- **Task Status**: {{$.status}}
|
|
||||||
- **Task Priority**: {{$.priority}}
|
|
||||||
- **Task Mode**: {{$.mode}}
|
|
||||||
- **Task Command**: {{$.cmd}}
|
|
||||||
- **Task Params**: {{$.param}}
|
|
||||||
- **Error Message**: {{$.error}}
|
|
||||||
- **Node**: {{$.node.name}}
|
|
||||||
- **Spider**: {{$.spider.name}}
|
|
||||||
- **Project**: {{$.spider.project.name}}
|
|
||||||
- **Schedule**: {{$.schedule.name}}
|
|
||||||
- **Result Count**: {{$.:task_stat.result_count}}
|
|
||||||
- **Wait Duration (sec)**: {#{{$.:task_stat.wait_duration}}/1000#}
|
|
||||||
- **Runtime Duration (sec)**: {#{{$.:task_stat.runtime_duration}}/1000#}
|
|
||||||
- **Total Duration (sec)**: {#{{$.:task_stat.total_duration}}/1000#}
|
|
||||||
- **Avg Results / Sec**: {#{{$.:task_stat.result_count}}/({{$.:task_stat.total_duration}}/1000)#}`,
|
|
||||||
Mobile: NotificationSettingMobile{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
var data []interface{}
|
|
||||||
for _, s := range settings {
|
|
||||||
data = append(data, s)
|
|
||||||
}
|
|
||||||
_, err = svc.col.InsertMany(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *Service) Send(s NotificationSetting, entity bson.M) (err error) {
|
|
||||||
switch s.Type {
|
|
||||||
case TypeMail:
|
|
||||||
return svc.SendMail(s, entity)
|
|
||||||
case TypeMobile:
|
|
||||||
return svc.SendMobile(s, entity)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *Service) SendMail(s NotificationSetting, entity bson.M) (err error) {
|
|
||||||
// to
|
|
||||||
to, err := parser.Parse(s.Mail.To, entity)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("parsing 'to' error: %v", err)
|
|
||||||
}
|
|
||||||
if to == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// cc
|
|
||||||
cc, err := parser.Parse(s.Mail.Cc, entity)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("parsing 'cc' error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// title
|
|
||||||
title, err := parser.Parse(s.Title, entity)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("parsing 'title' error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// content
|
|
||||||
content, err := parser.Parse(s.Template, entity)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("parsing 'content' error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// send mail
|
|
||||||
if err := SendMail(&models.NotificationSettingMail{
|
|
||||||
Server: s.Mail.Server,
|
|
||||||
Port: s.Mail.Port,
|
|
||||||
User: s.Mail.User,
|
|
||||||
Password: s.Mail.Password,
|
|
||||||
SenderEmail: s.Mail.SenderEmail,
|
|
||||||
SenderIdentity: s.Mail.SenderIdentity,
|
|
||||||
To: s.Mail.To,
|
|
||||||
Cc: s.Mail.Cc,
|
|
||||||
}, to, cc, title, content); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *Service) SendMobile(s NotificationSetting, entity bson.M) (err error) {
|
|
||||||
// webhook
|
|
||||||
webhook, err := parser.Parse(s.Mobile.Webhook, entity)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("parsing 'webhook' error: %v", err)
|
|
||||||
}
|
|
||||||
if webhook == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// title
|
|
||||||
title, err := parser.Parse(s.Title, entity)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("parsing 'title' error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// content
|
|
||||||
content, err := parser.Parse(s.Template, entity)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("parsing 'content' error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// send
|
|
||||||
if err := SendMobileNotification(webhook, title, content); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *Service) GetSettingList(query bson.M, pagination *entity.Pagination, sort bson.D) (res []NotificationSetting, total int, err error) {
|
|
||||||
// options
|
|
||||||
var options *mongo2.FindOptions
|
|
||||||
if pagination != nil || sort != nil {
|
|
||||||
options = new(mongo2.FindOptions)
|
|
||||||
if pagination != nil {
|
|
||||||
options.Skip = pagination.Size * (pagination.Page - 1)
|
|
||||||
options.Limit = pagination.Size
|
|
||||||
}
|
|
||||||
if sort != nil {
|
|
||||||
options.Sort = sort
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get list
|
|
||||||
var list []NotificationSetting
|
|
||||||
if err := svc.col.Find(query, options).All(&list); err != nil {
|
|
||||||
if err.Error() == mongo.ErrNoDocuments.Error() {
|
|
||||||
return nil, 0, nil
|
|
||||||
} else {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// total count
|
|
||||||
total, err = svc.col.Count(query)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return list, total, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *Service) GetSetting(id primitive.ObjectID) (res *NotificationSetting, err error) {
|
|
||||||
var s NotificationSetting
|
|
||||||
if err := svc.col.FindId(id).One(&s); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *Service) PosSetting(s *NotificationSetting) (err error) {
|
|
||||||
s.Id = primitive.NewObjectID()
|
|
||||||
if _, err := svc.col.Insert(s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *Service) PutSetting(id primitive.ObjectID, s NotificationSetting) (err error) {
|
|
||||||
if err := svc.col.ReplaceId(id, s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *Service) DeleteSetting(id primitive.ObjectID) (err error) {
|
|
||||||
if err := svc.col.DeleteId(id); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *Service) EnableSetting(id primitive.ObjectID) (err error) {
|
|
||||||
return svc._toggleSettingFunc(true)(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *Service) DisableSetting(id primitive.ObjectID) (err error) {
|
|
||||||
return svc._toggleSettingFunc(false)(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *Service) _toggleSettingFunc(value bool) func(id primitive.ObjectID) error {
|
|
||||||
return func(id primitive.ObjectID) (err error) {
|
|
||||||
var s NotificationSetting
|
|
||||||
if err := svc.col.FindId(id).One(&s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s.Enabled = value
|
|
||||||
if err := svc.col.ReplaceId(id, s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewService() *Service {
|
|
||||||
// service
|
|
||||||
svc := &Service{
|
|
||||||
col: mongo2.GetMongoCol(SettingsColName),
|
|
||||||
}
|
|
||||||
|
|
||||||
// model service
|
|
||||||
modelSvc, err := service.GetService()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
svc.modelSvc = modelSvc
|
|
||||||
|
|
||||||
if err := svc.Init(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return svc
|
|
||||||
}
|
|
||||||
|
|
||||||
var _service *Service
|
|
||||||
|
|
||||||
func GetService() *Service {
|
|
||||||
if _service == nil {
|
|
||||||
_service = NewService()
|
|
||||||
}
|
|
||||||
return _service
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package notification
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestService_sendMobile(t *testing.T) {
|
|
||||||
T.Setup(t)
|
|
||||||
e := T.NewExpect(t)
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
|
|
||||||
data := map[string]interface{}{
|
|
||||||
"task_id": T.TestTask.GetId().Hex(),
|
|
||||||
}
|
|
||||||
e.POST("/send/mobile").WithJSON(data).
|
|
||||||
Expect().Status(http.StatusOK)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user