Skip to content

feat: support multiple nango connections#4481

Merged
goranmoomin merged 12 commits intomainfrom
feat/multiple-nango-connections
Mar 11, 2026
Merged

feat: support multiple nango connections#4481
goranmoomin merged 12 commits intomainfrom
feat/multiple-nango-connections

Conversation

@goranmoomin
Copy link
Collaborator

@goranmoomin goranmoomin commented Mar 10, 2026

Note

Medium Risk
Medium risk because it changes integration session/disconnect APIs, calendar sync behavior, and Supabase uniqueness constraints to key by integration_id + connection_id, which could affect existing connections and sync data cleanup if mis-migrated.

Overview
Adds multi-account support for Nango-backed integrations by treating connections as (integration_id, connection_id) instead of a single connection per integration.

Updates the backend calendar API to require connection_id in list-calendars/list-events (Google/Outlook) and validates connection status/ownership via a new NangoConnectionState.build_http_client path. The Nango session API now accepts mode (auto/connect/reconnect) plus optional connection_id, and disconnect deletes by connection_id.

Desktop and web UIs are updated to surface multiple connections (add account, pick connection for reconnect/disconnect), and the desktop calendar sync pipeline is refactored to sync per provider and per connectionId, storing connection_id on calendars and filtering/syncing events accordingly (including cleanup for removed calendars). A Supabase migration and upsert conflict target are updated to allow multiple connections per integration, and the Tauri calendar plugin gains list_connection_ids plus connectionId parameters for calendar/event listing.

Written by Cursor Bugbot for commit af2da4e. This will update automatically on new commits. Configure here.

@netlify
Copy link

netlify bot commented Mar 10, 2026

Deploy Preview for hyprnote-storybook ready!

Name Link
🔨 Latest commit af2da4e
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote-storybook/deploys/69b117e3d3c2190008566b4f
😎 Deploy Preview https://deploy-preview-4481--hyprnote-storybook.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Mar 10, 2026

Deploy Preview for hyprnote ready!

Name Link
🔨 Latest commit af2da4e
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/69b117e231346d00085cc862
😎 Deploy Preview https://deploy-preview-4481--hyprnote.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@yujonglee
Copy link
Contributor

Note that we need supabase db push before merging this @goranmoomin

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Free Tier Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Auto-mode cleanup deletes all user connections for integration
    • The auto-mode 404 reconnect cleanup now deletes only the stale integration_id+connection_id row via delete_connection_by_connection instead of all user integration rows.
  • ✅ Fixed: Failed connection fetch causes sibling calendars to be deleted
    • syncCalendars now tracks per-provider connection fetch errors and skips provider-wide calendar deletion when any connection list call fails, preventing accidental sibling calendar removal.

Create PR

Or push these changes by commenting:

@cursor push 9fc6081528
Preview (9fc6081528)
diff --git a/apps/desktop/src/services/calendar/ctx.ts b/apps/desktop/src/services/calendar/ctx.ts
--- a/apps/desktop/src/services/calendar/ctx.ts
+++ b/apps/desktop/src/services/calendar/ctx.ts
@@ -100,13 +100,17 @@
       connectionId: string;
       calendars: CalendarListItem[];
     }[] = [];
+    let hadConnectionError = false;
 
     for (const connectionId of connection_ids) {
       const result = await calendarCommands.listCalendars(
         provider,
         connectionId,
       );
-      if (result.status === "error") continue;
+      if (result.status === "error") {
+        hadConnectionError = true;
+        continue;
+      }
       perConnection.push({ connectionId, calendars: result.data });
     }
 
@@ -117,14 +121,16 @@
     store.transaction(() => {
       const removedCalendarIds = new Set<string>();
 
-      for (const rowId of store.getRowIds("calendars")) {
-        const row = store.getRow("calendars", rowId);
-        if (
-          row.provider === provider &&
-          !incomingIds.has(row.tracking_id_calendar as string)
-        ) {
-          removedCalendarIds.add(rowId);
-          store.delRow("calendars", rowId);
+      if (!hadConnectionError) {
+        for (const rowId of store.getRowIds("calendars")) {
+          const row = store.getRow("calendars", rowId);
+          if (
+            row.provider === provider &&
+            !incomingIds.has(row.tracking_id_calendar as string)
+          ) {
+            removedCalendarIds.add(rowId);
+            store.delRow("calendars", rowId);
+          }
         }
       }
 

diff --git a/crates/api-nango/src/routes/connect.rs b/crates/api-nango/src/routes/connect.rs
--- a/crates/api-nango/src/routes/connect.rs
+++ b/crates/api-nango/src/routes/connect.rs
@@ -126,7 +126,10 @@
                         );
                         state
                             .supabase
-                            .delete_connection(&user_id, &body.integration_id)
+                            .delete_connection_by_connection(
+                                &body.integration_id,
+                                &existing.connection_id,
+                            )
                             .await?;
                     }
                     Err(err) => {

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

@goranmoomin goranmoomin force-pushed the feat/multiple-nango-connections branch from cd55872 to e5de61d Compare March 11, 2026 07:20
@goranmoomin goranmoomin merged commit f9a76b1 into main Mar 11, 2026
17 of 18 checks passed
@yujonglee yujonglee deleted the feat/multiple-nango-connections branch March 11, 2026 07:34
jack-jackhui pushed a commit to jack-jackhui/char that referenced this pull request Mar 15, 2026
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.

2 participants