Spaces:
Sleeping
Sleeping
| package handler | |
| import ( | |
| "fmt" | |
| "log" | |
| "net/http" | |
| "strings" | |
| "time" | |
| "github.com/gin-gonic/gin" | |
| "zencoder-2api/internal/database" | |
| "zencoder-2api/internal/model" | |
| "zencoder-2api/internal/service" | |
| ) | |
| type ExternalHandler struct{} | |
| func NewExternalHandler() *ExternalHandler { | |
| return &ExternalHandler{} | |
| } | |
| // ExternalTokenRequest 外部API提交token请求结构 | |
| type ExternalTokenRequest struct { | |
| AccessToken string `json:"access_token"` // OAuth获取的access_token | |
| RefreshToken string `json:"refresh_token"` // OAuth获取的refresh_token | |
| Proxy string `json:"proxy"` // 可选的代理设置 | |
| } | |
| // ExternalTokenResponse 外部API响应结构 | |
| type ExternalTokenResponse struct { | |
| Success bool `json:"success"` | |
| Message string `json:"message"` | |
| Account *model.Account `json:"account,omitempty"` | |
| Error string `json:"error,omitempty"` | |
| } | |
| // SubmitTokens 外部API接口:接收OAuth token信息并生成账号记录 | |
| func (h *ExternalHandler) SubmitTokens(c *gin.Context) { | |
| var req ExternalTokenRequest | |
| if err := c.ShouldBindJSON(&req); err != nil { | |
| c.JSON(http.StatusBadRequest, ExternalTokenResponse{ | |
| Success: false, | |
| Error: "请求格式错误: " + err.Error(), | |
| }) | |
| return | |
| } | |
| // 验证必要字段 | |
| if req.AccessToken == "" && req.RefreshToken == "" { | |
| c.JSON(http.StatusBadRequest, ExternalTokenResponse{ | |
| Success: false, | |
| Error: "必须提供 access_token 或 refresh_token", | |
| }) | |
| return | |
| } | |
| log.Printf("[外部API] 收到token提交请求,access_token长度: %d, refresh_token长度: %d", | |
| len(req.AccessToken), len(req.RefreshToken)) | |
| // 优先使用 access_token,如果同时提供了两个字段 | |
| var masterToken string | |
| if req.AccessToken != "" { | |
| // 直接使用提供的 access_token | |
| masterToken = req.AccessToken | |
| log.Printf("[外部API] 使用提供的 access_token") | |
| } else { | |
| // 使用 refresh_token 获取 access_token | |
| tokenResp, err := service.RefreshAccessToken(req.RefreshToken, req.Proxy) | |
| if err != nil { | |
| c.JSON(http.StatusBadRequest, ExternalTokenResponse{ | |
| Success: false, | |
| Error: "RefreshToken 无效: " + err.Error(), | |
| }) | |
| return | |
| } | |
| masterToken = tokenResp.AccessToken | |
| log.Printf("[外部API] 通过 RefreshToken 获取了 access_token") | |
| } | |
| log.Printf("[外部API] 开始生成账号凭证") | |
| // 生成凭证 | |
| cred, err := service.GenerateCredential(masterToken) | |
| if err != nil { | |
| c.JSON(http.StatusInternalServerError, ExternalTokenResponse{ | |
| Success: false, | |
| Error: fmt.Sprintf("生成失败: %v", err), | |
| }) | |
| return | |
| } | |
| log.Printf("[外部API] 凭证生成成功: ClientID=%s", cred.ClientID) | |
| // 创建账号 | |
| account := model.Account{ | |
| ClientID: cred.ClientID, | |
| ClientSecret: cred.Secret, | |
| Proxy: req.Proxy, | |
| IsActive: true, | |
| Status: "normal", | |
| } | |
| // 使用生成的client_id和client_secret获取token,带重试机制 | |
| // 使用OAuth client credentials方式刷新token,使用 https://fe.zencoder.ai/oauth/token | |
| maxRetries := 3 | |
| retryDelay := 2 * time.Second | |
| var lastErr error | |
| for attempt := 1; attempt <= maxRetries; attempt++ { | |
| log.Printf("[外部API] 尝试获取token,第 %d/%d 次", attempt, maxRetries) | |
| if _, err := service.RefreshToken(&account); err != nil { | |
| lastErr = err | |
| log.Printf("[外部API] 第 %d 次获取token失败: %v", attempt, err) | |
| if attempt < maxRetries { | |
| log.Printf("[外部API] 等待 %v 后重试", retryDelay) | |
| time.Sleep(retryDelay) | |
| continue | |
| } | |
| } else { | |
| log.Printf("[外部API] 第 %d 次获取token成功", attempt) | |
| lastErr = nil | |
| break | |
| } | |
| } | |
| if lastErr != nil { | |
| c.JSON(http.StatusBadRequest, ExternalTokenResponse{ | |
| Success: false, | |
| Error: fmt.Sprintf("认证失败(重试 %d 次后): %v", maxRetries, lastErr), | |
| }) | |
| return | |
| } | |
| // 解析 Token 获取详细信息 | |
| if payload, err := service.ParseJWT(account.AccessToken); err == nil { | |
| account.Email = payload.Email | |
| account.SubscriptionStartDate = service.GetSubscriptionDate(payload) | |
| if payload.Expiration > 0 { | |
| account.TokenExpiry = time.Unix(payload.Expiration, 0) | |
| } | |
| plan := payload.CustomClaims.Plan | |
| if plan != "" { | |
| plan = strings.ToUpper(plan[:1]) + plan[1:] | |
| } | |
| if plan != "" { | |
| account.PlanType = model.PlanType(plan) | |
| } | |
| } | |
| if account.PlanType == "" { | |
| account.PlanType = model.PlanFree | |
| } | |
| // 检查是否已存在 | |
| var existing model.Account | |
| var count int64 | |
| database.GetDB().Model(&model.Account{}).Where("client_id = ?", account.ClientID).Count(&count) | |
| if count > 0 { | |
| // 获取现有账号 | |
| database.GetDB().Where("client_id = ?", account.ClientID).First(&existing) | |
| // 更新现有账号 | |
| existing.AccessToken = account.AccessToken | |
| existing.TokenExpiry = account.TokenExpiry | |
| existing.PlanType = account.PlanType | |
| existing.Email = account.Email | |
| existing.SubscriptionStartDate = account.SubscriptionStartDate | |
| existing.IsActive = true | |
| existing.Status = "normal" // 重新激活 | |
| existing.ClientSecret = account.ClientSecret | |
| if account.Proxy != "" { | |
| existing.Proxy = account.Proxy | |
| } | |
| if err := database.GetDB().Save(&existing).Error; err != nil { | |
| c.JSON(http.StatusInternalServerError, ExternalTokenResponse{ | |
| Success: false, | |
| Error: fmt.Sprintf("更新失败: %v", err), | |
| }) | |
| return | |
| } | |
| log.Printf("[外部API] 账号更新成功: ClientID=%s, Email=%s, Plan=%s", existing.ClientID, existing.Email, existing.PlanType) | |
| c.JSON(http.StatusOK, ExternalTokenResponse{ | |
| Success: true, | |
| Message: "账号更新成功", | |
| Account: &existing, | |
| }) | |
| } else { | |
| // 创建新账号 | |
| if err := database.GetDB().Create(&account).Error; err != nil { | |
| c.JSON(http.StatusInternalServerError, ExternalTokenResponse{ | |
| Success: false, | |
| Error: fmt.Sprintf("创建失败: %v", err), | |
| }) | |
| return | |
| } | |
| log.Printf("[外部API] 新账号创建成功: ClientID=%s, Email=%s, Plan=%s", account.ClientID, account.Email, account.PlanType) | |
| c.JSON(http.StatusCreated, ExternalTokenResponse{ | |
| Success: true, | |
| Message: "账号创建成功", | |
| Account: &account, | |
| }) | |
| } | |
| } |