diff --git a/backend/model/spider.go b/backend/model/spider.go
index 666ed7d1..8b44481e 100644
--- a/backend/model/spider.go
+++ b/backend/model/spider.go
@@ -64,6 +64,10 @@ type Spider struct {
DedupField string `json:"dedup_field" bson:"dedup_field"` // 去重字段
DedupMethod string `json:"dedup_method" bson:"dedup_method"` // 去重方式
+ // Web Hook
+ IsWebHook bool `json:"is_web_hook" bson:"is_web_hook"` // 是否开启 Web Hook
+ WebHookUrl string `json:"web_hook_url" bson:"web_hook_url"` // Web Hook URL
+
// 前端展示
LastRunTs time.Time `json:"last_run_ts"` // 最后一次执行时间
LastStatus string `json:"last_status"` // 最后执行状态
diff --git a/backend/services/task.go b/backend/services/task.go
index df8b4071..db81ed4c 100644
--- a/backend/services/task.go
+++ b/backend/services/task.go
@@ -15,8 +15,10 @@ import (
"fmt"
"github.com/apex/log"
"github.com/globalsign/mgo/bson"
+ "github.com/imroc/req"
uuid "github.com/satori/go.uuid"
"github.com/spf13/viper"
+ "net/http"
"os"
"os/exec"
"path"
@@ -555,12 +557,22 @@ func ExecuteTask(id int) {
cmd += " " + t.Param
}
+ // 获得触发任务用户
+ user, err := model.GetUser(t.UserId)
+ if err != nil {
+ log.Errorf(GetWorkerPrefix(id) + err.Error())
+ return
+ }
+
// 任务赋值
t.NodeId = node.Id // 任务节点信息
t.StartTs = time.Now() // 任务开始时间
t.Status = constants.StatusRunning // 任务状态
t.WaitDuration = t.StartTs.Sub(t.CreateTs).Seconds() // 等待时长
+ // 发送 Web Hook 请求 (任务开始)
+ go SendWebHookRequest(user, t, spider)
+
// 文件检查
if err := SpiderFileCheck(t, spider); err != nil {
log.Errorf("spider file check error: %s", err.Error())
@@ -595,13 +607,6 @@ func ExecuteTask(id int) {
cronExecErrLog.Start()
defer cronExecErrLog.Stop()
- // 获得触发任务用户
- user, err := model.GetUser(t.UserId)
- if err != nil {
- log.Errorf(GetWorkerPrefix(id) + err.Error())
- return
- }
-
// 执行Shell命令
if err := ExecuteShellCmd(cmd, cwd, t, spider, user); err != nil {
log.Errorf(GetWorkerPrefix(id) + err.Error())
@@ -611,6 +616,10 @@ func ExecuteTask(id int) {
if user.Setting.NotificationTrigger == constants.NotificationTriggerOnTaskEnd || user.Setting.NotificationTrigger == constants.NotificationTriggerOnTaskError {
SendNotifications(user, t, spider)
}
+
+ // 发送 Web Hook 请求 (任务开始)
+ go SendWebHookRequest(user, t, spider)
+
return
}
@@ -630,6 +639,9 @@ func ExecuteTask(id int) {
t.RuntimeDuration = t.FinishTs.Sub(t.StartTs).Seconds() // 运行时长
t.TotalDuration = t.FinishTs.Sub(t.CreateTs).Seconds() // 总时长
+ // 发送 Web Hook 请求 (任务结束)
+ go SendWebHookRequest(user, t, spider)
+
// 如果是任务结束时发送通知,则发送通知
if user.Setting.NotificationTrigger == constants.NotificationTriggerOnTaskEnd {
SendNotifications(user, t, spider)
@@ -664,10 +676,6 @@ func FinishUpTask(s model.Spider, t model.Task) {
}()
}
-func MonitorTask(s model.Spider, t model.Task) {
-
-}
-
func SpiderFileCheck(t model.Task, spider model.Spider) error {
// 判断爬虫文件是否存在
gfFile := model.GetGridFs(spider.FileId)
@@ -997,6 +1005,44 @@ func SendNotifications(u model.User, t model.Task, s model.Spider) {
}
}
+func SendWebHookRequest(u model.User, t model.Task, s model.Spider) {
+ type RequestBody struct {
+ Status string `json:"status"`
+ Task model.Task `json:"task"`
+ Spider model.Spider `json:"spider"`
+ UserName string `json:"user_name"`
+ }
+
+ if s.IsWebHook && s.WebHookUrl != "" {
+ // request header
+ header := req.Header{
+ "Content-Type": "application/json; charset=utf-8",
+ }
+
+ // request body
+ reqBody := RequestBody{
+ Status: t.Status,
+ UserName: u.Username,
+ Task: t,
+ Spider: s,
+ }
+
+ // make POST http request
+ res, err := req.Post(s.WebHookUrl, header, req.BodyJSON(reqBody))
+ if err != nil {
+ log.Errorf("sent web hook request with error: " + err.Error())
+ debug.PrintStack()
+ return
+ }
+ if res.Response().StatusCode != http.StatusOK {
+ log.Errorf(fmt.Sprintf("sent web hook request with error http code: %d, task_id: %s, status: %s", res.Response().StatusCode, t.Id, t.Status))
+ debug.PrintStack()
+ return
+ }
+ log.Infof(fmt.Sprintf("sent web hook request, task_id: %s, status: %s)", t.Id, t.Status))
+ }
+}
+
func InitTaskExecutor() error {
// 构造任务执行器
c := cron.New(cron.WithSeconds())
diff --git a/frontend/src/components/InfoView/SpiderInfoView.vue b/frontend/src/components/InfoView/SpiderInfoView.vue
index e9139adc..d66d7632 100644
--- a/frontend/src/components/InfoView/SpiderInfoView.vue
+++ b/frontend/src/components/InfoView/SpiderInfoView.vue
@@ -96,7 +96,7 @@
-
+
+
+
+
+
+
diff --git a/frontend/src/i18n/zh.js b/frontend/src/i18n/zh.js
index 06f07239..81f12171 100644
--- a/frontend/src/i18n/zh.js
+++ b/frontend/src/i18n/zh.js
@@ -232,6 +232,7 @@ export default {
'Please enter de-duplicated field': '请输入去重字段',
'Overwrite': '覆盖',
'Ignore': '忽略',
+ 'De-Duplication': '去重',
// 爬虫列表
'Name': '名称',
@@ -654,6 +655,7 @@ docker run -d --restart always --name crawlab_worker \\
'Deleted API token successfully': '成功删除 API Token',
'Are you sure to add an API token?': '确认创建 API Token?',
'Are you sure to delete this API token?': '确认删除该 API Token?',
+ 'Please enter Web Hook URL': '请输入 Web Hook URL',
// 其他
'Star crawlab-team/crawlab on GitHub': '在 GitHub 上为 Crawlab 加星吧'
diff --git a/frontend/src/views/layout/components/Sidebar/index.vue b/frontend/src/views/layout/components/Sidebar/index.vue
index 1b16c86f..a7461196 100644
--- a/frontend/src/views/layout/components/Sidebar/index.vue
+++ b/frontend/src/views/layout/components/Sidebar/index.vue
@@ -93,6 +93,6 @@ export default {
.sidebar-container .sidebar-logo .version {
margin-left: 5px;
font-weight: normal;
- font-size: 12px;
+ font-size: 11px;
}