Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

22
app/admin/ep/tapd/BUILD Normal file
View File

@@ -0,0 +1,22 @@
package(default_visibility = ["//visibility:public"])
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/admin/ep/tapd/cmd:all-srcs",
"//app/admin/ep/tapd/conf:all-srcs",
"//app/admin/ep/tapd/dao:all-srcs",
"//app/admin/ep/tapd/http:all-srcs",
"//app/admin/ep/tapd/model:all-srcs",
"//app/admin/ep/tapd/service:all-srcs",
],
tags = ["automanaged"],
)

View File

@@ -0,0 +1,17 @@
##### ep-tapd
##### Version 1. 0. 4
1 增加外部测试接口
##### Version 1. 0. 3
1 回调改为postform
##### Version 1. 0. 2
1 修改url
##### Version 1. 0. 1
1 回调接分发包一层data
2 支持按workspaceid选项分发
##### Version 1. 0. 0
1 回调接口分发

View File

@@ -0,0 +1,14 @@
# Owner
maojian
yuanmin
fengyifeng
xuneng
# Author
yuanmin
fengyifeng
xuneng
# Reviewer
zhapuyu
wangxu01

19
app/admin/ep/tapd/OWNERS Normal file
View File

@@ -0,0 +1,19 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- fengyifeng
- maojian
- xuneng
- yuanmin
labels:
- admin
- admin/ep/tapd
- ep
options:
no_parent_owners: true
reviewers:
- fengyifeng
- wangxu01
- xuneng
- yuanmin
- zhapuyu

View File

@@ -0,0 +1,17 @@
# tapd
# 项目简介
### 背景/Background
### 概览Overview
# 编译环境
# 依赖包
# 编译执行

View File

