mirror of https://github.com/sunface/rust-course
resolved #4
parent
aca6193dec
commit
1e53668872
@ -0,0 +1,125 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/rpc"
|
||||||
|
"tfgo/tfe/api/async"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mafanr/juz/api/filter"
|
||||||
|
"github.com/mafanr/juz/api/manage"
|
||||||
|
"github.com/mafanr/juz/api/stats"
|
||||||
|
"github.com/mafanr/juz/misc"
|
||||||
|
|
||||||
|
"github.com/mafanr/g"
|
||||||
|
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
"github.com/labstack/echo"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ApiServer struct {
|
||||||
|
manage *manage.Manage
|
||||||
|
router *router
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ApiServer) Start() {
|
||||||
|
g.L.Info("start tfe..")
|
||||||
|
|
||||||
|
// 初始化mysql连接
|
||||||
|
misc.InitMysql()
|
||||||
|
|
||||||
|
// 从mysql中加载所有的api信息到内存中
|
||||||
|
p.loadData()
|
||||||
|
|
||||||
|
p.manage = &manage.Manage{}
|
||||||
|
go p.manage.Start()
|
||||||
|
|
||||||
|
p.router = &router{p, &filter.Filter{}}
|
||||||
|
|
||||||
|
// 连接到traffic rpc服务
|
||||||
|
p.initTraffic()
|
||||||
|
|
||||||
|
// 启动proxy http服务
|
||||||
|
go p.listen()
|
||||||
|
|
||||||
|
// 启动metrics收集
|
||||||
|
http.Handle("/metrics", promhttp.Handler())
|
||||||
|
http.ListenAndServe(":6062", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *ApiServer) Shutdown() {
|
||||||
|
g.L.Info("shutdown tfe..")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ApiServer) listen() {
|
||||||
|
e := echo.New()
|
||||||
|
|
||||||
|
// 回调相关
|
||||||
|
//同步回调接口
|
||||||
|
e.Any("/*", p.router.route, timing)
|
||||||
|
//异步回调接口
|
||||||
|
e.POST("/notify", async.Notify)
|
||||||
|
|
||||||
|
e.Logger.Fatal(e.Start(":" + misc.Conf.Api.Port))
|
||||||
|
}
|
||||||
|
|
||||||
|
func timing(f echo.HandlerFunc) echo.HandlerFunc {
|
||||||
|
return func(c echo.Context) error {
|
||||||
|
ts := time.Now()
|
||||||
|
rid := (ts.UnixNano()/10)*10 + misc.Conf.Api.ServerID
|
||||||
|
g.L.Info("New request accepted", zap.Int64("rid", rid), zap.String("ip", c.RealIP()))
|
||||||
|
c.Set("rid", rid)
|
||||||
|
defer func() {
|
||||||
|
// 统计请求指标
|
||||||
|
apiID := c.Get("api_id").(string)
|
||||||
|
service := c.Get("service").(string)
|
||||||
|
label := c.Get("label").(string)
|
||||||
|
stats.Req.With(prometheus.Labels{
|
||||||
|
"api_id": apiID,
|
||||||
|
"service": service,
|
||||||
|
"label": label,
|
||||||
|
}).Observe(float64(time.Now().Sub(ts).Nanoseconds() / 1e6))
|
||||||
|
|
||||||
|
err := c.Get("error_msg")
|
||||||
|
if err == nil {
|
||||||
|
g.L.Info("Request success", zap.Int64("rid", rid))
|
||||||
|
} else {
|
||||||
|
g.L.Info("Request failed", zap.Int64("rid", rid), zap.Error(err.(error)))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return f(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *ApiServer) initTraffic() {
|
||||||
|
r, err := rpc.Dial("tcp", misc.Conf.Traffic.Host+":"+misc.Conf.Traffic.Port)
|
||||||
|
if err != nil {
|
||||||
|
g.L.Fatal("connect to raffic error", zap.Error(err))
|
||||||
|
}
|
||||||
|
as.router.Filter.Rpc = r
|
||||||
|
|
||||||
|
// 定时检测rpc连接的存活性
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
var res int
|
||||||
|
err := as.router.Filter.Rpc.Call("RateLimiter.Ping", 1, &res)
|
||||||
|
if err != nil || res != 1 {
|
||||||
|
g.L.Warn("rpc ping failed", zap.Error(err))
|
||||||
|
r, err := rpc.Dial("tcp", misc.Conf.Traffic.Host+":"+misc.Conf.Traffic.Port)
|
||||||
|
if err != nil {
|
||||||
|
g.L.Warn("re-connect to traffic error", zap.Error(err))
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
as.router.Filter.Rpc = r
|
||||||
|
g.L.Info("re-connect to traffic ok")
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mafanr/g"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTfeStartStop(t *testing.T) {
|
||||||
|
p := &Proxy{}
|
||||||
|
g.InitConfig("../tfe.conf")
|
||||||
|
g.InitLogger()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
p.Start()
|
||||||
|
}()
|
||||||
|
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
|
resp, err := http.Get("http://localhost:" + g.Conf.Common.Port + "/service/api?service_id=" + g.TEST_API_NAME)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, resp.StatusCode)
|
||||||
|
|
||||||
|
p.Shutdown()
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package filter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/mafanr/juz/api/req"
|
||||||
|
"github.com/mafanr/juz/misc"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
对用户请求的参数进行验证
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (f *Filter) verifyParam(r *req.Request) error {
|
||||||
|
if r.Api.VerifyOn == misc.PARAM_VERIFY_OFF {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range r.Params {
|
||||||
|
regI, ok := r.Api.ParamRules.Load(k)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
reg := regI.(*regexp.Regexp)
|
||||||
|
if !reg.MatchString(v) { // 参数不合法
|
||||||
|
return fmt.Errorf("param %s,value %s, verify failed", k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mafanr/juz/g"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoadRow(t *testing.T) {
|
||||||
|
g.InitConfig("../tfe.conf")
|
||||||
|
g.InitLogger()
|
||||||
|
|
||||||
|
g.InitMysql()
|
||||||
|
|
||||||
|
p := &Proxy{}
|
||||||
|
p.apis = &sync.Map{}
|
||||||
|
p.loadAPIRow(g.TEST_API_NAME)
|
||||||
|
_, ok := p.apis.Load(g.TEST_API_NAME)
|
||||||
|
assert.True(t, ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadAll(t *testing.T) {
|
||||||
|
g.InitConfig("../tfe.conf")
|
||||||
|
g.InitLogger()
|
||||||
|
|
||||||
|
g.InitMysql()
|
||||||
|
|
||||||
|
p := &Proxy{}
|
||||||
|
p.apis = &sync.Map{}
|
||||||
|
p.loadAllAPIs()
|
||||||
|
|
||||||
|
_, ok := p.apis.Load(g.TEST_API_NAME)
|
||||||
|
assert.True(t, ok)
|
||||||
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
package audit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/mafanr/g"
|
||||||
|
|
||||||
|
"github.com/sunface/talent"
|
||||||
|
|
||||||
|
"github.com/labstack/echo"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TypeService = 1
|
||||||
|
TypeApi = 2
|
||||||
|
TypeStrategy = 3
|
||||||
|
TypePrivilegy = 4
|
||||||
|
TypeBatch = 5
|
||||||
|
|
||||||
|
OpCreate = 1
|
||||||
|
OpEdit = 2
|
||||||
|
OpRelease = 3
|
||||||
|
OpOffline = 4
|
||||||
|
OpDelete = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
func Log(userID string, service string, targetType int, targetID string, opType int, content string, desc string) {
|
||||||
|
newc := g.B64.EncodeToString(talent.String2Bytes(content))
|
||||||
|
query := fmt.Sprintf("insert into audit_log (user_id,service,target_type,target_id,op_type,content,description) values ('%s','%s','%d','%s','%d','%s','%s')",
|
||||||
|
userID, service, targetType, targetID, opType, newc, desc)
|
||||||
|
_, err := g.DB.Exec(query)
|
||||||
|
if err != nil {
|
||||||
|
g.L.Info("record audit log error", zap.Error(err), zap.String("query", query))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Count(c echo.Context) error {
|
||||||
|
tt := c.FormValue("target_type")
|
||||||
|
tid := c.FormValue("target_id")
|
||||||
|
if tt == "" || tid == "" {
|
||||||
|
return c.JSON(http.StatusBadRequest, g.Result{
|
||||||
|
Status: http.StatusBadRequest,
|
||||||
|
ErrCode: g.ParamEmptyC,
|
||||||
|
Message: g.ParamEmptyE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var query string
|
||||||
|
if tt == "0" {
|
||||||
|
query = fmt.Sprintf("select count(1) from audit_log where service in (%s)", tid)
|
||||||
|
} else {
|
||||||
|
query = fmt.Sprintf("select count(1) from audit_log where target_id='%s' and target_type='%s'", tid, tt)
|
||||||
|
}
|
||||||
|
rows, err := g.DB.Query(query)
|
||||||
|
if err != nil {
|
||||||
|
g.L.Info("access database error", zap.Error(err), zap.String("query", query))
|
||||||
|
return c.JSON(http.StatusInternalServerError, g.Result{
|
||||||
|
Status: http.StatusInternalServerError,
|
||||||
|
ErrCode: g.DatabaseC,
|
||||||
|
Message: g.DatabaseE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var total int
|
||||||
|
rows.Next()
|
||||||
|
rows.Scan(&total)
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, g.Result{
|
||||||
|
Status: http.StatusOK,
|
||||||
|
Data: total,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuditLog struct {
|
||||||
|
ID int `db:"id" json:"-"`
|
||||||
|
UserID string `db:"user_id" json:"user_id"`
|
||||||
|
Service string `db:"service" json:"service"`
|
||||||
|
TargetType string `db:"target_type" json:"target_type"`
|
||||||
|
TargetID string `db:"target_id" json:"target_id"`
|
||||||
|
OpType string `db:"op_type" json:"op_type"`
|
||||||
|
Content string `db:"content" json:"content"`
|
||||||
|
Desc string `db:"description" json:"desc"`
|
||||||
|
ModifyDate string `db:"modify_date" json:"modify_date"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Load(c echo.Context) error {
|
||||||
|
tt := c.FormValue("target_type")
|
||||||
|
tid := c.FormValue("target_id")
|
||||||
|
pageS := c.FormValue("page")
|
||||||
|
|
||||||
|
if tt == "" || tid == "" || pageS == "" {
|
||||||
|
return c.JSON(http.StatusBadRequest, g.Result{
|
||||||
|
Status: http.StatusBadRequest,
|
||||||
|
ErrCode: g.ParamEmptyC,
|
||||||
|
Message: g.ParamEmptyE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
page, _ := strconv.Atoi(pageS)
|
||||||
|
if page <= 0 {
|
||||||
|
return c.JSON(http.StatusBadRequest, g.Result{
|
||||||
|
Status: http.StatusBadRequest,
|
||||||
|
ErrCode: g.ParamInvalidC,
|
||||||
|
Message: g.ParamInvalidE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
rs := make([]AuditLog, 0)
|
||||||
|
var query string
|
||||||
|
if tt == "0" {
|
||||||
|
query = fmt.Sprintf("select * from audit_log where service in (%s) order by modify_date desc limit %d offset %d", tid, g.PER_PAGE, g.PER_PAGE*(page-1))
|
||||||
|
} else {
|
||||||
|
query = fmt.Sprintf("select * from audit_log where target_id='%s' and target_type='%s' order by modify_date desc limit %d offset %d", tid, tt, g.PER_PAGE, g.PER_PAGE*(page-1))
|
||||||
|
}
|
||||||
|
err := g.DB.Select(&rs, query)
|
||||||
|
if err != nil {
|
||||||
|
g.L.Info("access database error", zap.Error(err), zap.String("query", query))
|
||||||
|
return c.JSON(http.StatusInternalServerError, g.Result{
|
||||||
|
Status: http.StatusInternalServerError,
|
||||||
|
ErrCode: g.DatabaseC,
|
||||||
|
Message: g.DatabaseE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, l := range rs {
|
||||||
|
b, _ := g.B64.DecodeString(l.Content)
|
||||||
|
rs[i].Content = talent.Bytes2String(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, g.Result{
|
||||||
|
Status: http.StatusOK,
|
||||||
|
Data: rs,
|
||||||
|
})
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
package manage
|
||||||
|
|
||||||
|
const (
|
||||||
|
CodeInvalidUser = 10001
|
||||||
|
ErrInvalidUser = "Username invalid"
|
||||||
|
|
||||||
|
InvalidVerifyTableC = 10002
|
||||||
|
InvalidVerifyTableE = "Param verify rule is not a legal regexp"
|
||||||
|
|
||||||
|
InvalidVerifyRuleC = 10003
|
||||||
|
InvalidVerifyRuleE = "The test data is not satisfying the verify rule"
|
||||||
|
|
||||||
|
ApiWithServicePrefixC = 10004
|
||||||
|
ApiWithServicePrefixE = "API name must be prefix with service name"
|
||||||
|
ApiWithServiceSuffixC = 10014
|
||||||
|
ApiWithServiceSuffixE = "API ID must suffix with version"
|
||||||
|
|
||||||
|
ServiceEmptyC = 10005
|
||||||
|
ServiceEmptyE = "Service name cant be empty"
|
||||||
|
|
||||||
|
ApiOnlyAlphaNumAndDotC = 10006
|
||||||
|
ApiOnlyAlphaNumAndDotE = "API name can only be consisted of alphabet and numberic"
|
||||||
|
|
||||||
|
RouteAddrWithHTTPPrefixC = 10007
|
||||||
|
RouteAddrWithHTTPPrefixE = "Backend url must prefix with http:// or https://"
|
||||||
|
|
||||||
|
RouteAddrEmptyC = 10008
|
||||||
|
RouteAddrEmptyE = "Backend url cant be empty"
|
||||||
|
|
||||||
|
RouteProtoInvalidC = 10009
|
||||||
|
RouteProtoInvalidE = "Backend type invalid"
|
||||||
|
|
||||||
|
ReqTimeoutInvalidC = 10010
|
||||||
|
ReqTimeoutInvalidE = "Timeout must be in (0,60]"
|
||||||
|
|
||||||
|
RetryTimesInvalidC = 10011
|
||||||
|
RetryTimesInvalidE = "Retry times must be in (0,5]"
|
||||||
|
|
||||||
|
RetryIntvInvalidC = 10012
|
||||||
|
RetryIntvInvalidE = "Retry interval must be in (0,30]"
|
||||||
|
|
||||||
|
TrafficRatioInvalidC = 10013
|
||||||
|
TrafficRatioInvalidE = "Traffic ratio must be in [0,100]"
|
||||||
|
|
||||||
|
ApiPathTypeInvalidC = 10014
|
||||||
|
ApiPathTypeInvalidE = "API URL type must be 0 or 1"
|
||||||
|
|
||||||
|
ApiNotExistE = "Api not exist"
|
||||||
|
ApiNotExistC = 10015
|
||||||
|
|
||||||
|
ApiOnlyAlphaNumAndUriC = 10016
|
||||||
|
ApiOnlyAlphaNumAndUriE = "API name can only be consisted of alphabet,numberic and /"
|
||||||
|
|
||||||
|
ApiReservePathC = 10017
|
||||||
|
ApiReservePathE = "You cant use the reserverd name"
|
||||||
|
|
||||||
|
APIOfflineE = "API not released"
|
||||||
|
APIOfflineC = 1058
|
||||||
|
|
||||||
|
ApiStillReleasedE = "API still being released"
|
||||||
|
ApiStillReleasedC = 1059
|
||||||
|
|
||||||
|
ApiInactiveNotLongEnoughE = "You cant delete api until 30 seconds after offline"
|
||||||
|
ApiInactiveNotLongEnoughC = 1060
|
||||||
|
|
||||||
|
StrategyNameExistE = "Strategy name already exist"
|
||||||
|
StrategyNameExistc = 1061
|
||||||
|
)
|
@ -0,0 +1,80 @@
|
|||||||
|
package manage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mafanr/g"
|
||||||
|
|
||||||
|
"github.com/labstack/echo"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m *Manage) QueryLabels(c echo.Context) error {
|
||||||
|
service := c.FormValue("service")
|
||||||
|
if service == "" {
|
||||||
|
return c.JSON(http.StatusBadRequest, g.Result{
|
||||||
|
Status: http.StatusBadRequest,
|
||||||
|
ErrCode: g.ParamEmptyC,
|
||||||
|
Message: g.ParamEmptyE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
query := fmt.Sprintf("select name from labels where service='%s'", service)
|
||||||
|
rows, err := g.DB.Query(query)
|
||||||
|
if err != nil {
|
||||||
|
g.L.Info("access database error", zap.Error(err), zap.String("query", query))
|
||||||
|
return c.JSON(http.StatusInternalServerError, g.Result{
|
||||||
|
Status: http.StatusInternalServerError,
|
||||||
|
ErrCode: g.DatabaseC,
|
||||||
|
Message: g.DatabaseE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := make([]string, 0)
|
||||||
|
for rows.Next() {
|
||||||
|
var l string
|
||||||
|
rows.Scan(&l)
|
||||||
|
labels = append(labels, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, g.Result{
|
||||||
|
Status: http.StatusOK,
|
||||||
|
Data: labels,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manage) CreateLabel(c echo.Context) error {
|
||||||
|
service := c.FormValue("service")
|
||||||
|
name := c.FormValue("name")
|
||||||
|
if service == "" || name == "" {
|
||||||
|
return c.JSON(http.StatusBadRequest, g.Result{
|
||||||
|
Status: http.StatusBadRequest,
|
||||||
|
ErrCode: g.ParamEmptyC,
|
||||||
|
Message: g.ParamEmptyE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
query := fmt.Sprintf("insert into labels (service,name) values ('%s','%s')", service, name)
|
||||||
|
_, err := g.DB.Exec(query)
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), g.DUP_KEY_ERR) {
|
||||||
|
return c.JSON(http.StatusConflict, g.Result{
|
||||||
|
Status: http.StatusConflict,
|
||||||
|
ErrCode: g.AlreadyExistC,
|
||||||
|
Message: g.AlreadyExistE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
g.L.Info("access database error", zap.Error(err), zap.String("query", query))
|
||||||
|
return c.JSON(http.StatusInternalServerError, g.Result{
|
||||||
|
Status: http.StatusInternalServerError,
|
||||||
|
ErrCode: g.DatabaseC,
|
||||||
|
Message: g.DatabaseE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, g.Result{
|
||||||
|
Status: http.StatusOK,
|
||||||
|
})
|
||||||
|
}
|
@ -0,0 +1,149 @@
|
|||||||
|
package manage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mafanr/juz/api/manage/audit"
|
||||||
|
"github.com/mafanr/juz/api/manage/strategy"
|
||||||
|
"github.com/mafanr/juz/misc"
|
||||||
|
|
||||||
|
"github.com/mafanr/g"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"github.com/labstack/echo"
|
||||||
|
"github.com/sunface/talent"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Manage struct{}
|
||||||
|
|
||||||
|
func (m *Manage) Start() {
|
||||||
|
registerToEtcd()
|
||||||
|
|
||||||
|
e := echo.New()
|
||||||
|
//api管理
|
||||||
|
e.POST("/manage/api/query", m.QueryAPI, auth)
|
||||||
|
e.POST("/manage/api/count", m.CountAPI, auth)
|
||||||
|
e.POST("/manage/api/define", m.DefineAPI, auth)
|
||||||
|
e.POST("/manage/api/delete", m.DeleteAPI, auth)
|
||||||
|
e.POST("/manage/api/verifyParamRule", m.VerifyParamRule, auth)
|
||||||
|
|
||||||
|
e.POST("/manage/api/release", m.APIRelease, auth)
|
||||||
|
e.POST("/manage/api/batchRelease", m.APIBatchRelease, auth)
|
||||||
|
e.POST("/manage/api/offline", m.APIOffline, auth)
|
||||||
|
|
||||||
|
e.POST("/manage/api/batchStrategy", m.APIBatchStrategy, auth)
|
||||||
|
e.POST("/manage/api/batchDelStrategy", m.APIBatchDelStrategy, auth)
|
||||||
|
|
||||||
|
//策略管理
|
||||||
|
e.POST("/manage/strategy/create", strategy.Create, auth)
|
||||||
|
e.POST("/manage/strategy/update", strategy.Update, auth)
|
||||||
|
e.POST("/manage/strategy/load", strategy.Load, auth)
|
||||||
|
e.POST("/manage/strategy/change", strategy.Change, auth)
|
||||||
|
|
||||||
|
e.POST("/manage/strategy/delete", strategy.Delete, auth)
|
||||||
|
e.POST("/manage/strategy/query", strategy.Query, auth)
|
||||||
|
// e.POST("/manage/strategy/api", strategy.Api, auth)
|
||||||
|
|
||||||
|
// 审计日志
|
||||||
|
e.POST("/manage/auditLog/count", audit.Count, auth)
|
||||||
|
e.POST("/manage/auditLog/load", audit.Load, auth)
|
||||||
|
|
||||||
|
// 标签分组
|
||||||
|
e.POST("/manage/labels/query", m.QueryLabels, auth)
|
||||||
|
e.POST("/manage/labels/create", m.CreateLabel, auth)
|
||||||
|
|
||||||
|
e.Logger.Fatal(e.Start(":" + misc.Conf.Manage.Port))
|
||||||
|
}
|
||||||
|
|
||||||
|
func auth(f echo.HandlerFunc) echo.HandlerFunc {
|
||||||
|
return func(c echo.Context) error {
|
||||||
|
if c.FormValue("admin_token") != misc.Conf.Common.AdminToken {
|
||||||
|
return c.JSON(http.StatusUnauthorized, g.Result{
|
||||||
|
Status: http.StatusUnauthorized,
|
||||||
|
ErrCode: g.ForbiddenC,
|
||||||
|
Message: g.ForbiddenE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return f(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerToEtcd() {
|
||||||
|
g.EtcdCli = g.InitEtcd(misc.Conf.Etcd.Addrs)
|
||||||
|
|
||||||
|
// 保存服务状态到etcd
|
||||||
|
ip := talent.LocalIP()
|
||||||
|
fmt.Println("local ip:", ip)
|
||||||
|
|
||||||
|
host := ip + ":" + misc.Conf.Manage.Port
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
err := g.StoreServer(g.EtcdCli, &g.ServerInfo{g.TFEManage, host, 0})
|
||||||
|
if err != nil {
|
||||||
|
g.L.Error("Store to etcd error", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(time.Second * g.ServiceStoreInterval)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func validUserID(s string) bool {
|
||||||
|
i, err := strconv.Atoi(s)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manage) serviceExist(service string) bool {
|
||||||
|
// 验证service是否存在
|
||||||
|
var temp interface{}
|
||||||
|
query := fmt.Sprintf("select id from service where name ='%s'", service)
|
||||||
|
err := g.DB.Get(&temp, query)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manage) canView(priv string) bool {
|
||||||
|
if priv == g.PRIV_GUEST {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manage) canOperate(priv string) bool {
|
||||||
|
if priv == g.PRIV_ADMIN || priv == g.PRIV_OWNER {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isServiceCreator(userID string, service string) bool {
|
||||||
|
// 验证是否是service创建者
|
||||||
|
var temp interface{}
|
||||||
|
query := fmt.Sprintf("select id from service where name ='%s' and creator='%s'", service, userID)
|
||||||
|
err := g.DB.Get(&temp, query)
|
||||||
|
if err == nil {
|
||||||
|
// 是创建者
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func getServiceByApiName(apiName string) string {
|
||||||
|
return strings.Split(apiName, ".")[0]
|
||||||
|
}
|
@ -0,0 +1,340 @@
|
|||||||
|
package strategy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mafanr/juz/api/manage/audit"
|
||||||
|
"github.com/mafanr/juz/misc"
|
||||||
|
|
||||||
|
"github.com/mafanr/g"
|
||||||
|
|
||||||
|
"github.com/labstack/echo"
|
||||||
|
"github.com/sunface/talent"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func parse(c echo.Context) (*misc.Strategy, int, string) {
|
||||||
|
str := c.FormValue("strategy")
|
||||||
|
if str == "" {
|
||||||
|
return nil, g.ParamEmptyC, g.ParamEmptyE
|
||||||
|
}
|
||||||
|
|
||||||
|
st := &misc.Strategy{}
|
||||||
|
err := json.Unmarshal([]byte(str), &st)
|
||||||
|
if err != nil {
|
||||||
|
return nil, g.ParamInvalidC, g.ParamInvalidE
|
||||||
|
}
|
||||||
|
|
||||||
|
if st.Name == "" || st.Service == "" {
|
||||||
|
return nil, g.ParamInvalidC, g.ParamInvalidE
|
||||||
|
}
|
||||||
|
return st, 0, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func Create(c echo.Context) error {
|
||||||
|
st, ecode, emsg := parse(c)
|
||||||
|
if ecode != 0 {
|
||||||
|
return c.JSON(http.StatusBadRequest, g.Result{
|
||||||
|
Status: http.StatusBadRequest,
|
||||||
|
ErrCode: ecode,
|
||||||
|
Message: emsg,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if !canOperate(c, st.Service) {
|
||||||
|
return c.JSON(http.StatusForbidden, g.Result{
|
||||||
|
Status: http.StatusForbidden,
|
||||||
|
ErrCode: g.ForbiddenC,
|
||||||
|
Message: g.ForbiddenE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
query := fmt.Sprintf("insert into strategy (name,service,type,sub_type,content,create_date) values ('%s','%s','%d','%d','%s','%s')",
|
||||||
|
st.Name, st.Service, st.Type, st.SubType, st.Content, talent.Time2StringSecond(time.Now()))
|
||||||
|
res, err := g.DB.Exec(query)
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), g.DUP_KEY_ERR) {
|
||||||
|
return c.JSON(http.StatusConflict, g.Result{
|
||||||
|
Status: http.StatusConflict,
|
||||||
|
ErrCode: g.AlreadyExistC,
|
||||||
|
Message: "Strategy name already exist",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
g.L.Info("access database error", zap.Error(err), zap.String("query", query))
|
||||||
|
return c.JSON(http.StatusInternalServerError, g.Result{
|
||||||
|
Status: http.StatusInternalServerError,
|
||||||
|
ErrCode: g.DatabaseC,
|
||||||
|
Message: g.DatabaseE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
id, _ := res.LastInsertId()
|
||||||
|
audit.Log(c.FormValue("username"), st.Service, audit.TypeStrategy, fmt.Sprintf("%d:%s", id, st.Name), audit.OpCreate, c.FormValue("strategy"), "")
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, g.Result{
|
||||||
|
Status: http.StatusOK,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Update(c echo.Context) error {
|
||||||
|
st, ecode, emsg := parse(c)
|
||||||
|
if ecode != 0 {
|
||||||
|
return c.JSON(http.StatusBadRequest, g.Result{
|
||||||
|
Status: http.StatusBadRequest,
|
||||||
|
ErrCode: ecode,
|
||||||
|
Message: emsg,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if !canOperate(c, st.Service) {
|
||||||
|
return c.JSON(http.StatusForbidden, g.Result{
|
||||||
|
Status: http.StatusForbidden,
|
||||||
|
ErrCode: g.ForbiddenC,
|
||||||
|
Message: g.ForbiddenE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
query := fmt.Sprintf("update strategy set name='%s',sub_type='%d',content='%s' where id ='%d'",
|
||||||
|
st.Name, st.SubType, st.Content, st.ID)
|
||||||
|
_, err := g.DB.Exec(query)
|
||||||
|
if err != nil {
|
||||||
|
g.L.Info("access database error", zap.Error(err), zap.String("query", query))
|
||||||
|
return c.JSON(http.StatusInternalServerError, g.Result{
|
||||||
|
Status: http.StatusInternalServerError,
|
||||||
|
ErrCode: g.DatabaseC,
|
||||||
|
Message: g.DatabaseE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
audit.Log(c.FormValue("username"), st.Service, audit.TypeStrategy, fmt.Sprintf("%d:%s", st.ID, st.Name), audit.OpEdit, c.FormValue("strategy"), "")
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, g.Result{
|
||||||
|
Status: http.StatusOK,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Load(c echo.Context) error {
|
||||||
|
service := c.FormValue("service")
|
||||||
|
tp := c.FormValue("type")
|
||||||
|
if service == "" {
|
||||||
|
return c.JSON(http.StatusBadRequest, g.Result{
|
||||||
|
Status: http.StatusBadRequest,
|
||||||
|
ErrCode: g.ParamEmptyC,
|
||||||
|
Message: g.ParamEmptyE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ss := make([]*misc.Strategy, 0)
|
||||||
|
var query string
|
||||||
|
if tp == "0" {
|
||||||
|
query = fmt.Sprintf("select * from strategy where service ='%s' order by modify_date desc", service)
|
||||||
|
} else {
|
||||||
|
query = fmt.Sprintf("select * from strategy where service ='%s' and type='%s' order by modify_date desc", service, tp)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := g.DB.Select(&ss, query)
|
||||||
|
if err != nil {
|
||||||
|
g.L.Info("access database error", zap.Error(err), zap.String("query", query))
|
||||||
|
return c.JSON(http.StatusInternalServerError, g.Result{
|
||||||
|
Status: http.StatusInternalServerError,
|
||||||
|
ErrCode: g.DatabaseC,
|
||||||
|
Message: g.DatabaseE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, g.Result{
|
||||||
|
Status: http.StatusOK,
|
||||||
|
Data: ss,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Delete(c echo.Context) error {
|
||||||
|
service := c.FormValue("service")
|
||||||
|
id := c.FormValue("id")
|
||||||
|
name := c.FormValue("name")
|
||||||
|
tp, _ := strconv.Atoi(c.FormValue("type"))
|
||||||
|
if service == "" || id == "" {
|
||||||
|
return c.JSON(http.StatusBadRequest, g.Result{
|
||||||
|
Status: http.StatusBadRequest,
|
||||||
|
ErrCode: g.ParamEmptyC,
|
||||||
|
Message: g.ParamEmptyE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if !canOperate(c, service) {
|
||||||
|
return c.JSON(http.StatusForbidden, g.Result{
|
||||||
|
Status: http.StatusForbidden,
|
||||||
|
ErrCode: g.ForbiddenC,
|
||||||
|
Message: g.ForbiddenE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var query string
|
||||||
|
switch tp {
|
||||||
|
case misc.STRATEGY_BWLIST:
|
||||||
|
query = fmt.Sprintf("update api_define set bw_strategy='%d' where bw_strategy='%s'", 0, id)
|
||||||
|
case misc.STRATEGY_RETRY:
|
||||||
|
query = fmt.Sprintf("update api_define set retry_strategy='%d' where retry_strategy='%s'", 0, id)
|
||||||
|
case misc.STRATEGY_TRAFFIC:
|
||||||
|
query = fmt.Sprintf("update api_define set traffic_strategy='%d' where traffic_strategy='%s'", 0, id)
|
||||||
|
default:
|
||||||
|
return c.JSON(http.StatusBadRequest, g.Result{
|
||||||
|
Status: http.StatusBadRequest,
|
||||||
|
ErrCode: g.ParamInvalidC,
|
||||||
|
Message: g.ParamInvalidE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_, err := g.DB.Exec(query)
|
||||||
|
if err != nil {
|
||||||
|
g.L.Info("access database error", zap.Error(err), zap.String("query", query))
|
||||||
|
return c.JSON(http.StatusInternalServerError, g.Result{
|
||||||
|
Status: http.StatusInternalServerError,
|
||||||
|
ErrCode: g.DatabaseC,
|
||||||
|
Message: g.DatabaseE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tp {
|
||||||
|
case misc.STRATEGY_BWLIST:
|
||||||
|
query = fmt.Sprintf("update api_release set bw_strategy='%d' where bw_strategy='%s'", 0, id)
|
||||||
|
case misc.STRATEGY_RETRY:
|
||||||
|
query = fmt.Sprintf("update api_release set retry_strategy='%d' where retry_strategy='%s'", 0, id)
|
||||||
|
case misc.STRATEGY_TRAFFIC:
|
||||||
|
query = fmt.Sprintf("update api_release set traffic_strategy='%d' where traffic_strategy='%s'", 0, id)
|
||||||
|
}
|
||||||
|
_, err = g.DB.Exec(query)
|
||||||
|
if err != nil {
|
||||||
|
g.L.Info("access database error", zap.Error(err), zap.String("query", query))
|
||||||
|
return c.JSON(http.StatusInternalServerError, g.Result{
|
||||||
|
Status: http.StatusInternalServerError,
|
||||||
|
ErrCode: g.DatabaseC,
|
||||||
|
Message: g.DatabaseE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
query = fmt.Sprintf("delete from strategy where id='%s'", id)
|
||||||
|
_, err = g.DB.Exec(query)
|
||||||
|
if err != nil {
|
||||||
|
g.L.Info("access database error", zap.Error(err), zap.String("query", query))
|
||||||
|
return c.JSON(http.StatusInternalServerError, g.Result{
|
||||||
|
Status: http.StatusInternalServerError,
|
||||||
|
ErrCode: g.DatabaseC,
|
||||||
|
Message: g.DatabaseE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
audit.Log(c.FormValue("username"), service, audit.TypeStrategy, fmt.Sprintf("%s:%s", id, name), audit.OpDelete, "", "")
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, g.Result{
|
||||||
|
Status: http.StatusOK,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Change(c echo.Context) error {
|
||||||
|
status, _ := strconv.Atoi(c.FormValue("status"))
|
||||||
|
id := c.FormValue("id")
|
||||||
|
if id == "" {
|
||||||
|
return c.JSON(http.StatusBadRequest, g.Result{
|
||||||
|
Status: http.StatusBadRequest,
|
||||||
|
ErrCode: g.ParamEmptyC,
|
||||||
|
Message: g.ParamEmptyE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
newS := 0
|
||||||
|
op := 0
|
||||||
|
switch status {
|
||||||
|
case misc.STRATEGY_ON:
|
||||||
|
newS = misc.STRATEGY_OFF
|
||||||
|
op = audit.OpOffline
|
||||||
|
case misc.STRATEGY_OFF:
|
||||||
|
newS = misc.STRATEGY_ON
|
||||||
|
op = audit.OpRelease
|
||||||
|
default:
|
||||||
|
return c.JSON(http.StatusBadRequest, g.Result{
|
||||||
|
Status: http.StatusBadRequest,
|
||||||
|
ErrCode: g.ParamInvalidC,
|
||||||
|
Message: g.ParamInvalidE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
query := fmt.Sprintf("update strategy set status='%d' where id='%s'", newS, id)
|
||||||
|
_, err := g.DB.Exec(query)
|
||||||
|
if err != nil {
|
||||||
|
g.L.Info("access database error", zap.Error(err), zap.String("query", query))
|
||||||
|
return c.JSON(http.StatusInternalServerError, g.Result{
|
||||||
|
Status: http.StatusInternalServerError,
|
||||||
|
ErrCode: g.DatabaseC,
|
||||||
|
Message: g.DatabaseE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询发布的strategy内容
|
||||||
|
s := misc.Strategy{}
|
||||||
|
g.DB.Get(&s, fmt.Sprintf("select * from strategy where id='%s'", id))
|
||||||
|
d, _ := json.Marshal(s)
|
||||||
|
audit.Log(c.FormValue("username"), s.Service, audit.TypeStrategy, fmt.Sprintf("%s:%s", id, s.Name), op, talent.Bytes2String(d), "")
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, g.Result{
|
||||||
|
Status: http.StatusOK,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Query(c echo.Context) error {
|
||||||
|
id := c.FormValue("id")
|
||||||
|
if id == "" {
|
||||||
|
return c.JSON(http.StatusBadRequest, g.Result{
|
||||||
|
Status: http.StatusBadRequest,
|
||||||
|
ErrCode: g.ParamEmptyC,
|
||||||
|
Message: g.ParamEmptyE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
s := misc.Strategy{}
|
||||||
|
g.DB.Get(&s, fmt.Sprintf("select * from strategy where id='%s'", id))
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, g.Result{
|
||||||
|
Status: http.StatusOK,
|
||||||
|
Data: s,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func canOperate(c echo.Context, service string) bool {
|
||||||
|
role := c.FormValue("app_priv")
|
||||||
|
userID := c.FormValue("username")
|
||||||
|
|
||||||
|
if role == g.ROLE_NORMAL {
|
||||||
|
// 验证是否是service创建者
|
||||||
|
var temp interface{}
|
||||||
|
query := fmt.Sprintf("select id from service where name ='%s' and creator='%s'", service, userID)
|
||||||
|
err := g.DB.Get(&temp, query)
|
||||||
|
if err == nil {
|
||||||
|
// 是创建者
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证是否是管理员
|
||||||
|
query = fmt.Sprintf("select privilege from privilege where user_id='%s' and service='%s'", userID, service)
|
||||||
|
rows, err := g.DB.Query(query)
|
||||||
|
if !rows.Next() {
|
||||||
|
// 不存在该用户的权限
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var priv int
|
||||||
|
rows.Scan(&priv)
|
||||||
|
if priv == misc.PRIVILEGE_ADMIN {
|
||||||
|
// 是管理员
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
} else { //是应用级别的管理员
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
# 集成测试
|
@ -0,0 +1,34 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/valyala/fasthttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
runtime.GOMAXPROCS(1)
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
h := requestHandler
|
||||||
|
|
||||||
|
if err := fasthttp.ListenAndServe("localhost:10001", h); err != nil {
|
||||||
|
log.Fatalf("Error in ListenAndServe: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestHandler(ctx *fasthttp.RequestCtx) {
|
||||||
|
ctx.SetContentType("text/plain; charset=utf8")
|
||||||
|
|
||||||
|
// Set arbitrary headers
|
||||||
|
ctx.Response.Header.Set("X-My-Header", "my-header-value")
|
||||||
|
|
||||||
|
// Set cookies
|
||||||
|
var c fasthttp.Cookie
|
||||||
|
c.SetKey("cookie-name")
|
||||||
|
c.SetValue("cookie-value")
|
||||||
|
ctx.Response.Header.SetCookie(&c)
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
// Copyright © 2018 NAME HERE <EMAIL ADDRESS>
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/mafanr/juz/misc"
|
||||||
|
|
||||||
|
"github.com/mafanr/juz/api"
|
||||||
|
|
||||||
|
"github.com/mafanr/g"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// apiCmd represents the api command
|
||||||
|
var apiCmd = &cobra.Command{
|
||||||
|
Use: "api",
|
||||||
|
Short: "api gateway",
|
||||||
|
Long: ``,
|
||||||
|
// Uncomment the following line if your bare application
|
||||||
|
// has an action associated with it:
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
misc.InitConfig("juz.conf")
|
||||||
|
misc.Conf.Common.LogLevel = strings.ToLower(misc.Conf.Common.LogLevel)
|
||||||
|
g.InitLogger()
|
||||||
|
g.L.Info("Application version", zap.String("version", misc.Conf.Common.Version))
|
||||||
|
|
||||||
|
p := &api.ApiServer{}
|
||||||
|
p.Start()
|
||||||
|
|
||||||
|
// 等待服务器停止信号
|
||||||
|
chSig := make(chan os.Signal)
|
||||||
|
signal.Notify(chSig, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
sig := <-chSig
|
||||||
|
g.L.Info("juz received signal", zap.Any("signal", sig))
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(apiCmd)
|
||||||
|
|
||||||
|
// Here you will define your flags and configuration settings.
|
||||||
|
|
||||||
|
// Cobra supports Persistent Flags which will work for this command
|
||||||
|
// and all subcommands, e.g.:
|
||||||
|
// apiCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||||
|
|
||||||
|
// Cobra supports local flags which will only run when this command
|
||||||
|
// is called directly, e.g.:
|
||||||
|
// apiCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
// Copyright © 2018 NAME HERE <EMAIL ADDRESS>
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cfgFile string
|
||||||
|
|
||||||
|
// rootCmd represents the base command when called without any subcommands
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "tfe",
|
||||||
|
Short: "Api网关",
|
||||||
|
Long: ``,
|
||||||
|
// Uncomment the following line if your bare application
|
||||||
|
// has an action associated with it:
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||||
|
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||||
|
func Execute() {
|
||||||
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Here you will define your flags and configuration settings.
|
||||||
|
// Cobra supports persistent flags, which, if defined here,
|
||||||
|
// will be global for your application.
|
||||||
|
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.tfe.yaml)")
|
||||||
|
|
||||||
|
// Cobra also supports local flags, which will only run
|
||||||
|
// when this action is called directly.
|
||||||
|
// rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
// Copyright © 2018 NAME HERE <EMAIL ADDRESS>
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/mafanr/juz/misc"
|
||||||
|
"github.com/mafanr/juz/traffic"
|
||||||
|
|
||||||
|
"github.com/mafanr/g"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// trafficCmd represents the traffic command
|
||||||
|
var trafficCmd = &cobra.Command{
|
||||||
|
Use: "traffic",
|
||||||
|
Short: "traffic control center",
|
||||||
|
Long: ``,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
misc.InitConfig("juz.conf")
|
||||||
|
misc.Conf.Common.LogLevel = strings.ToLower(misc.Conf.Common.LogLevel)
|
||||||
|
g.InitLogger()
|
||||||
|
g.L.Info("Application version", zap.String("version", misc.Conf.Common.Version))
|
||||||
|
|
||||||
|
p := &traffic.Traffic{}
|
||||||
|
p.Start()
|
||||||
|
|
||||||
|
// 等待服务器停止信号
|
||||||
|
chSig := make(chan os.Signal)
|
||||||
|
signal.Notify(chSig, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
sig := <-chSig
|
||||||
|
g.L.Info("juz received signal", zap.Any("signal", sig))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(trafficCmd)
|
||||||
|
|
||||||
|
// Here you will define your flags and configuration settings.
|
||||||
|
|
||||||
|
// Cobra supports Persistent Flags which will work for this command
|
||||||
|
// and all subcommands, e.g.:
|
||||||
|
// trafficCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||||
|
|
||||||
|
// Cobra supports local flags which will only run when this command
|
||||||
|
// is called directly, e.g.:
|
||||||
|
// trafficCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
common:
|
||||||
|
version: 0.0.1
|
||||||
|
loglevel: warn
|
||||||
|
admintoken: "juz.io"
|
||||||
|
|
||||||
|
api:
|
||||||
|
port: "8081"
|
||||||
|
serverid: 1
|
||||||
|
|
||||||
|
manage:
|
||||||
|
port: "8089"
|
||||||
|
|
||||||
|
mysql:
|
||||||
|
# addr: 10.7.13.48
|
||||||
|
# port: 8066
|
||||||
|
# database: tfe
|
||||||
|
# acc: tfe
|
||||||
|
# pw: EUnt7sbiRzYzpRLz8S21
|
||||||
|
addr: localhost
|
||||||
|
port: 3306
|
||||||
|
database: mafanr_juz
|
||||||
|
acc: root
|
||||||
|
pw:
|
||||||
|
|
||||||
|
etcd:
|
||||||
|
addrs:
|
||||||
|
- "localhost:2379"
|
||||||
|
# - "10.7.24.191:2379"
|
||||||
|
# - "10.7.24.191:2379"
|
||||||
|
|
||||||
|
traffic:
|
||||||
|
host: "127.0.0.1"
|
||||||
|
port: "8088"
|
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright © 2018 NAME HERE <EMAIL ADDRESS>
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/mafanr/juz/cmd"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cmd.Execute()
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
// Copyright © 2018 Sunface <CTO@188.com>
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package misc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Common struct {
|
||||||
|
Version string
|
||||||
|
LogLevel string
|
||||||
|
AdminToken string
|
||||||
|
}
|
||||||
|
Api struct {
|
||||||
|
Port string
|
||||||
|
ServerID int64
|
||||||
|
}
|
||||||
|
Manage struct {
|
||||||
|
Port string
|
||||||
|
}
|
||||||
|
Mysql struct {
|
||||||
|
Addr string
|
||||||
|
Port string
|
||||||
|
Database string
|
||||||
|
Acc string
|
||||||
|
Pw string
|
||||||
|
}
|
||||||
|
Etcd struct {
|
||||||
|
Addrs []string
|
||||||
|
}
|
||||||
|
|
||||||
|
Traffic struct {
|
||||||
|
Host string
|
||||||
|
Port string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var Conf *Config
|
||||||
|
|
||||||
|
func InitConfig(path string) {
|
||||||
|
conf := &Config{}
|
||||||
|
data, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("read config error :", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = yaml.Unmarshal(data, &conf)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("yaml decode error :", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Conf = conf
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
package misc
|
||||||
|
|
||||||
|
const TEST_API_NAME = "devops.test.get.v1"
|
||||||
|
|
||||||
|
// 默认请求策略
|
||||||
|
const (
|
||||||
|
REQ_TIMEOUT = 15
|
||||||
|
RETRY_TIMES = 0
|
||||||
|
RETRY_INTERVAL = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PRIVILEGE_SUPER_ADMIN = 0
|
||||||
|
PRIVILEGE_ADMIN = 1
|
||||||
|
PRIVILEGE_NORMAL = 2
|
||||||
|
PRIVILEGE_VIEWER = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// 通用的
|
||||||
|
const (
|
||||||
|
ON = 1
|
||||||
|
OFF = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// 以下值绝对不可更改,前端也在使用
|
||||||
|
const (
|
||||||
|
STRATEGY_NO_LIMIT = 0
|
||||||
|
|
||||||
|
BW_OFF = 0
|
||||||
|
BLACK_LIST = 1
|
||||||
|
WHITE_LIST = 2
|
||||||
|
|
||||||
|
IP_TYPE = 1
|
||||||
|
PARAM_TYPE = 2
|
||||||
|
|
||||||
|
TRAFFIC_ON = 1
|
||||||
|
TRAFFIC_OFF = 0
|
||||||
|
|
||||||
|
PARAM_VERIFY_ON = 1
|
||||||
|
PARAM_VERIFY_OFF = 0
|
||||||
|
|
||||||
|
API_RELEASED = 1
|
||||||
|
API_OFFLINE = 0
|
||||||
|
|
||||||
|
STRATEGY_ALL = -1
|
||||||
|
STRATEGY_EMPTY = 0
|
||||||
|
STRATEGY_BWLIST = 1
|
||||||
|
STRATEGY_RETRY = 2
|
||||||
|
STRATEGY_TRAFFIC = 3
|
||||||
|
|
||||||
|
STRATEGY_ON = 1
|
||||||
|
STRATEGY_OFF = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// redis后缀
|
||||||
|
const (
|
||||||
|
TRAFFIC_CONCURRENT = ".cr"
|
||||||
|
)
|
Loading…
Reference in new issue