hi-server/pkg/kutt/kutt.go
shanshanzhong 1d81df6664
Some checks failed
Build docker and publish / build (20.15.1) (push) Has been cancelled
add:添加短链接服务
2026-01-24 00:32:08 -08:00

158 lines
4.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package kutt
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
// Client 是 Kutt API 客户端
// 用于创建和管理短链接
type Client struct {
apiURL string // Kutt API 基础 URL
apiKey string // API 认证密钥
httpClient *http.Client // HTTP 客户端
}
// NewClient 创建一个新的 Kutt 客户端
//
// 参数:
// - apiURL: Kutt API 基础 URL (例如: https://kutt.it/api/v2)
// - apiKey: Kutt API 密钥
//
// 返回:
// - *Client: Kutt 客户端实例
func NewClient(apiURL, apiKey string) *Client {
return &Client{
apiURL: apiURL,
apiKey: apiKey,
httpClient: &http.Client{
Timeout: 10 * time.Second,
},
}
}
// CreateLinkRequest 创建短链接的请求参数
type CreateLinkRequest struct {
Target string `json:"target"` // 目标 URL (必填)
Description string `json:"description,omitempty"` // 链接描述 (可选)
ExpireIn string `json:"expire_in,omitempty"` // 过期时间,例如 "2 days" (可选)
Password string `json:"password,omitempty"` // 访问密码 (可选)
CustomURL string `json:"customurl,omitempty"` // 自定义短链后缀 (可选)
Reuse bool `json:"reuse,omitempty"` // 如果目标 URL 已存在则复用 (可选)
Domain string `json:"domain,omitempty"` // 自定义域名 (可选)
}
// Link 短链接响应结构
type Link struct {
ID string `json:"id"` // 链接 UUID
Address string `json:"address"` // 短链地址后缀
Banned bool `json:"banned"` // 是否被封禁
CreatedAt time.Time `json:"created_at"` // 创建时间
Link string `json:"link"` // 完整短链接 URL
Password bool `json:"password"` // 是否有密码保护
Target string `json:"target"` // 目标 URL
Description string `json:"description"` // 链接描述
UpdatedAt time.Time `json:"updated_at"` // 更新时间
VisitCount int `json:"visit_count"` // 访问次数
}
// ErrorResponse Kutt API 错误响应
type ErrorResponse struct {
Error string `json:"error"`
Message string `json:"message"`
}
// CreateShortLink 创建短链接
//
// 参数:
// - ctx: 上下文
// - req: 创建请求参数
//
// 返回:
// - *Link: 创建的短链接信息
// - error: 错误信息
func (c *Client) CreateShortLink(ctx context.Context, req *CreateLinkRequest) (*Link, error) {
// 序列化请求体
body, err := json.Marshal(req)
if err != nil {
return nil, fmt.Errorf("marshal request failed: %w", err)
}
// 创建 HTTP 请求
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, c.apiURL+"/links", bytes.NewReader(body))
if err != nil {
return nil, fmt.Errorf("create request failed: %w", err)
}
// 设置请求头
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("X-API-KEY", c.apiKey)
// 发送请求
resp, err := c.httpClient.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("send request failed: %w", err)
}
defer resp.Body.Close()
// 读取响应体
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("read response failed: %w", err)
}
// 检查响应状态
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
var errResp ErrorResponse
if err := json.Unmarshal(respBody, &errResp); err == nil && errResp.Error != "" {
return nil, fmt.Errorf("kutt api error: %s - %s", errResp.Error, errResp.Message)
}
return nil, fmt.Errorf("kutt api error: status %d, body: %s", resp.StatusCode, string(respBody))
}
// 解析响应
var link Link
if err := json.Unmarshal(respBody, &link); err != nil {
return nil, fmt.Errorf("unmarshal response failed: %w", err)
}
return &link, nil
}
// CreateInviteShortLink 为邀请码创建短链接
// 这是一个便捷方法,用于生成邀请链接
//
// 参数:
// - ctx: 上下文
// - baseURL: 注册页面基础 URL (例如: https://gethifast.net)
// - inviteCode: 邀请码
// - domain: 短链接域名 (例如: getsapp.net),可为空
//
// 返回:
// - string: 短链接 URL
// - error: 错误信息
func (c *Client) CreateInviteShortLink(ctx context.Context, baseURL, inviteCode, domain string) (string, error) {
// 构建目标 URL - 落地页
// 格式https://gethifast.net/?ic=邀请码
targetURL := fmt.Sprintf("%s?ic=%s", baseURL, inviteCode)
req := &CreateLinkRequest{
Target: targetURL,
Description: fmt.Sprintf("Invite link for code: %s", inviteCode),
Reuse: true, // 如果已存在相同目标 URL 则复用
Domain: domain,
}
link, err := c.CreateShortLink(ctx, req)
if err != nil {
return "", err
}
return link.Link, nil
}