commit
6c7a2eb6b2
@ -0,0 +1,91 @@ |
||||
package risk |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
|
||||
"github.com/go-resty/resty/v2" |
||||
) |
||||
|
||||
const ( |
||||
writeLogUri = "/v1/metric/counter/write/v2" |
||||
syncUri = "/v1/config/database/sync" |
||||
) |
||||
|
||||
type api struct { |
||||
http *resty.Client |
||||
appId string |
||||
appSecret string |
||||
} |
||||
|
||||
func NewApi(opts clientOptions) *api { |
||||
|
||||
client := resty.New() |
||||
client.SetBaseURL(opts.host) |
||||
client.SetTimeout(opts.timeOut) |
||||
client.SetRetryCount(2) |
||||
client.SetDebug(opts.debug) |
||||
client.SetHeader("Content-Type", "application/json") |
||||
|
||||
return &api{ |
||||
http: client, |
||||
appId: opts.appId, |
||||
appSecret: opts.appSecret, |
||||
} |
||||
} |
||||
|
||||
// SyncDatabase 写入访问日志,访问/v1/metric/counter/write/v2接口
|
||||
func (a *api) WriteLog(ctx context.Context, data *LogData) error { |
||||
|
||||
sign, random, ts := Sign(a.appId, a.appSecret, data) |
||||
|
||||
req := Request{ |
||||
AppID: a.appId, |
||||
Time: int64(ts), |
||||
Data: data, |
||||
Sign: sign, |
||||
Random: random, |
||||
} |
||||
|
||||
res, err := a.http.R(). |
||||
SetBody(req). |
||||
Post(writeLogUri) |
||||
|
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
if res.StatusCode() != 200 { |
||||
return fmt.Errorf("write log has error, status_code: %d, response: %s", res.StatusCode(), res.Body()) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// SyncDatabase 同步数据库数据,访问/v1/config/database/sync接口
|
||||
func (a *api) SyncDatabase(ctx context.Context, data []*SyncData) error { |
||||
|
||||
sign, random, ts := Sign(a.appId, a.appSecret, data) |
||||
|
||||
req := Request{ |
||||
AppID: a.appId, |
||||
Time: int64(ts), |
||||
Data: data, |
||||
Sign: sign, |
||||
Random: random, |
||||
} |
||||
|
||||
res, err := a.http.R(). |
||||
SetBody(req). |
||||
Post(syncUri) |
||||
|
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
if res.StatusCode() != 200 { |
||||
return fmt.Errorf("write log has error, status_code: %d, response: %s", res.StatusCode(), res.Body()) |
||||
} |
||||
|
||||
return nil |
||||
} |
@ -0,0 +1,38 @@ |
||||
package risk |
||||
|
||||
import "context" |
||||
|
||||
type API interface { |
||||
WriteLog(ctx context.Context, data *LogData) error |
||||
SyncDatabase(ctx context.Context, data []*SyncData) error |
||||
} |
||||
|
||||
type Client struct { |
||||
opts clientOptions |
||||
apis API |
||||
} |
||||
|
||||
// NewClient 创建一个客户端
|
||||
func NewClient(opt ...ClientOption) *Client { |
||||
opts := defaultClientOptions |
||||
for _, o := range opt { |
||||
o(&opts) |
||||
} |
||||
|
||||
apis := NewApi(opts) |
||||
|
||||
return &Client{ |
||||
opts: opts, |
||||
apis: apis, |
||||
} |
||||
} |
||||
|
||||
// SyncDatabase 写入访问日志,访问/v1/metric/counter/write/v2接口
|
||||
func (cli *Client) WriteLog(ctx context.Context, data *LogData) error { |
||||
return cli.apis.WriteLog(ctx, data) |
||||
} |
||||
|
||||
// SyncDatabase 同步数据库数据,访问/v1/config/database/sync接口
|
||||
func (cli *Client) SyncDatabase(ctx context.Context, data []*SyncData) error { |
||||
return cli.apis.SyncDatabase(ctx, data) |
||||
} |
@ -0,0 +1,105 @@ |
||||
package risk |
||||
|
||||
import ( |
||||
"context" |
||||
"testing" |
||||
"time" |
||||
) |
||||
|
||||
var client = NewClient( |
||||
WithAppID("01b60cd2aacaf411"), |
||||
WithAppSecret("2D6MDRfFg7EudhW90UXOFrEI9td"), |
||||
WithServerHost("http://risk-sensor.drugeyes.vip:7031/"), |
||||
WithTimeOut(time.Second*30), |
||||
WithDebug(true), |
||||
) |
||||
|
||||
func TestClient_WriteLog(t *testing.T) { |
||||
type args struct { |
||||
ctx context.Context |
||||
data *LogData |
||||
} |
||||
tests := []struct { |
||||
name string |
||||
cli *Client |
||||
args args |
||||
wantErr bool |
||||
}{ |
||||
{ |
||||
name: "普通测试", |
||||
cli: client, |
||||
args: args{ |
||||
ctx: context.Background(), |
||||
data: &LogData{ |
||||
Organize: "测试", |
||||
User: "1", |
||||
Ip: "127.0.0.1", |
||||
UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36", |
||||
PageType: "LIST", |
||||
Value: 12, |
||||
Request: "测试库dd", |
||||
DbCount: []*DBCount{ |
||||
{ |
||||
UniqueKey: "00014", |
||||
DB: "db1", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
wantErr: false, |
||||
}, |
||||
} |
||||
for _, tt := range tests { |
||||
t.Run(tt.name, func(t *testing.T) { |
||||
if err := tt.cli.WriteLog(tt.args.ctx, tt.args.data); (err != nil) != tt.wantErr { |
||||
t.Errorf("Client.WriteLog() error = %v, wantErr %v", err, tt.wantErr) |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func TestClient_SyncDatabase(t *testing.T) { |
||||
type args struct { |
||||
ctx context.Context |
||||
data []*SyncData |
||||
} |
||||
tests := []struct { |
||||
name string |
||||
cli *Client |
||||
args args |
||||
wantErr bool |
||||
}{ |
||||
{ |
||||
name: "普通测试", |
||||
cli: client, |
||||
args: args{ |
||||
ctx: context.Background(), |
||||
data: []*SyncData{ |
||||
{ |
||||
DbKey: "db1", |
||||
DbName: "测试库aa", |
||||
DbCount: 255, |
||||
}, |
||||
{ |
||||
DbKey: "db2", |
||||
DbName: "测试库bb", |
||||
DbCount: 255, |
||||
}, |
||||
{ |
||||
DbKey: "db3", |
||||
DbName: "测试库cc", |
||||
DbCount: 255, |
||||
}, |
||||
}, |
||||
}, |
||||
wantErr: false, |
||||
}, |
||||
} |
||||
for _, tt := range tests { |
||||
t.Run(tt.name, func(t *testing.T) { |
||||
if err := tt.cli.SyncDatabase(tt.args.ctx, tt.args.data); (err != nil) != tt.wantErr { |
||||
t.Errorf("Client.SyncDatabase() error = %v, wantErr %v", err, tt.wantErr) |
||||
} |
||||
}) |
||||
} |
||||
} |
@ -0,0 +1,11 @@ |
||||
module gitea.drugeyes.vip/pharnexbase/risk-sdk-go |
||||
|
||||
go 1.19 |
||||
|
||||
require ( |
||||
github.com/go-resty/resty/v2 v2.7.0 |
||||
github.com/google/uuid v1.3.0 |
||||
github.com/spf13/cast v1.5.0 |
||||
) |
||||
|
||||
require golang.org/x/net v0.0.0-20211029224645-99673261e6eb // indirect |
@ -0,0 +1,19 @@ |
||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= |
||||
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= |
||||
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= |
||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= |
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= |
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= |
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= |
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= |
||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= |
||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= |
||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= |
||||
golang.org/x/net v0.0.0-20211029224645-99673261e6eb h1:pirldcYWx7rx7kE5r+9WsOXPXK0+WH5+uZ7uPmJ44uM= |
||||
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= |
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= |
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= |
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= |
@ -0,0 +1,30 @@ |
||||
package risk |
||||
|
||||
type Request struct { |
||||
AppID string `json:"app_id"` |
||||
Time int64 `json:"time"` |
||||
Data interface{} `json:"data"` |
||||
Random string `json:"random"` |
||||
Sign string `json:"sign"` |
||||
} |
||||
|
||||
type LogData struct { |
||||
Organize string `json:"organize"` |
||||
User string `json:"user"` |
||||
Ip string `json:"ip"` |
||||
UserAgent string `json:"user_agent"` |
||||
PageType string `json:"page_type"` |
||||
Value int64 `json:"value"` |
||||
Request string `json:"request"` |
||||
DbCount []*DBCount `json:"db_count"` |
||||
} |
||||
type DBCount struct { |
||||
DB string `json:"db"` |
||||
UniqueKey string `json:"unique_key"` |
||||
} |
||||
|
||||
type SyncData struct { |
||||
DbKey string `json:"db_key"` |
||||
DbName string `json:"db_name"` |
||||
DbCount int64 `json:"db_count"` |
||||
} |
@ -0,0 +1,53 @@ |
||||
package risk |
||||
|
||||
import "time" |
||||
|
||||
type ClientOption func(c *clientOptions) |
||||
|
||||
var defaultClientOptions = clientOptions{ |
||||
host: "http://risk-monitor-api.drugeyes.vip:7031/", |
||||
timeOut: 30 * time.Second, |
||||
} |
||||
|
||||
type clientOptions struct { |
||||
appId string |
||||
appSecret string |
||||
host string |
||||
timeOut time.Duration |
||||
debug bool |
||||
} |
||||
|
||||
// WithAppID 设置APPID
|
||||
func WithAppID(appId string) ClientOption { |
||||
return func(c *clientOptions) { |
||||
c.appId = appId |
||||
} |
||||
} |
||||
|
||||
// WithAppSecret 设置AppSecret
|
||||
func WithAppSecret(secret string) ClientOption { |
||||
return func(c *clientOptions) { |
||||
c.appSecret = secret |
||||
} |
||||
} |
||||
|
||||
// WithHost 设置服务器域名,例如http://risk-monitor-api.drugeyes.vip:7031/
|
||||
func WithServerHost(host string) ClientOption { |
||||
return func(c *clientOptions) { |
||||
c.host = host |
||||
} |
||||
} |
||||
|
||||
// WithTimeOut 设置请求过期时间
|
||||
func WithTimeOut(timeout time.Duration) ClientOption { |
||||
return func(c *clientOptions) { |
||||
c.timeOut = timeout |
||||
} |
||||
} |
||||
|
||||
// WithDebug 设置debug调试模式,打印请求相关内容
|
||||
func WithDebug(debug bool) ClientOption { |
||||
return func(c *clientOptions) { |
||||
c.debug = debug |
||||
} |
||||
} |
@ -0,0 +1,71 @@ |
||||
package risk |
||||
|
||||
import ( |
||||
"crypto/md5" |
||||
"encoding/hex" |
||||
"encoding/json" |
||||
"strings" |
||||
"time" |
||||
|
||||
"github.com/google/uuid" |
||||
|
||||
"github.com/spf13/cast" |
||||
) |
||||
|
||||
func CheckSign(appID, appKey, random, sign string, ts int, data interface{}) bool { |
||||
if int(time.Now().Unix())-ts > 120 { |
||||
return false |
||||
} |
||||
|
||||
v, _ := json.Marshal(data) |
||||
|
||||
m := md5.New() |
||||
|
||||
sb := strings.Builder{} |
||||
sb.WriteString(appID) |
||||
sb.WriteByte('-') |
||||
sb.WriteString(cast.ToString(ts)) |
||||
sb.WriteByte('-') |
||||
sb.Write(v) |
||||
sb.WriteByte('-') |
||||
sb.WriteString(random) |
||||
sb.WriteByte('-') |
||||
sb.WriteString(appKey) |
||||
|
||||
m.Write([]byte(sb.String())) |
||||
md5str1 := hex.EncodeToString(m.Sum(nil)) |
||||
|
||||
return md5str1 == sign |
||||
} |
||||
|
||||
func Sign(appID, appKey string, data interface{}) (sign string, random string, ts int) { |
||||
|
||||
random = newRandom() |
||||
|
||||
ts = int(time.Now().Unix()) |
||||
|
||||
v, _ := json.Marshal(data) |
||||
|
||||
m := md5.New() |
||||
|
||||
sb := strings.Builder{} |
||||
sb.WriteString(appID) |
||||
sb.WriteByte('-') |
||||
sb.WriteString(cast.ToString(ts)) |
||||
sb.WriteByte('-') |
||||
sb.Write(v) |
||||
sb.WriteByte('-') |
||||
sb.WriteString(random) |
||||
sb.WriteByte('-') |
||||
sb.WriteString(appKey) |
||||
|
||||
m.Write([]byte(sb.String())) |
||||
sign = hex.EncodeToString(m.Sum(nil)) |
||||
return |
||||
} |
||||
|
||||
func newRandom() string { |
||||
uuid := uuid.Must(uuid.NewRandom()) |
||||
random := uuid.String() |
||||
return strings.ReplaceAll(random, "-", "") |
||||
} |
Loading…
Reference in new issue