5b4774e9c1
- 新增完整的Go客户端库实现,支持邮件服务器API的各种操作 - 实现账户管理、签名管理、邮件发送、审计、配额、通道等功能模块 - 提供ServiceAuth和AppAuth两种认证模式的客户端 - 添加详细的README文档,包含安装指南和使用示例 - 配置.gitignore文件以忽略构建产物和开发工具配置 - 支持分页查询、错误处理和客户端选项配置
154 lines
3.6 KiB
Go
154 lines
3.6 KiB
Go
package emailcli
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Client struct {
|
|
baseURL string
|
|
serviceToken string
|
|
appKey string
|
|
appSecret string
|
|
httpClient *http.Client
|
|
}
|
|
|
|
type Option func(*Client)
|
|
|
|
func WithHTTPClient(hc *http.Client) Option {
|
|
return func(c *Client) { c.httpClient = hc }
|
|
}
|
|
|
|
func WithTimeout(d time.Duration) Option {
|
|
return func(c *Client) { c.httpClient.Timeout = d }
|
|
}
|
|
|
|
// NewServiceClient creates a client authenticated with a service token (management APIs).
|
|
func NewServiceClient(baseURL, serviceToken string, opts ...Option) *Client {
|
|
c := &Client{
|
|
baseURL: strings.TrimRight(baseURL, "/"),
|
|
serviceToken: serviceToken,
|
|
httpClient: &http.Client{Timeout: 30 * time.Second},
|
|
}
|
|
for _, o := range opts {
|
|
o(c)
|
|
}
|
|
return c
|
|
}
|
|
|
|
// NewAppClient creates a client authenticated with AppKey/AppSecret (mail sending API).
|
|
func NewAppClient(baseURL, appKey, appSecret string, opts ...Option) *Client {
|
|
c := &Client{
|
|
baseURL: strings.TrimRight(baseURL, "/"),
|
|
appKey: appKey,
|
|
appSecret: appSecret,
|
|
httpClient: &http.Client{Timeout: 30 * time.Second},
|
|
}
|
|
for _, o := range opts {
|
|
o(c)
|
|
}
|
|
return c
|
|
}
|
|
|
|
type APIResponse[T any] struct {
|
|
Code int `json:"code"`
|
|
Message string `json:"message"`
|
|
Data T `json:"data,omitempty"`
|
|
}
|
|
|
|
type APIError struct {
|
|
Code int
|
|
Message string
|
|
}
|
|
|
|
func (e *APIError) Error() string {
|
|
return fmt.Sprintf("api error %d: %s", e.Code, e.Message)
|
|
}
|
|
|
|
func (c *Client) url(path string) string {
|
|
return c.baseURL + path
|
|
}
|
|
|
|
func (c *Client) setAuth(req *http.Request) {
|
|
if c.serviceToken != "" {
|
|
req.Header.Set("Authorization", "Bearer "+c.serviceToken)
|
|
}
|
|
if c.appKey != "" {
|
|
req.Header.Set("X-App-Key", c.appKey)
|
|
req.Header.Set("X-App-Secret", c.appSecret)
|
|
}
|
|
}
|
|
|
|
func doRequest[T any](c *Client, ctx context.Context, method, path string, body interface{}, query url.Values) (T, error) {
|
|
var zero T
|
|
|
|
var bodyReader io.Reader
|
|
if body != nil {
|
|
b, err := json.Marshal(body)
|
|
if err != nil {
|
|
return zero, fmt.Errorf("marshal body: %w", err)
|
|
}
|
|
bodyReader = bytes.NewReader(b)
|
|
}
|
|
|
|
reqURL := c.url(path)
|
|
if len(query) > 0 {
|
|
reqURL += "?" + query.Encode()
|
|
}
|
|
|
|
req, err := http.NewRequestWithContext(ctx, method, reqURL, bodyReader)
|
|
if err != nil {
|
|
return zero, fmt.Errorf("new request: %w", err)
|
|
}
|
|
|
|
if body != nil {
|
|
req.Header.Set("Content-Type", "application/json")
|
|
}
|
|
c.setAuth(req)
|
|
|
|
resp, err := c.httpClient.Do(req)
|
|
if err != nil {
|
|
return zero, fmt.Errorf("do request: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
respBody, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return zero, fmt.Errorf("read response: %w", err)
|
|
}
|
|
|
|
var apiResp APIResponse[T]
|
|
if err := json.Unmarshal(respBody, &apiResp); err != nil {
|
|
return zero, fmt.Errorf("unmarshal response (status %d): %w\nbody: %s", resp.StatusCode, err, string(respBody))
|
|
}
|
|
|
|
if apiResp.Code != 200 {
|
|
return zero, &APIError{Code: apiResp.Code, Message: apiResp.Message}
|
|
}
|
|
|
|
return apiResp.Data, nil
|
|
}
|
|
|
|
func get[T any](c *Client, ctx context.Context, path string, query url.Values) (T, error) {
|
|
return doRequest[T](c, ctx, http.MethodGet, path, nil, query)
|
|
}
|
|
|
|
func post[T any](c *Client, ctx context.Context, path string, body interface{}) (T, error) {
|
|
return doRequest[T](c, ctx, http.MethodPost, path, body, nil)
|
|
}
|
|
|
|
func put[T any](c *Client, ctx context.Context, path string, body interface{}) (T, error) {
|
|
return doRequest[T](c, ctx, http.MethodPut, path, body, nil)
|
|
}
|
|
|
|
func del[T any](c *Client, ctx context.Context, path string) (T, error) {
|
|
return doRequest[T](c, ctx, http.MethodDelete, path, nil, nil)
|
|
}
|