@@ -0,0 +1,43 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "cmd",
embed = [":go_default_library"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
data = ["admin.toml"],
importpath = "go-common/app/admin/ep/tapd/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/ep/tapd/conf:go_default_library",
"//app/admin/ep/tapd/http:go_default_library",
"//app/admin/ep/tapd/service:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,100 @@
[bm]
addr = "0.0.0.0:9001"
maxListen = 10000
timeout = "1000s"
[httpClient]
key = "c05dd4e1638a8af0"
secret = "7daa7f8c06cd33c5c3067063c746fdcb"
dial = "2s"
timeout = "10s"
keepAlive = "60s"
timer = 1000
[httpClient.breaker]
window = "10s"
sleep = "2000ms"
bucket = 10
ratio = 0.5
request = 100
[mail]
host = "smtp.exmail.qq.com"
port = 465
username = "merlin@bilibili.com"
password = ""
noticeOwner = ["fengyifeng@bilibili.com"]
[orm]
dsn = "root:123456@tcp(172.18.33.130:3306)/tapd?timeout=200ms&readTimeout=2000ms&writeTimeout=2000ms&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 5
idleTimeout = "4h"
[memcache]
name = "merlin"
proto = "tcp"
addr = "172.18.33.61:11232"
idle = 5
active = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "12h"
[Tapd]
callbackToken = "hRp[Lfnfrp9Jypr7aaJMGn8NC.[E+Gvb9&nRazs6Mm{fEW98.z9yzV*phu)U97#"
useCache = true
[Scheduler]
updateHookURLCacheTask = "0 */10 * * * ?"
active = false
[auth]
managerHost = "http://uat-manager.bilibili.co"
dashboardHost = "http://dashboard-mng.bilibili.co"
dashboardCaller = "merlin"
[auth.DsHTTPClient]
key = "merlin"
secret = "4fb521f66dfd5efcf6e77d078ed2eb0a"
dial = "1s"
timeout = "1s"
keepAlive = "60s"
[auth.DsHTTPClient.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[auth.MaHTTPClient]
key = "f6433799dbd88751"
secret = "36f8ddb1806207fe07013ab6a77a3935"
dial = "1ms"
timeout = "1ms"
keepAlive = "60s"
[auth.MaHTTPClient.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[auth.session]
sessionIDLength = 32
cookieLifeTime = 1
cookieName = "mng-go"
domain = ".bilibili.co"
[auth.session.Memcache]
name = "go-business/auth"
proto = "tcp"
addr = "172.16.33.54:11211"
active = 10
idle = 10
dialTimeout = "1ms"
readTimeout = "1ms"
writeTimeout = "1ms"
idleTimeout = "80s"

View File

@@ -0,0 +1,54 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"time"
"go-common/app/admin/ep/tapd/conf"
"go-common/app/admin/ep/tapd/http"
"go-common/app/admin/ep/tapd/service"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
)
const (
_durationForClosingServer = 2000
)
func main() {
flag.Parse()
if err := conf.Init(); err != nil {
log.Error("conf.Init() error(%v)", err)
panic(err)
}
// init log
log.Init(conf.Conf.Log)
defer log.Close()
log.Info("tapd start")
// ecode init
ecode.Init(conf.Conf.Ecode)
// service init
s := service.New(conf.Conf)
http.Init(conf.Conf, s)
// init pprof conf.Conf.Perf
// init signal
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
si := <-c
log.Info("tapd get a signal %s", si.String())
switch si {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
log.Info("tapd exit")
s.Close()
time.Sleep(_durationForClosingServer)
return
case syscall.SIGHUP:
default:
return
}
}
}

View File

@@ -0,0 +1,39 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["conf.go"],
importpath = "go-common/app/admin/ep/tapd/conf",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/cache/memcache:go_default_library",
"//library/conf:go_default_library",
"//library/database/orm:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/permit:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/BurntSushi/toml:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,117 @@
package conf
import (
"errors"
"flag"
"go-common/library/cache/memcache"
"go-common/library/conf"
"go-common/library/database/orm"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/permit"
"go-common/library/time"
"github.com/BurntSushi/toml"
)
// global var
var (
confPath string
client *conf.Client
// Conf config
Conf = &Config{}
)
// Memcache memcache.
type Memcache struct {
*memcache.Config
Expire time.Duration
}
// Config config set
type Config struct {
// elk
Log *log.Config
// http
BM *bm.ServerConfig
// ecode
Ecode *ecode.Config
HTTPClient *bm.ClientConfig
Memcache *Memcache
// orm
ORM *orm.Config
Mail *Mail
Scheduler *Scheduler
Auth *permit.Config
Tapd *Tapd
}
// Mail mail
type Mail struct {
Host string
Port int
Username string
Password string
NoticeOwner []string
}
// Scheduler Scheduler.
type Scheduler struct {
UpdateHookURLCacheTask string
Active bool
}
// Tapd Tapd.
type Tapd struct {
CallbackToken string
UseCache bool
}
func init() {
flag.StringVar(&confPath, "conf", "", "default config path")
}
// Init init conf
func Init() error {
if confPath != "" {
return local()
}
return remote()
}
func local() (err error) {
_, err = toml.DecodeFile(confPath, &Conf)
return
}
func remote() (err error) {
if client, err = conf.New(); err != nil {
return
}
return load()
}
func load() (err error) {
var (
s string
ok bool
tmpConf *Config
)
if s, ok = client.Toml2(); !ok {
return errors.New("load config center error")
}
if _, err = toml.Decode(s, &tmpConf); err != nil {
return errors.New("could not decode config")
}
*Conf = *tmpConf
return
}

View File

@@ -0,0 +1,46 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"hook_url.go",
"mysql_event_log.go",
"mysql_hook_url.go",
"mysql_url_event.go",
],
importpath = "go-common/app/admin/ep/tapd/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/ep/tapd/conf:go_default_library",
"//app/admin/ep/tapd/model:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/database/orm:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/sync/pipeline/fanout:go_default_library",
"//vendor/github.com/jinzhu/gorm:go_default_library",
"//vendor/gopkg.in/gomail.v2:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,92 @@
package dao
import (
"bytes"
"context"
"encoding/json"
"net/http"
"time"
"go-common/app/admin/ep/tapd/conf"
"go-common/library/cache/memcache"
"go-common/library/database/orm"
"go-common/library/log"
xhttp "go-common/library/net/http/blademaster"
"go-common/library/sync/pipeline/fanout"
"github.com/jinzhu/gorm"
"gopkg.in/gomail.v2"
)
// Dao dao.
type Dao struct {
c *conf.Config
httpClient *xhttp.Client
db *gorm.DB
email *gomail.Dialer
mc *memcache.Pool
cache *fanout.Fanout
expire int32
}
// New init mysql db.
func New(c *conf.Config) *Dao {
return &Dao{
c: c,
httpClient: xhttp.NewClient(c.HTTPClient),
db: orm.NewMySQL(c.ORM),
email: gomail.NewDialer(c.Mail.Host, c.Mail.Port, c.Mail.Username, c.Mail.Password),
mc: memcache.NewPool(c.Memcache.Config),
cache: fanout.New("cache", fanout.Worker(5), fanout.Buffer(10240)),
expire: int32(time.Duration(c.Memcache.Expire) / time.Second),
}
}
// Close close the resource.
func (d *Dao) Close() {
if d.db != nil {
d.db.Close()
}
if d.mc != nil {
d.mc.Close()
}
}
// Ping verify server is ok.
func (d *Dao) Ping(c context.Context) (err error) {
if err = d.db.DB().Ping(); err != nil {
log.Info("dao.cloudDB.Ping() error(%v)", err)
}
return
}
// tokenCacheSave The err does not need to return, because this method is irrelevant.
func (d *Dao) tokenCacheSave(c context.Context, cacheItem *memcache.Item) {
var f = func(c context.Context) {
var (
conn = d.mc.Get(c)
err error
)
defer conn.Close()
if err = conn.Set(cacheItem); err != nil {
log.Error("AddCache conn.Set(%s) error(%v)", cacheItem.Key, err)
}
}
if err := d.cache.Do(c, f); err != nil {
log.Error("Token cache save err(%v)", err)
}
}
func (d *Dao) newRequest(method, url string, v interface{}) (req *http.Request, err error) {
body := &bytes.Buffer{}
if method != http.MethodGet {
if err = json.NewEncoder(body).Encode(v); err != nil {
log.Error("json encode value(%s) err(?) ", v, err)
return
}
}
if req, err = http.NewRequest(method, url, body); err != nil {
log.Error("http new request url(?) err(?)", url, err)
}
return
}

