gokins/mgr/trigger_hook.go
LinskRuis.32 31523beac5 bugs+
2020-10-24 11:55:07 +08:00

210 lines
4.3 KiB
Go

package mgr
import (
"context"
"crypto/hmac"
"crypto/sha1"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"gokins/comm"
"gokins/model"
"gokins/service/dbService"
"net/http"
"net/url"
"strings"
"time"
"github.com/dop251/goja"
ruisUtil "github.com/mgr9525/go-ruisutil"
)
type confHookBean struct {
Plug string `json:"plug"`
Conf string `json:"conf"`
}
type trigHookTask struct {
md *model.TModel
tg *model.TTrigger
conf *confHookBean
ctx context.Context
cncl context.CancelFunc
js string
vm *goja.Runtime
querys string
header http.Header
bodys []byte
body *ruisUtil.Map
confs *ruisUtil.Map
}
func (c *trigHookTask) stop() {
if c.cncl != nil {
c.cncl()
c.cncl = nil
}
}
func (c *trigHookTask) isRun() bool {
return c.cncl != nil
}
func (c *trigHookTask) start(pars ...interface{}) error {
if c.tg == nil || c.cncl != nil {
return errors.New("already run")
}
if len(pars) < 3 {
return errors.New("param err")
}
c.querys = pars[0].(string)
c.header = pars[1].(http.Header)
c.bodys = pars[2].([]byte)
c.conf = &confHookBean{}
err := json.Unmarshal([]byte(c.tg.Config), c.conf)
if err != nil {
return err
}
c.confs = ruisUtil.NewMapo(c.conf.Conf)
c.md = dbService.GetModel(c.tg.Mid)
if c.md == nil {
return errors.New("not found model")
}
js, ok := HookjsMap[c.conf.Plug]
if !ok {
return errors.New("not found plugin:" + c.conf.Plug)
}
c.js = js.js
c.ctx, c.cncl = context.WithCancel(mgrCtx)
go func() {
defer ruisUtil.Recovers("gorun", nil)
c.vm = goja.New()
c.tg.Errs = ""
err := c.run()
if err != nil {
c.tg.Errs = err.Error()
}
comm.Db.Cols("errs").Where("id=?", c.tg.Id).Update(c.tg)
c.stop()
}()
go func() {
for {
select {
case <-c.ctx.Done():
if c.vm != nil {
c.vm.ClearInterrupt()
}
goto end
default:
time.Sleep(time.Millisecond)
}
}
end:
println("ctx end")
}()
return nil
}
func (c *trigHookTask) run() (rterr error) {
defer ruisUtil.Recovers("RunTask start", func(errs string) {
println("trigHookTask run err:" + errs)
rterr = errors.New(errs)
})
c.initVm()
rslt, err := c.vm.RunString(c.js)
if err != nil {
println("vm.RunString err:" + err.Error())
return err
}
println(fmt.Sprintf("js result:%+v", rslt.Export()))
mainFun, ok := goja.AssertFunction(c.vm.Get("main"))
if !ok {
println("not found main err")
return errors.New("not found main err")
}
ret, err := mainFun(goja.Null())
if err != nil {
println("vm mainFun err:" + err.Error())
return err
}
rets := ruisUtil.NewMapo(ret.Export())
fmt.Printf("rets:%+v\n", rets)
if rets.GetBool("check") {
rn := &model.TModelRun{}
rn.Tid = c.md.Id
rn.Uid = c.tg.Uid
rn.Times = time.Now()
rn.Tgid = c.tg.Id
rn.Tgtyps = c.conf.Plug + "触发"
comm.Db.Insert(rn)
ExecMgr.Refresh()
println("trigHookTask model run id:", rn.Id)
} else {
errs := rets.GetString("errs")
if errs != "" {
return errors.New(errs)
}
}
return nil
}
func (c *trigHookTask) initVm() {
csl := c.vm.NewObject()
c.vm.Set("console", csl)
csl.Set("log", func(args ...interface{}) {
fmt.Println(args)
})
c.vm.Set("getHeader", func(key string) string {
return c.header.Get(key)
})
c.vm.Set("getBodys", func(key string) string {
return string(c.bodys)
})
c.vm.Set("getBody", func() interface{} {
if c.body == nil {
if c.conf.Plug == "github" {
unescape, _ := url.QueryUnescape(string(c.bodys)[8:])
c.bodys = []byte(unescape)
}
c.body = ruisUtil.NewMapo(c.bodys)
}
return c.body
})
c.vm.Set("getConf", func() interface{} {
return c.confs
})
c.vm.Set("encodeSha1", func(body string) string {
return ruisUtil.Sha1String(body)
})
c.vm.Set("verifySignature", func(secret string, signature string, body []byte) bool {
return verifySignature([]byte(secret), signature, body)
})
}
func signBody(secret, body []byte) []byte {
computed := hmac.New(sha1.New, secret)
computed.Write(body)
return []byte(computed.Sum(nil))
}
func verifySignature(secret []byte, signature string, body []byte) bool {
const signaturePrefix = "sha1="
const signatureLength = 45 // len(SignaturePrefix) + len(hex(sha1))
if len(signature) != signatureLength || !strings.HasPrefix(signature, signaturePrefix) {
return false
}
actual := make([]byte, 20)
hex.Decode(actual, []byte(signature[5:]))
return hmac.Equal(signBody(secret, body), actual)
}