diff --git a/internal/api/connectors.go b/internal/api/connectors.go index 47c300f8..cb19664f 100644 --- a/internal/api/connectors.go +++ b/internal/api/connectors.go @@ -446,7 +446,56 @@ func (s *Server) GetConnector(c echo.Context) error { // DeleteConnector handles deletion of a git provider connection func (s *Server) DeleteConnector(c echo.Context) error { - id := c.Param("id") + idStr := c.Param("id") + id, err := strconv.Atoi(idStr) + if err != nil { + return c.JSON(http.StatusBadRequest, ErrorResponse{ + Error: "Invalid connector ID", + }) + } + + // Validate connector ownership + if _, err := s.validateConnectorOwnership(c, id); err != nil { + return err + } + + // Fetch connector details for webhook removal + var provider, providerURL, patToken string + query := ` + SELECT provider, provider_url, pat_token + FROM integration_tokens + WHERE id = $1 + ` + err = s.db.QueryRow(query, id).Scan(&provider, &providerURL, &patToken) + if err != nil { + if err == sql.ErrNoRows { + return c.JSON(http.StatusNotFound, ErrorResponse{ + Error: "Connector not found", + }) + } + log.Printf("Failed to fetch connector details for deletion: %v", err) + return c.JSON(http.StatusInternalServerError, ErrorResponse{ + Error: "Database error: " + err.Error(), + }) + } + + // Fetch projects to queue webhook removal + repositoryData, err := s.fetchAndCacheRepositoryData(id, false, false) + if err != nil { + log.Printf("Failed to fetch repository data for connector %d during deletion: %v", id, err) + // We log the error but proceed with connector deletion so the user isn't permanently blocked + } else if repositoryData.Error != "" { + log.Printf("Repository access error during deletion of connector %d: %s", id, repositoryData.Error) + // Again, proceed to delete the connector + } else { + // Queue webhook removal jobs for each project + ctx := c.Request().Context() + for _, projectPath := range repositoryData.Projects { + if err := s.jobQueue.QueueWebhookRemovalJob(ctx, id, projectPath, provider, providerURL, patToken, true); err != nil { + log.Printf("Failed to queue webhook removal job for %s: %v", projectPath, err) + } + } + } // Execute the delete query result, err := s.db.Exec(` @@ -455,7 +504,7 @@ func (s *Server) DeleteConnector(c echo.Context) error { `, id) if err != nil { - log.Printf("Failed to delete connector with ID %s: %v", id, err) + log.Printf("Failed to delete connector with ID %d: %v", id, err) return c.JSON(http.StatusInternalServerError, ErrorResponse{ Error: "Database error: " + err.Error(), }) diff --git a/internal/api/server.go b/internal/api/server.go index f43ef901..c8f4ab3e 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -1630,7 +1630,7 @@ func (s *Server) DisableManualTriggerForAllProjects(c echo.Context) error { var queueErrors []string for _, projectPath := range repositoryData.Projects { - err := s.jobQueue.QueueWebhookRemovalJob(ctx, connectorId, projectPath, provider, providerURL, patToken) + err := s.jobQueue.QueueWebhookRemovalJob(ctx, connectorId, projectPath, provider, providerURL, patToken, false) if err != nil { queueErrors = append(queueErrors, fmt.Sprintf("Failed to queue removal job for %s: %v", projectPath, err)) } else { diff --git a/internal/jobqueue/jobqueue.go b/internal/jobqueue/jobqueue.go index ef8575d7..0bda4f4d 100644 --- a/internal/jobqueue/jobqueue.go +++ b/internal/jobqueue/jobqueue.go @@ -153,11 +153,12 @@ type WebhookInstallWorker struct { // WebhookRemovalJobArgs represents the arguments for a webhook removal job type WebhookRemovalJobArgs struct { - ConnectorID int `json:"connector_id"` - ProjectPath string `json:"project_path"` - Provider string `json:"provider"` - BaseURL string `json:"base_url"` - PAT string `json:"pat"` + ConnectorID int `json:"connector_id"` + ProjectPath string `json:"project_path"` + Provider string `json:"provider"` + BaseURL string `json:"base_url"` + PAT string `json:"pat"` + SkipRegistryUpdate bool `json:"skip_registry_update"` } // Kind returns the job kind for River @@ -1413,11 +1414,13 @@ func (w *WebhookRemovalWorker) handleGitLabWebhookRemoval(ctx context.Context, a log.Printf("Successfully removed webhooks for project %s", args.ProjectPath) - // Update the webhook registry to mark as unconnected - err = w.updateWebhookRegistryForRemoval(ctx, args, projectID) - if err != nil { - log.Printf("Failed to update webhook registry for project %s: %v", args.ProjectPath, err) - return fmt.Errorf("failed to update webhook registry: %w", err) + // Update the webhook registry to mark as unconnected, unless skipped + if !args.SkipRegistryUpdate { + err = w.updateWebhookRegistryForRemoval(ctx, args, projectID) + if err != nil { + log.Printf("Failed to update webhook registry for project %s: %v", args.ProjectPath, err) + return fmt.Errorf("failed to update webhook registry: %w", err) + } } return nil @@ -1443,11 +1446,13 @@ func (w *WebhookRemovalWorker) handleGitHubWebhookRemoval(ctx context.Context, a log.Printf("Successfully removed webhooks for GitHub repository %s/%s", owner, repo) - // Update the webhook registry to mark as unconnected - err = w.updateWebhookRegistryForGitHubRemoval(ctx, args) - if err != nil { - log.Printf("Failed to update webhook registry for GitHub repository %s/%s: %v", owner, repo, err) - return fmt.Errorf("failed to update webhook registry: %w", err) + // Update the webhook registry to mark as unconnected, unless skipped + if !args.SkipRegistryUpdate { + err = w.updateWebhookRegistryForGitHubRemoval(ctx, args) + if err != nil { + log.Printf("Failed to update webhook registry for GitHub repository %s/%s: %v", owner, repo, err) + return fmt.Errorf("failed to update webhook registry: %w", err) + } } return nil @@ -1873,10 +1878,12 @@ func (w *WebhookRemovalWorker) handleBitbucketWebhookRemoval(ctx context.Context // Don't return error here - we still want to update the registry } - // Update the webhook_registry to mark as removed - err = w.updateWebhookRegistryForBitbucketRemoval(ctx, args) - if err != nil { - return fmt.Errorf("failed to update webhook registry: %w", err) + // Update the webhook_registry to mark as removed, unless skipped + if !args.SkipRegistryUpdate { + err = w.updateWebhookRegistryForBitbucketRemoval(ctx, args) + if err != nil { + return fmt.Errorf("failed to update webhook registry: %w", err) + } } log.Printf("Bitbucket webhook removal completed for repository: %s/%s", workspace, repo) @@ -2095,8 +2102,10 @@ func (w *WebhookRemovalWorker) handleGiteaWebhookRemoval(ctx context.Context, ar // continue to registry update even on API failure } - if err := w.updateWebhookRegistryForGiteaRemoval(ctx, args); err != nil { - return fmt.Errorf("failed to update webhook registry: %w", err) + if !args.SkipRegistryUpdate { + if err := w.updateWebhookRegistryForGiteaRemoval(ctx, args); err != nil { + return fmt.Errorf("failed to update webhook registry: %w", err) + } } log.Printf("Gitea webhook removal completed for repository: %s/%s", owner, repo) @@ -2329,13 +2338,14 @@ func (jq *JobQueue) QueueWebhookInstallJob(ctx context.Context, connectorID int, } // QueueWebhookRemovalJob queues a webhook removal job -func (jq *JobQueue) QueueWebhookRemovalJob(ctx context.Context, connectorID int, projectPath, provider, baseURL, pat string) error { +func (jq *JobQueue) QueueWebhookRemovalJob(ctx context.Context, connectorID int, projectPath, provider, baseURL, pat string, skipRegistryUpdate bool) error { args := WebhookRemovalJobArgs{ - ConnectorID: connectorID, - ProjectPath: projectPath, - Provider: provider, - BaseURL: baseURL, - PAT: pat, + ConnectorID: connectorID, + ProjectPath: projectPath, + Provider: provider, + BaseURL: baseURL, + PAT: pat, + SkipRegistryUpdate: skipRegistryUpdate, } _, err := jq.client.Insert(ctx, args, nil)