View File

@@ -0,0 +1,169 @@
package dao
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"reflect"
"strings"
"sync"
"go-common/app/admin/ep/tapd/model"
"go-common/library/cache/memcache"
"go-common/library/log"
)
var rwmLock = new(sync.RWMutex)
// CallHookUrl Call Hook Url.
func (d *Dao) CallHookUrl(c context.Context, URL string, body interface{}) (err error) {
var (
req *http.Request
res = make(map[string]interface{})
)
if req, err = d.newRequest(http.MethodPost, URL, body); err != nil {
log.Error("d.CallHookUrl url(%s) err(%v)", URL, err)
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Error("d.CallHookUrl url(%s) err(%v)", URL, err)
}
return
}
// CallHookUrlAsForm Call Hook Url As Form.
func (d *Dao) CallHookUrlAsForm(c context.Context, URL string, body map[string]interface{}) (err error) {
var (
res = make(map[string]interface{})
req *http.Request
)
data := make(url.Values)
for mapKey := range body {
typeKind := reflect.TypeOf(body[mapKey]).Kind()
switch typeKind {
case reflect.Int:
data[mapKey] = []string{fmt.Sprintf("%d", body[mapKey].(int))}
case reflect.Int64:
data[mapKey] = []string{fmt.Sprintf("%d", body[mapKey].(int64))}
case reflect.Float64:
data[mapKey] = []string{fmt.Sprintf("%.0f", body[mapKey].(float64))}
case reflect.String:
data[mapKey] = []string{body[mapKey].(string)}
default:
data[mapKey] = []string{fmt.Sprint(body[mapKey])}
}
}
if req, err = http.NewRequest(http.MethodPost, URL, strings.NewReader(data.Encode())); err != nil {
return
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
err = d.httpClient.Do(c, req, &res)
return
}
// SaveEnableHookURLToCache Save Enable Hook URL To Cache.
func (d *Dao) SaveEnableHookURLToCache() (eventMap map[string][]*model.HookUrl, err error) {
rwmLock.Lock()
defer rwmLock.Unlock()
var urlEvents []*model.UrlEvent
eventMap = make(map[string][]*model.HookUrl)
// 获取有效event
if urlEvents, err = d.QueryURLEventByStatus(model.HookEventStatusEnable); err != nil {
return
}
//倒查url 过滤不存在或无效状态
for _, urlEvent := range urlEvents {
var hookURL *model.HookUrl
if hookURL, err = d.QueryHookURLByID(urlEvent.UrlID); err != nil || hookURL.ID == 0 || hookURL.Status != model.HookURLStatusEnable {
continue
}
eventMap[urlEvent.Event] = append(eventMap[urlEvent.Event], hookURL)
}
for eventMapKey := range eventMap {
item := &memcache.Item{Key: eventMapKey, Object: eventMap[eventMapKey], Flags: memcache.FlagJSON, Expiration: d.expire}
d.tokenCacheSave(context.Background(), item)
}
return
}
// GetEnableHookURLFromCacheAndSaveIfNot Get Enable Hook URL From Cache and save if not.
func (d *Dao) GetEnableHookURLFromCacheAndSaveIfNot(event model.Event) (hookURLs []*model.HookUrl, err error) {
var (
conn = d.mc.Get(context.Background())
item *memcache.Item
)
rwmLock.RLock()
defer rwmLock.RUnlock()
defer conn.Close()
if item, err = conn.Get(string(event)); err == nil {
if err = json.Unmarshal(item.Value, &hookURLs); err != nil {
log.Error("GetEnableHookURLFromCache json parse error(%v)", err)
}
return
}
var eventMap map[string][]*model.HookUrl
if eventMap, err = d.SaveEnableHookURLToCache(); err != nil {
return
}
hookURLs = eventMap[string(event)]
return
}
// GetEnableHookURLFromCache Get Enable Hook URL From Cache.
func (d *Dao) GetEnableHookURLFromCache(event model.Event) (hookURLs []*model.HookUrl, err error) {
var (
conn = d.mc.Get(context.Background())
item *memcache.Item
)
rwmLock.RLock()
defer rwmLock.RUnlock()
defer conn.Close()
if item, err = conn.Get(string(event)); err == nil {
if err = json.Unmarshal(item.Value, &hookURLs); err != nil {
log.Error("GetEnableHookURLFromCache json parse error(%v)", err)
}
return
}
return
}
// GetEnableHookURLFromDB Get Enable Hook URL From DB.
func (d *Dao) GetEnableHookURLFromDB(event model.Event) (hookURLs []*model.HookUrl, err error) {
var urlEvents []*model.UrlEvent
if urlEvents, err = d.QueryURLEventByEventAndStatus(string(event), model.HookEventStatusEnable); err != nil {
return
}
for _, urlEvent := range urlEvents {
var hookURL *model.HookUrl
if hookURL, err = d.QueryHookURLByID(urlEvent.UrlID); err != nil {
return
}
hookURLs = append(hookURLs, hookURL)
}
return
}

View File

@@ -0,0 +1,37 @@
package dao
import "go-common/app/admin/ep/tapd/model"
// AddEventLog Add Event Log.
func (d *Dao) AddEventLog(eventLog *model.EventLog) error {
return d.db.Create(eventLog).Error
}
// UpdateEventLog Update Event Log.
func (d *Dao) UpdateEventLog(eventLog *model.EventLog) error {
return d.db.Model(&model.EventLog{}).Where("id=?", eventLog.ID).Update(eventLog).Error
}
//FindEventLogs Find Event Logs.
func (d *Dao) FindEventLogs(req *model.QueryEventLogReq) (total int64, eventLogs []*model.EventLog, err error) {
gDB := d.db.Model(&model.EventLog{})
if req.WorkspaceID > 0 {
gDB = gDB.Where("workspace_id=?", req.WorkspaceID)
}
if req.EventID > 0 {
gDB = gDB.Where("event_id=?", req.EventID)
}
if string(req.Event) != "" {
gDB = gDB.Where("event like ?", string(req.Event)+_wildcards)
}
if err = gDB.Count(&total).Error; err != nil {
return
}
err = gDB.Order("ctime desc").Offset((req.PageNum - 1) * req.PageSize).Limit(req.PageSize).Find(&eventLogs).Error
return
}

View File

@@ -0,0 +1,120 @@
package dao
import (
"go-common/app/admin/ep/tapd/model"
"go-common/library/ecode"
)
const _wildcards = "%"
// AddHookURL Add Hook URL.
func (d *Dao) AddHookURL(hookURL *model.HookUrl) error {
return d.db.Create(hookURL).Error
}
// UpdateHookURL Update Hook URL.
func (d *Dao) UpdateHookURL(hookURL *model.HookUrl) error {
return d.db.Model(&model.HookUrl{}).Where("id=?", hookURL.ID).Update(hookURL).Error
}
// QueryHookURLByID Query Hook URL By ID.
func (d *Dao) QueryHookURLByID(id int64) (hookURL *model.HookUrl, err error) {
hookURL = &model.HookUrl{}
err = d.db.Model(&model.HookUrl{}).Where("id = ?", id).First(hookURL).Error
if err == ecode.NothingFound {
err = nil
}
return
}
// AddHookURLandEvent Add Hook URL and Event.
func (d *Dao) AddHookURLandEvent(hookURL *model.HookUrl, urlEvents []*model.UrlEvent) (err error) {
tx := d.db.Begin()
if err = tx.Error; err != nil {
return
}
if err = tx.Create(hookURL).Error; err != nil {
tx.Rollback()
return
}
for _, urlEvent := range urlEvents {
urlEvent.UrlID = hookURL.ID
if err = tx.Create(urlEvent).Error; err != nil {
tx.Rollback()
return
}
}
if err = tx.Commit().Error; err != nil {
tx.Rollback()
}
return
}
// UpdateHookURLandEvent Update Hook URL and Event.
func (d *Dao) UpdateHookURLandEvent(hookURL *model.HookUrl, urlEvents []*model.UrlEvent) (err error) {
tx := d.db.Begin()
if err = tx.Error; err != nil {
return
}
if err = tx.Model(model.HookUrl{}).Where("id=?", hookURL.ID).
Updates(map[string]interface{}{"url": hookURL.URL, "workspace_id": hookURL.WorkspaceID, "status": hookURL.Status, "update_by": hookURL.UpdateBy}).
Error; err != nil {
tx.Rollback()
return
}
for _, urlEvent := range urlEvents {
if urlEvent.ID != 0 {
//update
if err = tx.Model(model.UrlEvent{}).Where("id=?", urlEvent.ID).Update(urlEvent).Error; err != nil {
tx.Rollback()
return
}
} else {
//add
if err = tx.Create(urlEvent).Error; err != nil {
tx.Rollback()
return
}
}
}
if err = tx.Commit().Error; err != nil {
tx.Rollback()
}
return
}
//FindHookURLs Find Hook URLs.
func (d *Dao) FindHookURLs(req *model.QueryHookURLReq) (total int64, hookURLs []*model.HookUrl, err error) {
gDB := d.db.Model(&model.HookUrl{})
if req.ID > 0 {
gDB = gDB.Where("id=?", req.ID)
}
if req.Status > 0 {
gDB = gDB.Where("status=?", req.Status)
}
if req.UpdateBy != "" {
gDB = gDB.Where("update_by=?", req.UpdateBy)
}
if req.URL != "" {
gDB = gDB.Where("url like ?", req.URL+_wildcards)
}
if err = gDB.Count(&total).Error; err != nil {
return
}
err = gDB.Order("ctime desc").Offset((req.PageNum - 1) * req.PageSize).Limit(req.PageSize).Find(&hookURLs).Error
return
}

View File

@@ -0,0 +1,52 @@
package dao
import (
"go-common/app/admin/ep/tapd/model"
"go-common/library/ecode"
)
// AddURLEvent Add URL Event.
func (d *Dao) AddURLEvent(urlEvent *model.UrlEvent) error {
return d.db.Create(urlEvent).Error
}
// QueryURLEventByUrlAndEvent Query URL Event By Url And Event.
func (d *Dao) QueryURLEventByUrlAndEvent(urlID int64, eventType string) (urlEvents []*model.UrlEvent, err error) {
err = d.db.Model(&model.HookUrl{}).Where("url_id = ? and event = ?", urlID, eventType).Find(&urlEvents).Error
if err == ecode.NothingFound {
err = nil
}
return
}
// QueryURLEventByUrl Query URL Event By Url.
func (d *Dao) QueryURLEventByUrl(urlID int64) (urlEvents []*model.UrlEvent, err error) {
err = d.db.Model(&model.HookUrl{}).Where("url_id = ?", urlID).Find(&urlEvents).Error
if err == ecode.NothingFound {
err = nil
}
return
}
// QueryURLEventByEventAndStatus Query URL Event By Event and status.
func (d *Dao) QueryURLEventByEventAndStatus(event string, status int) (urlEvents []*model.UrlEvent, err error) {
err = d.db.Model(&model.HookUrl{}).Where("event = ? and status = ?", event, status).Find(&urlEvents).Error
if err == ecode.NothingFound {
err = nil
}
return
}
// QueryURLEventByStatus Query URL Event By Status.
func (d *Dao) QueryURLEventByStatus(status int) (urlEvents []*model.UrlEvent, err error) {
err = d.db.Model(&model.HookUrl{}).Where("status = ?", status).Find(&urlEvents).Error
if err == ecode.NothingFound {
err = nil
}
return
}
// UpdateURLEventStatus Update URL Event status.
func (d *Dao) UpdateURLEventStatus(id int64, status int) error {
return d.db.Model(&model.UrlEvent{}).Where("id=?", id).Update("status", status).Error
}

View File

@@ -0,0 +1,41 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"hook.go",
"http.go",
],
importpath = "go-common/app/admin/ep/tapd/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/ep/tapd/conf:go_default_library",
"//app/admin/ep/tapd/model:go_default_library",
"//app/admin/ep/tapd/service:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/binding:go_default_library",
"//library/net/http/blademaster/middleware/permit:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,135 @@
package http
import (
"go-common/app/admin/ep/tapd/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
const (
_sessUnKey = "username"
)
func tapdCallback(c *bm.Context) {
c.JSON(nil, svc.TapdCallBack(c, c.Request.Body))
}
func updateHook(c *bm.Context) {
var (
err error
v = &model.HookURLUpdateReq{}
username string
)
if err = c.BindWith(v, binding.JSON); err != nil {
return
}
if username, err = getUsername(c); err != nil {
return
}
c.JSON(svc.UpdateHookURL(c, username, v))
}
func queryHook(c *bm.Context) {
var (
v = &model.QueryHookURLReq{}
err error
)
if err = c.BindWith(v, binding.JSON); err != nil {
return
}
if err = v.Verify(); err != nil {
c.JSON(nil, err)
return
}
c.JSON(svc.QueryHookURL(c, v))
}
func queryURLEvent(c *bm.Context) {
var (
v = new(struct {
ID int64 `form:"url_id"`
})
err error
)
if err = c.Bind(v); err != nil {
return
}
c.JSON(svc.QueryURLEvent(c, v.ID))
}
func queryEventLog(c *bm.Context) {
var (
v = &model.QueryEventLogReq{}
err error
)
if err = c.BindWith(v, binding.JSON); err != nil {
return
}
if err = v.Verify(); err != nil {
c.JSON(nil, err)
return
}
c.JSON(svc.QueryEventLog(c, v))
}
func saveHookUrlInCache(c *bm.Context) {
c.JSON(svc.SaveEnableHookURL(c))
}
func queryHookUrlInCache(c *bm.Context) {
c.JSON(svc.QueryEnableHookURLInCache(c))
}
func test(c *bm.Context) {
var (
v = new(struct {
Data interface{} `json:"data"`
Code int `json:"code"`
})
err error
)
if err = c.BindWith(v, binding.JSON); err != nil {
return
}
log.Info("WorkspaceID [%d]", v.Data)
c.JSON(v, err)
}
func testform(c *bm.Context) {
var (
v = new(struct {
ID string `form:"event"`
})
err error
)
if err = c.Bind(v); err != nil {
return
}
log.Info("fengyifeng WorkspaceID", v.ID)
c.JSON(v, err)
}
func getUsername(c *bm.Context) (username string, err error) {
user, exist := c.Get(_sessUnKey)
if !exist {
err = ecode.AccessKeyErr
c.JSON(nil, err)
return
}
username = user.(string)
return
}

View File

@@ -0,0 +1,74 @@
package http
import (
"net/http"
"go-common/app/admin/ep/tapd/conf"
"go-common/app/admin/ep/tapd/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/permit"
)
var (
svc *service.Service
authSvc *permit.Permit
)
// Init init
func Init(c *conf.Config, s *service.Service) {
svc = s
authSvc = permit.New(c.Auth)
engine := bm.DefaultServer(c.BM)
engine.Ping(ping)
outerRouter(engine)
if err := engine.Start(); err != nil {
log.Error("engine.Start error(%v)", err)
panic(err)
}
}
// outerRouter init outer router api path.
func outerRouter(e *bm.Engine) {
inner := e.Group("/internal/ep/tapd")
{
inner.GET("/version", getVersion)
inner.POST("/test", test)
inner.POST("/testform", testform)
version1 := inner.Group("/v1", authSvc.Permit(""))
{
version1.POST("/hook/update", updateHook)
version1.POST("/hook/query", queryHook)
version1.GET("/hook/event/query", queryURLEvent)
version1.GET("/hook/cache/save", saveHookUrlInCache)
version1.GET("/hook/cache/query", queryHookUrlInCache)
version1.POST("/eventlog/query", queryEventLog)
}
}
outer := e.Group("/ep/tapd")
{
outer.POST("/callback", tapdCallback)
outer.GET("/version", getVersion)
}
}
func ping(c *bm.Context) {
if err := svc.Ping(c); err != nil {
log.Error("ping error(%v)", err)
c.AbortWithStatus(http.StatusServiceUnavailable)
}
}
func getVersion(c *bm.Context) {
v := new(struct {
Version string `json:"version"`
})
v.Version = "v.0.0.7"
c.JSON(v, nil)
}

View File

@@ -0,0 +1,33 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"constants.go",
"dto.go",
"tapd.go",
],
importpath = "go-common/app/admin/ep/tapd/model",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = ["//library/ecode:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,39 @@
package model
// pagination.
const (
DefaultPageSize = 5
DefaultPageNum = 1
)
// Hook URL Status element
const (
HookURLStatusEnable = 1
HookURLStatusDisable = 2
)
// HookEventStatusEnable
const (
HookEventStatusEnable = 1
HookEventStatusDisable = 2
)
// Event Event
type Event string
// event element
const (
StoryCreate Event = "story::create"
BugCreate = "bug::create"
TaskCreate = "task::create"
LaunchformCreate = "launchform::create"
StoryUpdate = "story::update"
BugUpdate = "bug::update"
TaskUpdate = "task::update"
LaunchformUpdate = "launchform::update"
StoryDelete = "story::delete"
BugDelete = "bug::delete"
TaskDelete = "task::delete"
)

View File

@@ -0,0 +1,36 @@
package model
import (
"time"
)
// HookUrl Hook Url.
type HookUrl struct {
ID int64 `json:"id" gorm:"auto_increment;primary_key;column:id"`
URL string `json:"url" gorm:"column:url"`
WorkspaceID int `json:"workspace_id" gorm:"column:workspace_id"`
Status int `json:"status" gorm:"column:status"`
UpdateBy string `json:"update_by" gorm:"column:update_by"`
CTime time.Time `json:"ctime" gorm:"column:ctime"`
UTime time.Time `json:"mtime" gorm:"column:mtime"`
}
// UrlEvent Url Event.
type UrlEvent struct {
ID int64 `json:"id" gorm:"auto_increment;primary_key;column:id"`
Event string `json:"event" gorm:"column:event"`
UrlID int64 `json:"url_id" gorm:"column:url_id"`
Status int `json:"status" gorm:"column:status"`
CTime time.Time `json:"ctime" gorm:"column:ctime"`
UTime time.Time `json:"mtime" gorm:"column:mtime"`
}
// EventLog Event Log.
type EventLog struct {
ID int64 `json:"id" gorm:"auto_increment;primary_key;column:id"`
Event string `json:"event" gorm:"column:event"`
WorkspaceID int `json:"workspace_id" gorm:"column:workspace_id"`
EventID int `json:"event_id" gorm:"column:event_id"`
CTime time.Time `json:"ctime" gorm:"column:ctime"`
UTime time.Time `json:"mtime" gorm:"column:mtime"`
}

View File

@@ -0,0 +1,77 @@
package model
import "go-common/library/ecode"
// Pagination Pagination.
type Pagination struct {
PageSize int `form:"page_size" json:"page_size"`
PageNum int `form:"page_num" json:"page_num"`
}
// Verify verify the value of pageNum and pageSize.
func (p *Pagination) Verify() error {
if p.PageNum < 0 {
return ecode.MerlinIllegalPageNumErr
} else if p.PageNum == 0 {
p.PageNum = DefaultPageNum
}
if p.PageSize < 0 {
return ecode.MerlinIllegalPageSizeErr
} else if p.PageSize == 0 {
p.PageSize = DefaultPageSize
}
return nil
}
// HookURLUpdateReq Hook URL Update Req.
type HookURLUpdateReq struct {
ID int64 `json:"id"`
URL string `json:"url"`
WorkspaceID int `json:"workspace_id"`
Status int `json:"status"`
Events []string `json:"events"`
}
// QueryHookURLReq Query Hook URL Req
type QueryHookURLReq struct {
Pagination
HookURLUpdateReq
UpdateBy string `json:"update_by"`
}
// QueryHookURLRep Query Hook URL Rep.
type QueryHookURLRep struct {
Pagination
Total int64 `json:"total"`
HookUrls []*HookUrl `json:"hook_urls"`
}
// EventRequest Event Request.
type EventRequest struct {
Event Event `json:"event"`
WorkspaceID string `json:"workspace_id"`
EventID string `json:"id"`
Created string `json:"created"`
Secret string `json:"secret"`
}
// EventCallBackRequest Event CallBack Request.
type EventCallBackRequest struct {
Code int `json:"code"`
Data interface{} `json:"data"`
}
// QueryEventLogReq Query Event Log Req
type QueryEventLogReq struct {
Pagination
Event Event `json:"event"`
WorkspaceID int `json:"workspace_id"`
EventID int `json:"id"`
}
// QueryEventLogRep Query Event Log Rep.
type QueryEventLogRep struct {
Pagination
Total int64 `json:"total"`
EventLogs []*EventLog `json:"event_logs"`
}

View File

@@ -0,0 +1,39 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"hook.go",
"service.go",
],
importpath = "go-common/app/admin/ep/tapd/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/ep/tapd/conf:go_default_library",
"//app/admin/ep/tapd/dao:go_default_library",
"//app/admin/ep/tapd/model:go_default_library",
"//library/ecode:go_default_library",
"//library/sync/pipeline/fanout:go_default_library",
"//vendor/github.com/robfig/cron:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,280 @@
package service
import (
"context"
"encoding/json"
"io"
"io/ioutil"
"strconv"
"time"
"go-common/app/admin/ep/tapd/model"
"go-common/library/ecode"
)
// TapdCallBack Tapd Call Back.
func (s *Service) TapdCallBack(c context.Context, body io.ReadCloser) (err error) {
var (
URLs []string
jsonByte []byte
eventRequest *model.EventRequest
eventInterface = make(map[string]interface{})
createdTime time.Time
workspaceID int
eventID int
)
if jsonByte, err = ioutil.ReadAll(body); err != nil {
return
}
//get event
if err = json.Unmarshal(jsonByte, &eventRequest); err != nil {
return
}
if eventRequest.Secret != s.c.Tapd.CallbackToken {
err = ecode.Unauthorized
return
}
if err = json.Unmarshal(jsonByte, &eventInterface); err != nil {
return
}
// add log
workspaceID, _ = strconv.Atoi(eventRequest.WorkspaceID)
eventID, _ = strconv.Atoi(eventRequest.EventID)
eventLog := &model.EventLog{
Event: string(eventRequest.Event),
WorkspaceID: workspaceID,
EventID: eventID,
}
if err = s.dao.AddEventLog(eventLog); err != nil {
return
}
//handle special param
if createdTime, err = time.Parse("2006-01-02 15:04:05", eventRequest.Created); err != nil {
return
}
eventInterface["id"] = eventRequest.EventID
eventInterface["created"] = createdTime.Unix()
if URLs, err = s.GetEnableHookURL(c, eventRequest.Event, workspaceID); err != nil {
return
}
for _, URL := range URLs {
s.transferChan.Do(context.Background(), func(c context.Context) {
s.dao.CallHookUrlAsForm(context.Background(), URL, eventInterface)
})
}
return
}
// GetEnableHookURL Get Enable Hook URL.
func (s *Service) GetEnableHookURL(c context.Context, eventType model.Event, workspaceID int) (URLs []string, err error) {
var hookURLs []*model.HookUrl
if s.c.Tapd.UseCache {
if hookURLs, err = s.dao.GetEnableHookURLFromCacheAndSaveIfNot(eventType); err != nil {
return
}
} else {
if hookURLs, err = s.dao.GetEnableHookURLFromDB(eventType); err != nil {
return
}
}
for _, hookURL := range hookURLs {
if hookURL.ID > 0 && hookURL.Status == model.HookURLStatusEnable {
if hookURL.WorkspaceID == 0 {
URLs = append(URLs, hookURL.URL)
} else {
if workspaceID == hookURL.WorkspaceID {
URLs = append(URLs, hookURL.URL)
}
}
}
}
return
}
// SaveEnableHookURL Save Enable Hook URL.
func (s *Service) SaveEnableHookURL(c context.Context) (eventMap map[string][]*model.HookUrl, err error) {
return s.dao.SaveEnableHookURLToCache()
}
// QueryEnableHookURLInCache query Enable Hook URL In Cache.
func (s *Service) QueryEnableHookURLInCache(c context.Context) (hookURLs []*model.HookUrl, err error) {
return s.dao.GetEnableHookURLFromCache(model.StoryCreate)
}
// UpdateHookURL Update Hook URL
func (s *Service) UpdateHookURL(c context.Context, username string, req *model.HookURLUpdateReq) (rep map[string]interface{}, err error) {
var repID int64
if req.ID > 0 {
// update
var (
urlEvents []*model.UrlEvent
urlEventsInDB []*model.UrlEvent
hookUrlInDB *model.HookUrl
)
if hookUrlInDB, err = s.dao.QueryHookURLByID(req.ID); err != nil {
return
}
if hookUrlInDB.ID == 0 {
err = ecode.NothingFound
return
}
hookUrl := &model.HookUrl{
ID: req.ID,
URL: req.URL,
WorkspaceID: req.WorkspaceID,
Status: req.Status,
UpdateBy: username,
}
if urlEventsInDB, err = s.dao.QueryURLEventByUrl(req.ID); err != nil {
return
}
//新增 或修改为可用
for _, eventStr := range req.Events {
urlEvent := &model.UrlEvent{
Event: eventStr,
Status: model.HookEventStatusEnable,
UrlID: req.ID,
}
for _, urlEventInDB := range urlEventsInDB {
if urlEventInDB.Event == eventStr {
urlEvent.ID = urlEventInDB.ID
break
}
}
urlEvents = append(urlEvents, urlEvent)
}
// 未传值视为disable
for _, urlEventInDB := range urlEventsInDB {
if urlEventInDB.Status == model.HookEventStatusEnable {
var isEnable bool
for _, eventStr := range req.Events {
if eventStr == urlEventInDB.Event {
isEnable = true
break
}
}
if !isEnable {
urlEvent := &model.UrlEvent{
Event: urlEventInDB.Event,
Status: model.HookEventStatusDisable,
ID: urlEventInDB.ID,
UrlID: urlEventInDB.UrlID,
}
urlEvents = append(urlEvents, urlEvent)
}
}
}
if err = s.dao.UpdateHookURLandEvent(hookUrl, urlEvents); err != nil {
return
}
repID = req.ID
} else {
// add
var urlEvents []*model.UrlEvent
hookUrl := &model.HookUrl{
URL: req.URL,
WorkspaceID: req.WorkspaceID,
Status: req.Status,
UpdateBy: username,
}
for _, eventStr := range req.Events {
urlEvent := &model.UrlEvent{
Event: eventStr,
Status: model.HookEventStatusEnable,
}
urlEvents = append(urlEvents, urlEvent)
}
if err = s.dao.AddHookURLandEvent(hookUrl, urlEvents); err != nil {
return
}
repID = hookUrl.ID
}
rep = make(map[string]interface{})
rep["url_id"] = repID
return
}
// QueryHookURL Query Hook URL.
func (s *Service) QueryHookURL(c context.Context, req *model.QueryHookURLReq) (rep *model.QueryHookURLRep, err error) {
var (
total int64
hookUrls []*model.HookUrl
)
if total, hookUrls, err = s.dao.FindHookURLs(req); err != nil {
return
}
rep = &model.QueryHookURLRep{
Pagination: model.Pagination{
PageNum: req.PageNum,
PageSize: req.PageSize,
},
Total: total,
HookUrls: hookUrls,
}
return
}
// QueryURLEvent Query URL Event.
func (s *Service) QueryURLEvent(c context.Context, urlID int64) (rep []*model.UrlEvent, err error) {
var tmpUrlEvents []*model.UrlEvent
if tmpUrlEvents, err = s.dao.QueryURLEventByUrl(urlID); err != nil {
return
}
for _, tmpUrlEvent := range tmpUrlEvents {
if tmpUrlEvent.Status == model.HookEventStatusEnable {
rep = append(rep, tmpUrlEvent)
}
}
return
}
// QueryEventLog Query Event Log.
func (s *Service) QueryEventLog(c context.Context, req *model.QueryEventLogReq) (rep *model.QueryEventLogRep, err error) {
var (
total int64
eventLogs []*model.EventLog
)
if total, eventLogs, err = s.dao.FindEventLogs(req); err != nil {
return
}
rep = &model.QueryEventLogRep{
Pagination: model.Pagination{
PageNum: req.PageNum,
PageSize: req.PageSize,
},
Total: total,
EventLogs: eventLogs,
}
return
}

View File

@@ -0,0 +1,49 @@
package service
import (
"context"
"go-common/app/admin/ep/tapd/conf"
"go-common/app/admin/ep/tapd/dao"
"go-common/library/sync/pipeline/fanout"
"github.com/robfig/cron"
)
// Service struct
type Service struct {
c *conf.Config
dao *dao.Dao
transferChan *fanout.Fanout
cron *cron.Cron
}
// New init.
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
dao: dao.New(c),
transferChan: fanout.New("cache", fanout.Worker(5), fanout.Buffer(10240)),
}
if s.c.Scheduler.Active {
s.cron = cron.New()
if err := s.cron.AddFunc(c.Scheduler.UpdateHookURLCacheTask, func() { s.dao.SaveEnableHookURLToCache() }); err != nil {
panic(err)
}
s.cron.Start()
}
return
}
// Close Service.
func (s *Service) Close() {
s.dao.Close()
}
// Ping check server ok.
func (s *Service) Ping(c context.Context) (err error) {
err = s.dao.Ping(c)
return
}