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