chore: update dependencies and refactor webhook handling
- Added new dependencies for SQLite support and improved HTTP client functionality in go.mod and go.sum. - Refactored webhook server implementation to utilize a simplified version, enhancing code maintainability. - Updated API client to leverage a generic request method, streamlining API interactions. - Modified configuration to include access token for webhook server, improving security. - Enhanced event handling and request processing in the API client for better performance.
This commit is contained in:
@@ -5,20 +5,19 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"cellbot/pkg/net"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/valyala/fasthttp"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// APIClient Milky API 客户端
|
||||
// 用于调用协议端的 API (POST /api/:api)
|
||||
// 使用 pkg/net.HTTPClient 提供的通用 HTTP 请求功能
|
||||
type APIClient struct {
|
||||
httpClient *net.HTTPClient
|
||||
baseURL string
|
||||
accessToken string
|
||||
httpClient *fasthttp.Client
|
||||
logger *zap.Logger
|
||||
timeout time.Duration
|
||||
retryCount int
|
||||
}
|
||||
|
||||
// NewAPIClient 创建 API 客户端
|
||||
@@ -30,17 +29,17 @@ func NewAPIClient(baseURL, accessToken string, timeout time.Duration, retryCount
|
||||
retryCount = 3
|
||||
}
|
||||
|
||||
config := net.HTTPClientConfig{
|
||||
BaseURL: baseURL,
|
||||
Timeout: timeout,
|
||||
RetryCount: retryCount,
|
||||
}
|
||||
|
||||
return &APIClient{
|
||||
httpClient: net.NewHTTPClient(config, logger.Named("api-client"), nil),
|
||||
baseURL: baseURL,
|
||||
accessToken: accessToken,
|
||||
httpClient: &fasthttp.Client{
|
||||
ReadTimeout: timeout,
|
||||
WriteTimeout: timeout,
|
||||
MaxConnsPerHost: 100,
|
||||
},
|
||||
logger: logger.Named("api-client"),
|
||||
timeout: timeout,
|
||||
retryCount: retryCount,
|
||||
logger: logger.Named("api-client"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,63 +68,26 @@ func (c *APIClient) Call(ctx context.Context, endpoint string, input interface{}
|
||||
zap.String("endpoint", endpoint),
|
||||
zap.String("url", url))
|
||||
|
||||
// 重试机制
|
||||
var lastErr error
|
||||
for i := 0; i <= c.retryCount; i++ {
|
||||
if i > 0 {
|
||||
c.logger.Info("Retrying API call",
|
||||
zap.String("endpoint", endpoint),
|
||||
zap.Int("attempt", i),
|
||||
zap.Int("max", c.retryCount))
|
||||
|
||||
// 指数退避
|
||||
backoff := time.Duration(i) * time.Second
|
||||
select {
|
||||
case <-time.After(backoff):
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := c.doRequest(ctx, url, inputData)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("API call failed after %d retries: %w", c.retryCount, lastErr)
|
||||
return c.doRequest(ctx, url, inputData)
|
||||
}
|
||||
|
||||
// doRequest 执行单次请求
|
||||
func (c *APIClient) doRequest(ctx context.Context, url string, inputData []byte) (*APIResponse, error) {
|
||||
req := fasthttp.AcquireRequest()
|
||||
resp := fasthttp.AcquireResponse()
|
||||
defer fasthttp.ReleaseRequest(req)
|
||||
defer fasthttp.ReleaseResponse(resp)
|
||||
|
||||
// 设置请求
|
||||
req.SetRequestURI(url)
|
||||
req.Header.SetMethod("POST")
|
||||
req.Header.SetContentType("application/json")
|
||||
req.SetBody(inputData)
|
||||
|
||||
// 设置 Authorization 头
|
||||
if c.accessToken != "" {
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.accessToken))
|
||||
// 使用 pkg/net.HTTPClient 的通用请求方法
|
||||
config := net.GenericRequestConfig{
|
||||
URL: url,
|
||||
Method: "POST",
|
||||
Body: inputData,
|
||||
AccessToken: c.accessToken,
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
err := c.httpClient.DoTimeout(req, resp, c.timeout)
|
||||
resp, err := c.httpClient.DoGenericRequest(ctx, config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed: %w", err)
|
||||
}
|
||||
|
||||
// 检查 HTTP 状态码
|
||||
statusCode := resp.StatusCode()
|
||||
switch statusCode {
|
||||
switch resp.StatusCode {
|
||||
case 401:
|
||||
return nil, fmt.Errorf("unauthorized: access token invalid or missing")
|
||||
case 404:
|
||||
@@ -135,12 +97,12 @@ func (c *APIClient) doRequest(ctx context.Context, url string, inputData []byte)
|
||||
case 200:
|
||||
// 继续处理
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected status code: %d", statusCode)
|
||||
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
var apiResp APIResponse
|
||||
if err := sonic.Unmarshal(resp.Body(), &apiResp); err != nil {
|
||||
if err := sonic.Unmarshal(resp.Body, &apiResp); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse response: %w", err)
|
||||
}
|
||||
|
||||
@@ -162,28 +124,14 @@ func (c *APIClient) doRequest(ctx context.Context, url string, inputData []byte)
|
||||
}
|
||||
|
||||
// CallWithoutRetry 调用 API(不重试)
|
||||
// 注意:pkg/net.HTTPClient 的通用请求已经内置了重试机制
|
||||
// 这个方法保留是为了向后兼容,但仍然会使用底层的重试机制
|
||||
func (c *APIClient) CallWithoutRetry(ctx context.Context, endpoint string, input interface{}) (*APIResponse, error) {
|
||||
// 序列化输入参数
|
||||
var inputData []byte
|
||||
var err error
|
||||
|
||||
if input == nil {
|
||||
inputData = []byte("{}")
|
||||
} else {
|
||||
inputData, err = sonic.Marshal(input)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal input: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 构建 URL
|
||||
url := fmt.Sprintf("%s/api/%s", c.baseURL, endpoint)
|
||||
|
||||
return c.doRequest(ctx, url, inputData)
|
||||
return c.Call(ctx, endpoint, input)
|
||||
}
|
||||
|
||||
// Close 关闭客户端
|
||||
func (c *APIClient) Close() error {
|
||||
// fasthttp.Client 不需要显式关闭
|
||||
// pkg/net.HTTPClient 的 fasthttp.Client 不需要显式关闭
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user