Files
sub2api/backend/internal/handler/admin/gdrive_oauth_handler.go
T

149 lines
4.8 KiB
Go

package admin
import (
"log/slog"
"net/http"
"github.com/Wei-Shaw/sub2api/internal/pkg/response"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/gin-gonic/gin"
)
// GDriveOAuthHandler 处理 Google Drive OAuth 授权流程。
type GDriveOAuthHandler struct {
settingService *service.SettingService
gdriveOAuth *service.SoraGDriveOAuthService
gdriveStorage *service.SoraGDriveStorage
}
// NewGDriveOAuthHandler 创建 GDrive OAuth Handler。
func NewGDriveOAuthHandler(settingService *service.SettingService, gdriveOAuth *service.SoraGDriveOAuthService, gdriveStorage *service.SoraGDriveStorage) *GDriveOAuthHandler {
return &GDriveOAuthHandler{
settingService: settingService,
gdriveOAuth: gdriveOAuth,
gdriveStorage: gdriveStorage,
}
}
// StartOAuthRequest 启动 OAuth 授权请求。
type StartOAuthRequest struct {
ClientID string `json:"client_id" binding:"required"`
ClientSecret string `json:"client_secret" binding:"required"`
RedirectURI string `json:"redirect_uri" binding:"required"`
}
// StartOAuth 生成 Google OAuth 授权 URL。
// POST /api/v1/admin/settings/sora-storage/gdrive-oauth/start
func (h *GDriveOAuthHandler) StartOAuth(c *gin.Context) {
if h.gdriveOAuth == nil {
response.Error(c, http.StatusInternalServerError, "GDrive OAuth service not initialized")
return
}
var req StartOAuthRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "Invalid request: "+err.Error())
return
}
authURL, state, err := h.gdriveOAuth.GenerateAuthURL(req.ClientID, req.ClientSecret, req.RedirectURI)
if err != nil {
response.Error(c, http.StatusInternalServerError, "生成授权 URL 失败: "+err.Error())
return
}
response.Success(c, gin.H{
"auth_url": authURL,
"state": state,
})
}
// OAuthCallbackRequest OAuth 回调请求。
type OAuthCallbackRequest struct {
ClientID string `json:"client_id" binding:"required"`
ClientSecret string `json:"client_secret" binding:"required"`
RedirectURI string `json:"redirect_uri" binding:"required"`
Code string `json:"code" binding:"required"`
ProfileID string `json:"profile_id"` // 要保存到的 profile ID(可选)
}
// OAuthCallback 用授权码换取 refresh_token 并保存到 profile。
// POST /api/v1/admin/settings/sora-storage/gdrive-oauth/callback
func (h *GDriveOAuthHandler) OAuthCallback(c *gin.Context) {
if h.gdriveOAuth == nil {
response.Error(c, http.StatusInternalServerError, "GDrive OAuth service not initialized")
return
}
var req OAuthCallbackRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "Invalid request: "+err.Error())
return
}
refreshToken, err := h.gdriveOAuth.ExchangeCode(c.Request.Context(), req.ClientID, req.ClientSecret, req.RedirectURI, req.Code)
if err != nil {
slog.Error("[GDriveOAuth] exchange failed",
"client_id_len", len(req.ClientID),
"client_secret_len", len(req.ClientSecret),
"redirect_uri", req.RedirectURI,
"code_len", len(req.Code),
"error", err,
)
response.Error(c, http.StatusBadRequest, "换取 refresh_token 失败: "+err.Error())
return
}
// 如果指定了 profile_id,自动保存 refresh_token 到 profile
if req.ProfileID != "" {
profiles, err := h.settingService.ListSoraS3Profiles(c.Request.Context())
if err == nil {
for _, p := range profiles.Items {
if p.ProfileID == req.ProfileID {
_, _ = h.settingService.UpdateSoraS3Profile(c.Request.Context(), req.ProfileID, &service.SoraS3Profile{
Name: p.Name,
Provider: p.Provider,
AccessMode: p.AccessMode,
Enabled: p.Enabled,
Endpoint: p.Endpoint,
Region: p.Region,
Bucket: p.Bucket,
AccessKeyID: p.AccessKeyID,
Prefix: p.Prefix,
ForcePathStyle: p.ForcePathStyle,
CDNURL: p.CDNURL,
DefaultStorageQuotaBytes: p.DefaultStorageQuotaBytes,
AuthType: p.AuthType,
ClientID: p.ClientID,
FolderID: p.FolderID,
RefreshToken: refreshToken,
})
break
}
}
}
}
response.Success(c, gin.H{
"refresh_token": refreshToken,
"message": "OAuth 授权成功",
})
}
// TestGDriveStorage 测试 GDrive 存储的完整上传→下载→删除流程。
// POST /api/v1/admin/settings/sora-storage/gdrive-test
func (h *GDriveOAuthHandler) TestGDriveStorage(c *gin.Context) {
if h.gdriveStorage == nil {
response.Error(c, http.StatusInternalServerError, "GDrive storage not initialized")
return
}
result, err := h.gdriveStorage.TestFullCycle(c.Request.Context())
if err != nil {
response.Error(c, http.StatusBadRequest, "GDrive 测试失败: "+err.Error())
return
}
response.Success(c, result)
}