Skip to content

[FR] Add POST /api/connections/{id}/sync to trigger a connection sync over the public API #1520

Description

@Harsh23Kashyap

[FR] Add POST /api/connections/{id}/sync to trigger a connection sync over the public API

Problem

Operators have a growing surface for inspecting connection state over the public API: GET /api/connections (PR #1517) lists connections with sync state, and GET /api/connections/{id} (PR #1519) returns one connection with recent jobs and in-flight count. But once an operator sees "the GitHub connection has been broken for 6 hours", there is no public API to force a re-sync. The only path today is to open the web UI and click "Sync now" — which means scripted recovery (on-call runbooks, Grafana→Slack→script hooks) is impossible.

Background

The backend worker already has a public method for this: ConnectionManager.createJobs([connection]) in packages/backend/src/connectionManager.ts:129, exposed internally via the express route POST /api/sync-connection in packages/backend/src/api.ts:59. The web app already calls it from a server action (syncConnection in packages/web/src/features/workerApi/actions.ts:14) that's OWNER-gated and used by the React UI. The gap is purely the public-API surface on the web app: no route.ts for it.

Current behavior

There is no way for an external client (a script, a CI job, a Slack bot) to force a connection sync using only the public API. The internal worker endpoint is reachable only on the worker's network port, not the public web port.

Expected behavior

POST /api/connections/{id}/sync returns the new job id for an OWNER in the org. Like the list and detail endpoints, it is org-scoped (cross-org access returns 404, not 403). The response mirrors the existing workerApi/actions.ts → syncConnection server action's contract: { jobId: string }. The status code is 202 Accepted because the job is enqueued, not completed.

Proposed solution

Add a new route file at packages/web/src/app/api/(server)/connections/[id]/sync/route.ts that:

  1. Validates the path param id as a positive integer (400 on bad id).
  2. Loads the connection with where: { id, orgId: org.id } (404 on missing or cross-org).
  3. Reuses the existing syncConnection server action from workerApi/actions.ts so the public route and the UI share one implementation. The action already handles OWNER-gating and the worker call.
  4. Returns 202 with { jobId } on success. Errors from the worker bubble up as 500 (already handled by sew).
  5. Adds a POST /api/connections/{id}/sync path to the public OpenAPI spec and a Trigger connection sync entry to the System API Reference group.
  6. Adds a one-sentence CHANGELOG entry under [Unreleased] → Added.

Alternatives considered

  • Add a 409 Conflict if a sync is already in-flight. The backend's createJobs does not dedupe (each call creates a new ConnectionSyncJob row), so the conflict check would have to live in the web app, and the operator would lose the "force a re-sync even if one is running" use case. Operators can already see in-flight count via GET /api/connections/{id}, so they can decide whether to wait. Skipped.
  • Move the existing server action to an API route entirely. Would break the UI's form submit. The action is the right primitive; the API route should call it.
  • Add a bulk POST /api/connections/sync-all endpoint. Useful but larger scope; punted to a future PR.

Scope

One route file (~50 lines), one action-reuse import, one OpenAPI registration, one docs entry, one CHANGELOG line. Stacks on the list/detail endpoints (PRs #1517, #1519). Reuses connectionSummarySchema.omit/extend patterns from those PRs.

Acceptance criteria

  • POST /api/connections/{id}/sync returns 202 with { jobId } for an OWNER.
  • Returns 400 if id is not a positive integer.
  • Returns 401 if no auth.
  • Returns 403 if the caller is a MEMBER (not OWNER).
  • Returns 404 if the connection does not exist in the caller's org, or if it exists in another org (no cross-org existence leak).
  • Returns 500 if the worker is unreachable.
  • Public OpenAPI spec lists the new path under the System group with a 202/400/401/403/404/500 response matrix.
  • Vitest covers: 401, 403, 400 (bad id), 404 (missing), 404 (cross-org), 202 happy path, 500 (worker down).
  • No new TypeScript errors.

Backward compatibility

Purely additive. The existing workerApi/actions.ts → syncConnection server action is unchanged.

Risks

  • No new auth model. Reuses the existing OWNER gate from the server action.
  • No rate limiting on the public API. An OWNER could trigger many syncs in a loop. Mitigation: the backend's createJobs does not dedupe, but the BullMQ queue + worker concurrency cap provides back-pressure. A future PR could add a rate limit.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions