package admin import ( "net/http" "strconv" "github.com/Wei-Shaw/sub2api/internal/handler/dto" "github.com/Wei-Shaw/sub2api/internal/pkg/response" "github.com/Wei-Shaw/sub2api/internal/pkg/windsurf" "github.com/Wei-Shaw/sub2api/internal/service" "github.com/gin-gonic/gin" ) type WindsurfHandler struct { authService *service.WindsurfAuthService lsService *service.WindsurfLSService probeService *service.WindsurfProbeService } func NewWindsurfHandler( authService *service.WindsurfAuthService, lsService *service.WindsurfLSService, probeService *service.WindsurfProbeService, ) *WindsurfHandler { return &WindsurfHandler{ authService: authService, lsService: lsService, probeService: probeService, } } func (h *WindsurfHandler) Login(c *gin.Context) { var req dto.WindsurfLoginRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } concurrency := 1 if req.Concurrency != nil && *req.Concurrency > 0 { concurrency = *req.Concurrency } priority := 0 if req.Priority != nil { priority = *req.Priority } probeAfter := false if req.ProbeAfter != nil { probeAfter = *req.ProbeAfter } input := &service.WindsurfLoginInput{ Email: req.Email, Password: req.Password, Name: req.Name, Notes: req.Notes, ProxyID: req.ProxyID, GroupIDs: req.GroupIDs, Concurrency: concurrency, Priority: priority, ProbeAfter: probeAfter, LSInstanceID: req.LSInstanceID, } output, err := h.authService.Login(c.Request.Context(), input) if err != nil { response.Error(c, http.StatusInternalServerError, err.Error()) return } response.Success(c, dto.WindsurfLoginResponse{ AccountID: output.AccountID, Platform: "windsurf", Type: "windsurf-session", Email: output.Email, Tier: output.Tier, AuthMethod: output.AuthMethod, APIKeyPresent: output.APIKeyPresent, RefreshTokenPresent: output.RefreshTokenPresent, }) } func (h *WindsurfHandler) BatchLogin(c *gin.Context) { var req dto.WindsurfBatchLoginRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } concurrency := 1 if req.Concurrency != nil && *req.Concurrency > 0 { concurrency = *req.Concurrency } priority := 0 if req.Priority != nil { priority = *req.Priority } probeAfter := false if req.ProbeAfter != nil { probeAfter = *req.ProbeAfter } results, err := h.authService.BatchLogin( c.Request.Context(), req.Items, req.ProxyID, req.GroupIDs, concurrency, priority, probeAfter, ) if err != nil { response.Error(c, http.StatusInternalServerError, err.Error()) return } successCount := 0 failCount := 0 batchResults := make([]dto.WindsurfBatchLoginResult, 0, len(results)) for _, r := range results { br := dto.WindsurfBatchLoginResult{ Email: r.Email, Success: r.Success, Error: r.Error, } if r.Success && r.Output != nil { successCount++ br.Account = &dto.WindsurfLoginResponse{ AccountID: r.Output.AccountID, Platform: "windsurf", Type: "windsurf-session", Email: r.Output.Email, Tier: r.Output.Tier, AuthMethod: r.Output.AuthMethod, APIKeyPresent: r.Output.APIKeyPresent, RefreshTokenPresent: r.Output.RefreshTokenPresent, } } else { failCount++ } batchResults = append(batchResults, br) } response.Success(c, dto.WindsurfBatchLoginResponse{ Results: batchResults, Total: len(results), SuccessCount: successCount, FailCount: failCount, }) } func (h *WindsurfHandler) RefreshToken(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { response.BadRequest(c, "invalid account id") return } if err := h.authService.RefreshToken(c.Request.Context(), id); err != nil { response.Error(c, http.StatusInternalServerError, err.Error()) return } response.Success(c, dto.WindsurfRefreshTokenResponse{ Refreshed: true, }) } func (h *WindsurfHandler) BatchRefreshTokens(c *gin.Context) { var req dto.WindsurfBatchIDsRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } successCount := 0 failCount := 0 for _, id := range req.AccountIDs { if err := h.authService.RefreshToken(c.Request.Context(), id); err != nil { failCount++ } else { successCount++ } } response.Success(c, gin.H{ "total": len(req.AccountIDs), "success_count": successCount, "fail_count": failCount, }) } func (h *WindsurfHandler) GetLSStatus(c *gin.Context) { if h.lsService == nil { response.Success(c, dto.WindsurfLSStatusResponse{ Mode: "disabled", Healthy: false, }) return } status := h.lsService.Status() resp := dto.WindsurfLSStatusResponse{ Mode: status.Mode, Healthy: status.Healthy, Instances: status.Instances, Endpoint: status.Endpoint, } if dc, ok := h.lsService.Connector().(*windsurf.DockerDiscoveryConnector); ok { for _, inst := range dc.InstanceStatuses() { resp.Details = append(resp.Details, dto.WindsurfLSInstanceDetail{ ContainerID: inst.ContainerID, ContainerName: inst.ContainerName, Host: inst.Host, Port: inst.Port, Healthy: inst.Healthy, DiscoveredAt: inst.DiscoveredAt.Format("2006-01-02T15:04:05Z07:00"), LastProbeAt: inst.LastProbeAt.Format("2006-01-02T15:04:05Z07:00"), LastProbeErr: inst.LastProbeErr, }) } } response.Success(c, resp) } func (h *WindsurfHandler) ListModels(c *gin.Context) { models := windsurf.ListModelsOpenAI() response.Success(c, models) } func (h *WindsurfHandler) Probe(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { response.BadRequest(c, "invalid account id") return } result, err := h.probeService.ProbeAccount(c.Request.Context(), id) if err != nil { response.Error(c, http.StatusInternalServerError, err.Error()) return } response.Success(c, result) } func (h *WindsurfHandler) BatchProbe(c *gin.Context) { var req dto.WindsurfBatchIDsRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } type probeResult struct { AccountID int64 `json:"account_id"` Success bool `json:"success"` Tier string `json:"tier,omitempty"` Error string `json:"error,omitempty"` } results := make([]probeResult, 0, len(req.AccountIDs)) successCount := 0 failCount := 0 for _, id := range req.AccountIDs { r, err := h.probeService.ProbeAccount(c.Request.Context(), id) if err != nil { failCount++ results = append(results, probeResult{AccountID: id, Error: err.Error()}) continue } if r.Error != "" { failCount++ results = append(results, probeResult{AccountID: id, Error: r.Error}) continue } successCount++ results = append(results, probeResult{AccountID: id, Success: true, Tier: r.Tier}) } response.Success(c, gin.H{ "results": results, "total": len(req.AccountIDs), "success_count": successCount, "fail_count": failCount, }) } func (h *WindsurfHandler) GetRuntime(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { response.BadRequest(c, "invalid account id") return } result, err := h.probeService.GetRuntime(c.Request.Context(), id) if err != nil { response.Error(c, http.StatusInternalServerError, err.Error()) return } response.Success(c, result) }