package admin import ( "strconv" "github.com/Wei-Shaw/sub2api/internal/pkg/response" "github.com/Wei-Shaw/sub2api/internal/service" "github.com/gin-gonic/gin" ) // OpenAIOAuthHandler handles OpenAI OAuth-related operations type OpenAIOAuthHandler struct { openaiOAuthService *service.OpenAIOAuthService adminService service.AdminService } // NewOpenAIOAuthHandler creates a new OpenAI OAuth handler func NewOpenAIOAuthHandler(openaiOAuthService *service.OpenAIOAuthService, adminService service.AdminService) *OpenAIOAuthHandler { return &OpenAIOAuthHandler{ openaiOAuthService: openaiOAuthService, adminService: adminService, } } // OpenAIGenerateAuthURLRequest represents the request for generating OpenAI auth URL type OpenAIGenerateAuthURLRequest struct { ProxyID *int64 `json:"proxy_id"` RedirectURI string `json:"redirect_uri"` } // GenerateAuthURL generates OpenAI OAuth authorization URL // POST /api/v1/admin/openai/generate-auth-url func (h *OpenAIOAuthHandler) GenerateAuthURL(c *gin.Context) { var req OpenAIGenerateAuthURLRequest if err := c.ShouldBindJSON(&req); err != nil { // Allow empty body req = OpenAIGenerateAuthURLRequest{} } result, err := h.openaiOAuthService.GenerateAuthURL(c.Request.Context(), req.ProxyID, req.RedirectURI) if err != nil { response.InternalError(c, "Failed to generate auth URL: "+err.Error()) return } response.Success(c, result) } // OpenAIExchangeCodeRequest represents the request for exchanging OpenAI auth code type OpenAIExchangeCodeRequest struct { SessionID string `json:"session_id" binding:"required"` Code string `json:"code" binding:"required"` RedirectURI string `json:"redirect_uri"` ProxyID *int64 `json:"proxy_id"` } // ExchangeCode exchanges OpenAI authorization code for tokens // POST /api/v1/admin/openai/exchange-code func (h *OpenAIOAuthHandler) ExchangeCode(c *gin.Context) { var req OpenAIExchangeCodeRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "Invalid request: "+err.Error()) return } tokenInfo, err := h.openaiOAuthService.ExchangeCode(c.Request.Context(), &service.OpenAIExchangeCodeInput{ SessionID: req.SessionID, Code: req.Code, RedirectURI: req.RedirectURI, ProxyID: req.ProxyID, }) if err != nil { response.BadRequest(c, "Failed to exchange code: "+err.Error()) return } response.Success(c, tokenInfo) } // OpenAIRefreshTokenRequest represents the request for refreshing OpenAI token type OpenAIRefreshTokenRequest struct { RefreshToken string `json:"refresh_token" binding:"required"` ProxyID *int64 `json:"proxy_id"` } // RefreshToken refreshes an OpenAI OAuth token // POST /api/v1/admin/openai/refresh-token func (h *OpenAIOAuthHandler) RefreshToken(c *gin.Context) { var req OpenAIRefreshTokenRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "Invalid request: "+err.Error()) return } var proxyURL string if req.ProxyID != nil { proxy, err := h.adminService.GetProxy(c.Request.Context(), *req.ProxyID) if err == nil && proxy != nil { proxyURL = proxy.URL() } } tokenInfo, err := h.openaiOAuthService.RefreshToken(c.Request.Context(), req.RefreshToken, proxyURL) if err != nil { response.BadRequest(c, "Failed to refresh token: "+err.Error()) return } response.Success(c, tokenInfo) } // RefreshAccountToken refreshes token for a specific OpenAI account // POST /api/v1/admin/openai/accounts/:id/refresh func (h *OpenAIOAuthHandler) RefreshAccountToken(c *gin.Context) { accountID, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { response.BadRequest(c, "Invalid account ID") return } // Get account account, err := h.adminService.GetAccount(c.Request.Context(), accountID) if err != nil { response.NotFound(c, "Account not found") return } // Ensure account is OpenAI platform if !account.IsOpenAI() { response.BadRequest(c, "Account is not an OpenAI account") return } // Only refresh OAuth-based accounts if !account.IsOAuth() { response.BadRequest(c, "Cannot refresh non-OAuth account credentials") return } // Use OpenAI OAuth service to refresh token tokenInfo, err := h.openaiOAuthService.RefreshAccountToken(c.Request.Context(), account) if err != nil { response.InternalError(c, "Failed to refresh credentials: "+err.Error()) return } // Build new credentials from token info newCredentials := h.openaiOAuthService.BuildAccountCredentials(tokenInfo) // Preserve non-token settings from existing credentials for k, v := range account.Credentials { if _, exists := newCredentials[k]; !exists { newCredentials[k] = v } } updatedAccount, err := h.adminService.UpdateAccount(c.Request.Context(), accountID, &service.UpdateAccountInput{ Credentials: newCredentials, }) if err != nil { response.InternalError(c, "Failed to update account credentials: "+err.Error()) return } response.Success(c, updatedAccount) } // CreateAccountFromOAuth creates a new OpenAI OAuth account from token info // POST /api/v1/admin/openai/create-from-oauth func (h *OpenAIOAuthHandler) CreateAccountFromOAuth(c *gin.Context) { var req struct { SessionID string `json:"session_id" binding:"required"` Code string `json:"code" binding:"required"` RedirectURI string `json:"redirect_uri"` ProxyID *int64 `json:"proxy_id"` Name string `json:"name"` Concurrency int `json:"concurrency"` Priority int `json:"priority"` GroupIDs []int64 `json:"group_ids"` } if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "Invalid request: "+err.Error()) return } // Exchange code for tokens tokenInfo, err := h.openaiOAuthService.ExchangeCode(c.Request.Context(), &service.OpenAIExchangeCodeInput{ SessionID: req.SessionID, Code: req.Code, RedirectURI: req.RedirectURI, ProxyID: req.ProxyID, }) if err != nil { response.BadRequest(c, "Failed to exchange code: "+err.Error()) return } // Build credentials from token info credentials := h.openaiOAuthService.BuildAccountCredentials(tokenInfo) // Use email as default name if not provided name := req.Name if name == "" && tokenInfo.Email != "" { name = tokenInfo.Email } if name == "" { name = "OpenAI OAuth Account" } // Create account account, err := h.adminService.CreateAccount(c.Request.Context(), &service.CreateAccountInput{ Name: name, Platform: "openai", Type: "oauth", Credentials: credentials, ProxyID: req.ProxyID, Concurrency: req.Concurrency, Priority: req.Priority, GroupIDs: req.GroupIDs, }) if err != nil { response.InternalError(c, "Failed to create account: "+err.Error()) return } response.Success(c, account) }