Skip to content

feat(dashboard): GitHub sync UX — honest webhook origin + project-page sync switcher - #47

Merged
dvcdsys merged 4 commits into
developfrom
fix/webhook-origin-public-url
May 20, 2026
Merged

feat(dashboard): GitHub sync UX — honest webhook origin + project-page sync switcher#47
dvcdsys merged 4 commits into
developfrom
fix/webhook-origin-public-url

Conversation

@dvcdsys

@dvcdsys dvcdsys commented May 20, 2026

Copy link
Copy Markdown
Owner

Summary

Two related improvements to the GitHub project sync UX in the dashboard.

1. Honest webhook delivery origin (tunnel optional)

The Webhook Integrations panel flagged ⚠ No live tunnel whenever no managed tunnel was running — even when CIX_PUBLIC_URL was set and the server is publicly reachable via infrastructure. A tunnel is optional in that case.

  • New GET /api/v1/github/webhooks/origin{ origin, source } (tunnel | public_url | none), mirroring the server's publicBaseURL() precedence.
  • WebhooksTab renders three honest states; the warning only shows for none.

2. Manage the sync method from the project page

A new Sync settings card lets an operator reconfigure how an external GitHub project is reindexed — webhook / polling / manual — without leaving the project page.

  • PATCH /api/v1/projects/{hash}/git-repo generalized into a sync-method switcher (updateProjectGitRepoSync): body { sync_method, poll_interval_seconds? }, returns { git_repo, note }.
    • webhook → auto-register the hook; on failure (no admin / no public URL) fall back to polling with a note.
    • pollingwebhook_mode=disabled + polling on (+ interval).
    • manual → disabled, polling off.
    • Switching away from webhook best-effort de-registers the hook and clears webhook_id.
  • gitrepos.SetSync (atomic, XOR-enforced) + ClearWebhookID; removed the now-unused SetPolling.
  • Webhook receiver now ignores deliveries when webhook_mode='disabled' — a lingering hook can't double-sync alongside the poll scheduler.
  • Dashboard: SyncSettingsCard (method radio + interval input, shows webhook_mode / next poll / last error), read-only for non-admins.

Reviewer notes

  • Branched off develop after the polling PR (feat(server): git polling sync as webhook alternative for non-admin repos #46) merged.
  • openapi.gen.go's large diff is oapi-codegen's deterministic reordering — verified routes/symbols, nothing dropped; all Go tests pass.
  • Frontend uses the ad-hoc api client (no generated-client dependency); src/api/generated.ts can be regenerated via npm run gen:api later (non-blocking).

Test plan

  • go build ./..., go vet ./... clean
  • go test ./... green — new coverage: webhook-origin (public_url/none); sync switcher (webhook fallback, polling, manual, invalid→422); SetSync/ClearWebhookID; receiver ignores disabled repos
  • Frontend not built/typechecked locally (no node in this env) — needs npm run build + a visual pass of the Webhook panel states and the project-page Sync settings card before merge
  • Manual: on the project page, switch a repo between webhook/polling/manual and confirm the resulting state + toasts

🤖 Generated with Claude Code

dvcdsys and others added 2 commits May 20, 2026 15:28
…l optional)

The Webhook Integrations panel showed a "No live tunnel" warning whenever no
managed tunnel was running — even when CIX_PUBLIC_URL was set and the server
was already publicly reachable via infrastructure (reverse proxy, ingress,
static IP). A tunnel is optional in that case; webhooks deliver fine.

- New GET /api/v1/github/webhooks/origin returns the effective webhook
  delivery origin and its source (tunnel | public_url | none), mirroring the
  server's publicBaseURL() precedence.
- WebhooksTab now renders three states: active tunnel URL, infra-provided
  origin via CIX_PUBLIC_URL (neutral — tunnel optional), or — only when
  neither is configured — a "no public origin" warning.

Backend tests cover the public_url and none sources. Frontend uses the ad-hoc
api client (no generated-client dependency); src/api/generated.ts can be
regenerated via `npm run gen:api` when node is available (non-blocking).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a "Sync settings" card to the project page that lets an operator
reconfigure how an external GitHub project is reindexed — webhook,
polling, or manual — without leaving the page.

Server:
- Generalize PATCH /api/v1/projects/{hash}/git-repo into a sync-method
  switcher (operationId updateProjectGitRepoSync). Request now takes
  { sync_method: webhook|polling|manual, poll_interval_seconds? } and
  returns { git_repo, note }.
  - webhook → webhook_mode=auto + auto-register the hook; on failure
    (no admin / no public URL) fall back to polling with a note.
  - polling → webhook_mode=disabled + polling on (+ interval).
  - manual → webhook_mode=disabled, polling off.
  - Switching away from webhook best-effort de-registers the hook
    (githubapi.DeleteWebhook) and clears webhook_id.
- gitrepos: SetSync (atomic webhook_mode + polling + interval, XOR
  enforced) and ClearWebhookID; remove the now-unused SetPolling.
- Webhook receiver now ignores deliveries when webhook_mode='disabled',
  so a lingering hook can't double-sync alongside the poll scheduler.

Dashboard:
- SyncSettingsCard (method radio + interval input, shows webhook_mode,
  next poll, last error). useProjectGitRepo + useUpdateProjectSync hooks.
  Read-only for non-admins.

Backend tests cover all three methods, the webhook→polling fallback,
invalid method (422), SetSync transitions, ClearWebhookID, and the
receiver ignoring disabled repos. Frontend not built locally (no node);
needs `npm run build` + visual check before merge.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@dvcdsys dvcdsys changed the title fix(dashboard): treat CIX_PUBLIC_URL as a valid webhook origin (tunnel optional) feat(dashboard): GitHub sync UX — honest webhook origin + project-page sync switcher May 20, 2026
dvcdsys and others added 2 commits May 20, 2026 15:58
The sync switcher's "Webhook" option only ever attempted auto-registration
and silently fell back to polling on failure, and the card never surfaced
the webhook URL/secret — so a user without an admin token (or public URL)
could not actually set up a webhook. It looked broken.

- Server: choosing webhook no longer falls back to polling. If auto-register
  can't install the hook (no admin token / no public URL), the repo is left
  in webhook_mode='manual' — a valid webhook the operator finishes by hand —
  with a note explaining how. (Add-repo's auto→polling fallback is unchanged.)
- Dashboard: when Webhook is selected, the card shows the payload URL (copy)
  and the HMAC secret behind a reveal toggle (admin-only), GitHub setup
  instructions, a hint when no public origin is configured, and whether the
  hook was auto-registered. New useProjectWebhookInfo hook.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Require admin on PATCH /projects/{hash}/git-repo: it decrypts the PAT and
  registers/deletes GitHub webhooks, matching ReconcileWebhooks' privilege.
  Closes the direct-API hole where a viewer could drive those operations
  (the dashboard already gated the card behind isAdmin).
- Re-saving "webhook" on a repo that's already a manually-configured hook
  (webhook_mode=manual, no stored hook id) preserves manual instead of
  flipping to auto and registering a SECOND hook beside the operator's.

Test covers the manual-preservation path (second webhook save keeps manual
with the "left as-is" note).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@dvcdsys
dvcdsys merged commit d3c322b into develop May 20, 2026
1 check passed
@dvcdsys
dvcdsys deleted the fix/webhook-origin-public-url branch May 20, 2026 15:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant