Skip to content

feat: add self-hosted Supabase support#2885

Closed
wwwillchen-bot wants to merge 4 commits intodyad-sh:mainfrom
wwwillchen-bot:agent--1772652164604-1772651978
Closed

feat: add self-hosted Supabase support#2885
wwwillchen-bot wants to merge 4 commits intodyad-sh:mainfrom
wwwillchen-bot:agent--1772652164604-1772651978

Conversation

@wwwillchen-bot
Copy link
Copy Markdown
Collaborator

@wwwillchen-bot wwwillchen-bot commented Mar 4, 2026

Summary

  • Add configuration options for self-hosted Supabase instances (API URL and secret key)
  • Add UI in SupabaseIntegration component to configure self-hosted settings, accessible even when not connected to Supabase Cloud
  • Update settings encryption/decryption to handle the new secret key field
  • Update Supabase management client to use self-hosted API URL when configured

Test plan

  • Verify the self-hosted settings section appears in the Supabase integration UI
  • Test saving self-hosted API URL and secret key (both must be set together)
  • Verify settings persist across app restarts
  • Test that the management client uses the self-hosted URL when configured

🤖 Generated with Claude Code


Open with Devin

@wwwillchen
Copy link
Copy Markdown
Collaborator

@BugBot run

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces comprehensive support for self-hosted Supabase instances, enabling users to connect to their private Supabase deployments. It provides a new user interface for configuring the necessary API URL and secret key, ensures these credentials are encrypted for security, and modifies the underlying Supabase management client to correctly route API requests to the specified self-hosted endpoint. This enhancement significantly expands the flexibility and deployment options for users.

Highlights

  • Self-hosted Supabase Configuration: Added new configuration options for self-hosted Supabase instances, including an API URL and a secret key, which are stored securely.
  • User Interface for Self-hosting: Implemented a dedicated UI section within the Supabase integration component to allow users to configure and save their self-hosted Supabase settings, accessible even when not connected to Supabase Cloud.
  • Secure Credential Handling: Integrated encryption and decryption mechanisms for the new self-hosted Supabase secret key to ensure sensitive data is handled securely.
  • Dynamic API Endpoint Selection: Updated the Supabase management client to dynamically use the configured self-hosted API URL and secret key for all API calls when self-hosted settings are provided, otherwise falling back to the default Supabase Cloud API.
Changelog
  • src/components/SupabaseIntegration.tsx
    • Imported the Input component for user input fields.
    • Added state variables for self-hosted Supabase API URL and secret key.
    • Implemented a new function handleSelfHostedSettingsSave to manage saving self-hosted configurations.
    • Created a selfHostedSection JSX element to render the UI for self-hosted settings.
    • Modified the component's render logic to display the self-hosted section conditionally or always, allowing configuration before connection.
  • src/lib/schemas.ts
    • Extended the SupabaseSchema to include optional fields for selfHostedSupabaseApiUrl and selfHostedSupabaseSecretKey.
  • src/main/settings.ts
    • Added logic to decrypt the selfHostedSupabaseSecretKey when reading settings.
    • Added logic to encrypt the selfHostedSupabaseSecretKey when writing settings.
  • src/supabase_admin/supabase_management_client.ts
    • Defined DEFAULT_SUPABASE_API_URL and utility functions getSelfHostedConfig, getSupabaseApiBaseUrl, and getAuthorizationHeader to manage self-hosted configurations.
    • Updated getSupabaseClient and getSupabaseClientForOrganization to use the self-hosted secret key and base URL if configured.
    • Modified various API interaction functions (listSupabaseOrganizations, getOrganizationMembers, getOrganizationDetails, getSupabaseProjectLogs, listSupabaseFunctions, listSupabaseBranches, deploySupabaseFunction, bulkUpdateFunctions) to dynamically use the self-hosted Supabase API base URL and authorization header.
Activity
  • The pull request was generated by wwwillchen-bot, indicating an automated or semi-automated creation process.
  • The PR description includes a detailed summary and a test plan, suggesting a clear intent and verification steps.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

gemini-code-assist[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 4, 2026

🔍 Dyadbot Code Review Summary

Verdict: 🤔 NOT SURE - Potential issues

Reviewed by 3 independent agents: Correctness Expert, Code Health Expert, UX Wizard.

Issues Summary

Severity File Issue
🔴 HIGH src/components/SupabaseIntegration.tsx Disconnect All clears self-hosted Supabase settings
🟡 MEDIUM src/components/SupabaseIntegration.tsx:24-29 Local state not synced with external settings changes
🟡 MEDIUM src/supabase_admin/supabase_management_client.ts getAuthorizationHeader() inconsistently applied across fetch calls
🟡 MEDIUM src/components/SupabaseIntegration.tsx:169-175 Save button has no loading/disabled state during save
🟢 Low Priority Notes (4 items)
  • URL validation not triggered - src/components/SupabaseIntegration.tsx:144: type="url" validation doesn't fire on button click (only on form submit), so invalid URLs are silently saved
  • No show/hide toggle for secret key - src/components/SupabaseIntegration.tsx:160: Password field has no eye-icon toggle for users to verify pasted key
  • Hardcoded English strings - src/components/SupabaseIntegration.tsx:169: Button label and section text use hardcoded English instead of t() translation function
  • Redundant readSettings() calls - src/supabase_admin/supabase_management_client.ts:29-63: getSelfHostedConfig() reads settings from disk on every call, resulting in 3-5 disk reads per API call
🚫 Dropped False Positives (4 items)
  • Not-connected state missing context - Dropped: Parent component likely handles the Supabase Cloud connect CTA; rendering self-hosted section independently is the stated intent
  • Section has no border in not-connected state - Dropped: Conditional styling is intentional based on the connected/disconnected layout
  • Decrypt block encryptionType guard inconsistency - Dropped: Follows the exact same pattern as existing accessToken and refreshToken decryption
  • Leftover commented-out Supabase icon import - Dropped: Pre-existing code not introduced by this PR

Generated by Dyadbot multi-agent code review

github-actions[bot]

This comment was marked as resolved.

cubic-dev-ai[bot]

This comment was marked as resolved.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 4, 2026

Greptile Summary

This PR adds first-class support for self-hosted Supabase instances. It introduces a supabaseMode column ("cloud" | "self-hosted") on the apps table, new settings fields (selfHosted.apiUrl, selfHosted.secretKey, selfHosted.publishableKey), and UI in both SupabaseIntegration (global settings) and SupabaseConnector (per-app connector) to configure and switch between modes. The Supabase management client is updated to route API calls through the self-hosted URL when the mode is "self-hosted", and all downstream handlers (chat_stream_handlers, response_processor, version_handlers, etc.) thread the new mode field through to every Supabase operation.

Key changes:

  • New supabase_mode DB column + migration; getEffectiveAppSupabaseMode provides a backwards-compatible fallback (existing rows with a supabaseProjectId are treated as "cloud")
  • Self-hosted settings are encrypted at rest via the existing safeStorage mechanism and preserved when disconnecting from cloud
  • Self-hosted branching and project logs short-circuit gracefully (return empty results)
  • chat_stream_handlers.ts correctly guards Supabase calls with hasSelfHostedSupabaseConfig, but token_count_handlers.ts is missing the same guard — if a self-hosted app has its settings cleared, getSelfHostedProjectUrl() will throw during token counting

Confidence Score: 3/5

  • Mostly safe to merge, but the missing guard in token_count_handlers.ts can cause an unhandled exception for self-hosted apps when settings are cleared.
  • The overall feature implementation is consistent and the self-hosted routing logic is correct throughout most of the codebase. The main concern is the asymmetry between chat_stream_handlers.ts (which properly guards with hasSelfHostedSupabaseConfig) and token_count_handlers.ts (which only checks supabaseProjectId). This can throw at runtime. Two minor style issues in SupabaseConnector.tsx (wrong i18n key, stale dep) do not affect correctness.
  • src/ipc/handlers/token_count_handlers.ts needs the same self-hosted guard that was applied to chat_stream_handlers.ts

Important Files Changed

Filename Overview
src/supabase_admin/supabase_management_client.ts Adds getSelfHostedConfig() and getSupabaseApiBaseUrl(mode) helpers; routes all management API calls through the self-hosted URL when mode === "self-hosted". Branching and project logs short-circuit cleanly for self-hosted. Logic is sound; no new issues introduced.
src/main/settings.ts Adds legacy migration path for selfHostedSupabaseSecretKey/selfHostedSupabaseApiUrl fields, and correctly encrypts/decrypts selfHosted.secretKey during read/write cycles. The migration logic is correct and consistent with the existing token encryption pattern.
src/lib/schemas.ts Adds SupabaseAppModeSchema, SelfHostedSupabaseSettingsSchema, hasSelfHostedSupabaseConfig, and getEffectiveAppSupabaseMode helpers. All are well-typed and tested in schemas.test.ts.
src/ipc/handlers/token_count_handlers.ts Adds mode to getSupabaseClientCode and getSupabaseContext calls, but the guard condition still only checks supabaseProjectId. When supabaseMode === "self-hosted" and self-hosted settings have been cleared, getSelfHostedProjectUrl() will throw — unlike chat_stream_handlers.ts which has the correct hasSelfHostedSupabaseConfig guard.
src/components/SupabaseConnector.tsx Large refactor to support cloud/self-hosted mode toggling. Two style issues: the "cloud mode reconnect" card mistakenly uses selfHostedTitle i18n key, and isConnected is listed as an effect dependency but is unused in the effect body.
src/components/SupabaseIntegration.tsx Adds self-hosted settings form (API URL, secret key, publishable key) that is shown regardless of cloud connection status. Correctly preserves self-hosted settings when disconnecting from cloud. The fresh-settings read before save prevents concurrent write conflicts.
src/ipc/handlers/supabase_handlers.ts Properly threads mode through listBranches, getEdgeLogs, and setAppProject/unsetAppProject handlers. The fake connect test handler now correctly sets supabaseMode: "cloud".
src/supabase_admin/supabase_context.ts Self-hosted paths correctly short-circuit all cloud management API calls and return lightweight context using only locally-configured URL/publishable key. Schema introspection is explicitly unsupported for self-hosted (returns a descriptive note), which is an acceptable first-pass limitation.
src/ipc/handlers/chat_stream_handlers.ts Correctly adds hasSelfHostedSupabaseConfig guard alongside isSupabaseConnected before fetching Supabase client code and context. Mode is properly threaded to all downstream calls.
drizzle/0027_productive_pretty_boy.sql Simple ALTER TABLE apps ADD supabase_mode text migration. Column is nullable, so existing rows default to NULL and the legacy fallback in getEffectiveAppSupabaseMode correctly interprets NULL + projectId as "cloud".

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[App has supabaseProjectId] --> B{supabaseMode?}
    B -- cloud / null --> C{isSupabaseConnected?}
    B -- self-hosted --> D{hasSelfHostedSupabaseConfig?}

    C -- yes --> E[getSupabaseClient\nwith org credentials]
    C -- no --> F[Skip Supabase operations]

    D -- yes --> G[getSupabaseClient\nwith secretKey from settings]
    D -- no --> F

    G --> H[getSelfHostedConfig\nreads apiUrl + secretKey]
    H --> I[SupabaseManagementAPI\nbaseUrl = selfHosted.apiUrl]

    E --> J[Token refresh flow\nper-org credentials]
    J --> K[SupabaseManagementAPI\nbaseUrl = api.supabase.com]

    I --> L[Supabase Operations\nbranches / functions / SQL]
    K --> L

    style D fill:#f9a,stroke:#c00
    style F fill:#fdd,stroke:#c00
Loading

Comments Outside Diff (1)

  1. src/ipc/handlers/token_count_handlers.ts, line 80 (link)

    Missing self-hosted Supabase guard

    chat_stream_handlers.ts correctly guards Supabase calls with hasSelfHostedSupabaseConfig(settings) before calling getSupabaseClientCode or getSupabaseContext for self-hosted mode. token_count_handlers.ts only checks chat.app?.supabaseProjectId, which means if a user sets supabaseMode = "self-hosted" but then clears the self-hosted settings, getSelfHostedProjectUrl() (called inside getSupabaseClientCode / getSupabaseContext) will throw an unhandled error.

    The guard used in chat_stream_handlers.ts should be applied here too:

    if (
      chat.app?.supabaseProjectId &&
      (chat.app.supabaseMode === "self-hosted"
        ? hasSelfHostedSupabaseConfig(settings)
        : isSupabaseConnected(settings))
    ) {
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/ipc/handlers/token_count_handlers.ts
Line: 80

Comment:
**Missing self-hosted Supabase guard**

`chat_stream_handlers.ts` correctly guards Supabase calls with `hasSelfHostedSupabaseConfig(settings)` before calling `getSupabaseClientCode` or `getSupabaseContext` for self-hosted mode. `token_count_handlers.ts` only checks `chat.app?.supabaseProjectId`, which means if a user sets `supabaseMode = "self-hosted"` but then clears the self-hosted settings, `getSelfHostedProjectUrl()` (called inside `getSupabaseClientCode` / `getSupabaseContext`) will throw an unhandled error.

The guard used in `chat_stream_handlers.ts` should be applied here too:

```typescript
if (
  chat.app?.supabaseProjectId &&
  (chat.app.supabaseMode === "self-hosted"
    ? hasSelfHostedSupabaseConfig(settings)
    : isSupabaseConnected(settings))
) {
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/components/SupabaseConnector.tsx
Line: 583-590

Comment:
**Wrong i18n key reused for cloud mode section**

This section is rendered when `selectedMode === "cloud" && !isConnected` (i.e., cloud mode is selected but no cloud accounts are connected), yet it uses `t("integrations.supabase.selfHostedTitle")` as the `CardTitle`. While `selfHostedTitle` happens to resolve to the generic string `"Supabase"`, it is semantically the wrong key for a cloud-mode UI card and could cause confusion in the future when the string is updated.

A dedicated key (e.g., `"integrations.supabase.cloudTitle"`) should be added and used here instead.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/components/SupabaseConnector.tsx
Line: 111-123

Comment:
**`isConnected` listed as effect dependency but unused in body**

`isConnected` is declared as a dependency of this `useEffect` but it is never read inside the effect body. The linter (or future readers) may flag this as an unnecessary dependency. If the intent is to reset the selected mode whenever the cloud connection status changes, consider instead deriving `selectedMode` directly from the app/settings state (controlled component approach) rather than keeping it in local state.

```suggestion
  }, [
    app?.supabaseProjectId,
    appSupabaseMode,
    hasSelfHostedSupabase,
  ]);
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 4c7de05

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 4, 2026

Additional Comments (1)

src/components/SupabaseIntegration.tsx, line 59
Stale self-hosted state after full disconnect

When handleDisconnectAllFromSupabase sets supabase: undefined, the local React state variables selfHostedApiUrl and selfHostedSecretKey are not reset. After a successful disconnect, the component still displays the previous self-hosted credentials in the inputs. If the user clicks "Save Self-hosted Settings" without noticing, those deleted values are silently restored to settings.

Reset the local state inside the success branch to keep the UI in sync:

  const handleDisconnectAllFromSupabase = async () => {
    setIsDisconnecting(true);
    try {
      // Clear the entire supabase object in settings (including all organizations)
      const result = await updateSettings({
        supabase: undefined,
        // Also disable the migration setting on disconnect
        enableSupabaseWriteSqlMigration: false,
      });
      if (result) {
        showSuccess(t("integrations.supabase.disconnectedAll"));
        setSelfHostedApiUrl("");
        setSelfHostedSecretKey("");
        await refetchOrganizations();
      } else {
        showError(t("integrations.supabase.failedDisconnect"));
      }
    } catch (err: any) {
      showError(
        err.message || "An error occurred while disconnecting from Supabase",
      );
    } finally {
      setIsDisconnecting(false);
    }
  };

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 4, 2026

🤖 Claude Code Review Summary

PR Confidence: 3/5

Core self-hosted auth bypass and stale-state bugs are fixed, but 4 pre-existing flaky E2E tests remain and no new tests were added for the self-hosted feature.

Unresolved Threads

Thread Rationale Link
No tests added for non-trivial feature Adding tests requires significant effort and should be scoped separately. Reviewed Principles #2 and #4 but neither addresses test coverage vs. shipping velocity trade-offs. View

Resolved Threads

Issue Rationale Link
Self-hosted mode blocked by cloud auth requirement (6 threads: gemini, chatgpt-codex, cubic) Added early self-hosted config check in getSupabaseClient that short-circuits before cloud auth, per Principle #1: Backend-Flexible View
Global auth/URL override security concern (3 threads: gemini, cubic) Removed getAuthorizationHeader global override; self-hosted now returns early with its own client View
Stale React state overwrites tokens (4 threads: devin, chatgpt-codex, github-actions, cubic) Added useEffect for state sync and fresh IPC settings read before save View
Disconnect All clears self-hosted settings (github-actions) Modified disconnect to preserve self-hosted settings while clearing cloud auth fields View
Save button has no loading state (github-actions) Added isSaving state with disabled button and "Saving..." label View
Schema does not enforce both-or-neither invariant (cubic) Resolved: enforced at UI and runtime levels; adding .refine() would change ZodObject to ZodEffects, breaking type inference View
Product Principle Suggestions

The following suggestions could improve rules/product-principles.md to help resolve ambiguous cases in the future:

  • Principle Infinite Loop: Checking Node.js setup... #2: Productionizable: Add guidance on minimum test coverage requirements for new features, e.g. "New features adding schema fields, encryption logic, or IPC handlers should include at least unit tests for the data layer."

🤖 Generated by Claude Code

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 4, 2026

✅ Claude Code completed successfully

Summary

  • Fixed 16 review comments across 2 files (self-hosted Supabase auth, React state management, UI interactions)
  • 1 comment flagged for human review (missing tests for new feature)
  • CI failure is pre-existing flaky E2E tests (mcp, select_component, switch_versions, undo) - not related to this PR
Details

Review Comments Fixed:

  • Added early self-hosted config check in getSupabaseClient() to bypass cloud auth when self-hosted is configured
  • Removed getAuthorizationHeader() global override that could leak tokens between cloud and self-hosted endpoints
  • Simplified getSupabaseClientForOrganization() to remove unnecessary self-hosted overrides (handled by early return)
  • Fixed handleDisconnectAllFromSupabase to preserve self-hosted settings when disconnecting cloud auth
  • Added useEffect to sync local React state when settings change externally
  • Fixed stale-state race in handleSelfHostedSettingsSave by reading fresh settings via IPC before saving
  • Added loading/disabled state to Save Self-hosted Settings button

CI Checks:

  • 4 failed E2E tests are pre-existing flaky tests unrelated to this PR (timeout-based failures)
  • Build, lint, and other checks pass

Workflow run

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 4, 2026

Additional Comments (2)

src/supabase_admin/supabase_management_client.ts, line 469
Cloud credentials sent to self-hosted URL in getOrganizationMembers

This function calls getSupabaseClientForOrganization(organizationSlug) to get cloud OAuth credentials, but then uses getSupabaseApiBaseUrl() for the base URL. When both a self-hosted instance and a cloud organization are configured simultaneously, this sends a cloud-issued Bearer token to the self-hosted API endpoint, resulting in authentication failure.

getOrganizationDetails has the identical issue at the equivalent location (around line 522).

Suggested fix: Route through getSupabaseClient({ organizationSlug }) instead, which correctly handles the self-hosted short-circuit:

  const client = await getSupabaseClient({ organizationSlug });
  const accessToken = (client as any).options.accessToken;
  const baseUrl = (client as any).options.baseUrl ?? DEFAULT_SUPABASE_API_URL;

This ensures the token and base URL always come from the same configuration source.


src/supabase_admin/supabase_management_client.ts, line 410
Cloud OAuth token sent to self-hosted URL during org listing

This function takes a cloud OAuth accessToken as its argument (used during the Supabase cloud authentication callback) and uses getSupabaseApiBaseUrl() for the base URL. When a self-hosted instance is configured, the cloud OAuth token will be sent to the self-hosted API's /v1/organizations endpoint, causing the cloud org-listing step to fail.

Since this function's purpose is specifically to enumerate cloud organizations during the OAuth flow, it should always use the cloud URL regardless of self-hosted configuration:

  const response = await fetchWithRetry(
    `${DEFAULT_SUPABASE_API_URL}/v1/organizations`,

devin-ai-integration[bot]

This comment was marked as resolved.

@wwwillchen
Copy link
Copy Markdown
Collaborator

@BugBot run

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 4, 2026

Additional Comments (2)

src/supabase_admin/supabase_management_client.ts, line 480
Cloud token sent to self-hosted URL in getOrganizationMembers

When both self-hosted Supabase and cloud organizations are configured simultaneously, this function calls getSupabaseClientForOrganization(organizationSlug) to retrieve a cloud OAuth access token, but then uses getSupabaseApiBaseUrl() which returns the self-hosted URL. This sends the cloud credential to the wrong endpoint and will cause authentication failures.

The same pattern is present in getOrganizationDetails at line 522–534.

These functions should use the cloud base URL explicitly (since they were called with an organizationSlug and resolved a cloud credential), or fall through to the self-hosted client path via getSupabaseClient consistently.

// Instead of resolving the cloud client and then calling getSupabaseApiBaseUrl(),
// derive the base URL from the client that was already resolved:
const client = await getSupabaseClientForOrganization(organizationSlug);
const accessToken = (client as any).options.accessToken;
const baseUrl = (client as any).options.baseUrl ?? DEFAULT_SUPABASE_API_URL;

src/supabase_admin/supabase_management_client.ts, line 418
Cloud OAuth token routed to self-hosted URL in listSupabaseOrganizations

listSupabaseOrganizations receives a cloud OAuth accessToken as a parameter (used during the Supabase Cloud connect flow) but resolves the request URL via getSupabaseApiBaseUrl(). If the user has self-hosted settings configured, this function will POST the cloud token to the self-hosted API endpoint, which will reject it and break the ability to connect new cloud organizations.

Since this function operates exclusively with a cloud-supplied access token, it should always target the cloud API URL:

  const baseUrl = DEFAULT_SUPABASE_API_URL;

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 4, 2026

🔍 Dyadbot Code Review Summary

Verdict: 🤔 NOT SURE - Potential issues

Reviewed by 3 independent agents: Correctness Expert, Code Health Expert, UX Wizard.

Issues Summary

Severity File Issue
🟡 MEDIUM src/components/SupabaseIntegration.tsx:117 No field-level validation feedback for paired constraint
🟡 MEDIUM src/components/SupabaseIntegration.tsx:206 Self-hosted section lacks parent Supabase context when disconnected
🟢 Low Priority Notes (4 items)
  • Hardcoded English strings - src/components/SupabaseIntegration.tsx:97,99,138 - Success/error messages use hardcoded strings instead of t() i18n, inconsistent with rest of component
  • No show/hide toggle for secret key - src/components/SupabaseIntegration.tsx:185 - Password field has no eye icon toggle, making it hard to verify long API secrets
  • No URL format validation - src/components/SupabaseIntegration.tsx:169 - type="url" is set but no actual URL validation runs on save since inputs aren't in a <form>
  • No Enter-key submission support - src/components/SupabaseIntegration.tsx:165 - Inputs not wrapped in <form>, so pressing Enter does nothing
🚫 Dropped False Positives (5 items)
  • Decryption inconsistency - Dropped: The new code correctly follows the exact same encryptionType check pattern used by all other secrets in readSettings() (refreshToken, accessToken, org tokens, neon tokens)
  • URL/token mismatch in org functions - Dropped: Already well-covered by existing comments from chatgpt-codex-connector, github-actions, and cubic-dev-ai bots
  • Disconnect race condition - Dropped: Already covered by devin-ai-integration comment on line 62
  • Settings overwrite on early save - Dropped: Already covered by chatgpt-codex-connector comment
  • Schema doesn't enforce both-or-neither - Dropped: Already covered by cubic-dev-ai comment on schemas.ts line 202; constraint is enforced at UI and runtime layers

Generated by Dyadbot multi-agent code review

github-actions[bot]

This comment was marked as resolved.

github-actions[bot]

This comment was marked as resolved.

@github-actions github-actions bot added the needs-human:review-issue ai agent flagged an issue that requires human review label Mar 4, 2026
wwwillchen and others added 3 commits March 12, 2026 13:19
Add configuration options for self-hosted Supabase instances:
- Add selfHostedSupabaseApiUrl and selfHostedSupabaseSecretKey to settings schema
- Add UI in SupabaseIntegration to configure self-hosted settings
- Update settings encryption/decryption to handle new secret key
- Update management client to use self-hosted API URL when configured

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add early self-hosted config check in getSupabaseClient to bypass cloud auth requirement
- Remove getAuthorizationHeader global override to prevent token/URL misrouting
- Preserve self-hosted settings when disconnecting from Supabase Cloud
- Add useEffect to sync local state when settings change externally
- Read fresh settings via IPC before saving to avoid stale state overwrites
- Add loading/disabled state to Save button during async save

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@wwwillchen
Copy link
Copy Markdown
Collaborator

@BugBot run

devin-ai-integration[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

dyad-assistant[bot]

This comment was marked as resolved.

@dyad-assistant
Copy link
Copy Markdown
Contributor

🔍 Dyadbot Code Review Summary

Verdict: 🤔 NOT SURE - Potential issues

Reviewed by 3 independent agents: Correctness Expert, Code Health Expert, UX Wizard.

Issues Summary

Severity File Issue
🟡 MEDIUM src/ipc/handlers/chat_stream_handlers.ts:755 OR condition allows cloud-mode apps through self-hosted-only check
🟡 MEDIUM src/supabase_admin/supabase_context.ts:82 Self-hosted API URL not JSON-escaped in generated client code
🟡 MEDIUM src/components/SupabaseConnector.tsx:487 Duplicate disconnect buttons shown simultaneously
🟡 MEDIUM src/components/SupabaseConnector.tsx:415 Branch selector renders empty dropdown for self-hosted
🟡 MEDIUM src/components/SupabaseConnector.tsx:673 Hardcoded English strings instead of i18n translation keys
🟡 MEDIUM supabase/.branches/_current_branch Supabase CLI local dev files accidentally committed
🟢 Low Priority Notes (4 items)
  • Switching modes silently overwrites project connection - src/components/SupabaseConnector.tsx - No confirmation dialog when switching from self-hosted to cloud mode overwrites the active project reference
  • Grammar inconsistency in connected project text - src/components/chat/DyadAddIntegration.tsx:75 - Cloud case missing article "a" ("connected to Supabase project" vs "connected to a Supabase project")
  • Raw error string shown to users - src/components/SupabaseConnector.tsx:1047 - String(error) can produce unhelpful messages like "TypeError: Failed to fetch"
  • Duplicated self-hosted config reading - src/supabase_admin/supabase_context.ts - getSelfHostedProjectUrl() and getSelfHostedPublishableKey() duplicate logic already in supabase_management_client.ts:getSelfHostedConfig()
🚫 Dropped False Positives (7 items)
  • Schema doesn't enforce both-or-neither invariant - Dropped: Already commented on by existing reviewer
  • Self-hosted section lacks parent context - Dropped: Already commented on by existing reviewer
  • Org functions don't accept mode parameter - Dropped: These functions are cloud-only today and getSupabaseApiBaseUrl() correctly defaults to cloud URL; no actual bug
  • Self-hosted template string duplicated 3 times - Dropped: The templates serve different purposes (client code, context, project info) with different content beyond the shared metadata
  • Legacy migration reads never-existing fields - Dropped: Likely migration path from an earlier PR iteration; common forward-compat pattern
  • catch(err: any) suppresses type safety - Dropped: Common pattern throughout the existing codebase; not unique to this PR
  • Mode selector asymmetric (only shows self-hosted when configured) - Dropped: Intentional design — self-hosted tab only appears after configuring in Settings

Generated by Dyadbot multi-agent code review

@github-actions
Copy link
Copy Markdown
Contributor

🎭 Playwright Test Results

❌ Some tests failed

OS Passed Failed Flaky Skipped
🍎 macOS 256 2 9 6

Summary: 256 passed, 2 failed, 9 flaky, 6 skipped

Failed Tests

🍎 macOS

  • mcp.spec.ts > mcp - call calculator
    • Error: locator.fill: Error: strict mode violation: getByRole('textbox', { name: 'Key' }) resolved to 3 elements:
  • mcp.spec.ts > mcp - call calculator via http
    • Error: locator.fill: Error: strict mode violation: getByRole('textbox', { name: 'Key' }) resolved to 3 elements:

📋 Re-run Failing Tests (macOS)

Copy and paste to re-run all failing spec files locally:

npm run e2e \
  e2e-tests/mcp.spec.ts

⚠️ Flaky Tests

🍎 macOS

  • auto_approve.spec.ts > auto-approve (passed after 1 retry)
  • debugging_logs.spec.ts > clear filters button works (passed after 1 retry)
  • logs_server.spec.ts > system messages UI shows server logs with correct type (passed after 1 retry)
  • refresh.spec.ts > spa navigation inside iframe does not change iframe src attribute (passed after 1 retry)
  • setup_flow.spec.ts > Setup Flow > setup banner shows correct state when node.js is installed (passed after 1 retry)
  • setup.spec.ts > setup ai provider (passed after 1 retry)
  • template-create-nextjs.spec.ts > create next.js app (passed after 1 retry)
  • undo.spec.ts > undo with native git (passed after 1 retry)
  • visual_editing.spec.ts > swap image via URL (passed after 1 retry)

📊 View full report

- Fix guard condition in chat_stream_handlers to check mode-specific credentials
- Add app?.supabaseMode to useEffect dependency array in PreviewPanel
- Use JSON.stringify for self-hosted URL in generated client code
- Remove duplicate disconnect button in self-hosted mode
- Hide branch selector for self-hosted (branching not available)
- Add field-level validation errors for paired self-hosted fields
- Add parent Supabase heading when not connected to cloud
- Show self-hosted UI when app is in self-hosted mode even if config is cleared
- Replace hardcoded English strings with i18n translation keys
- Add supabase CLI state files to .gitignore and remove from tracking
- Simplify disconnect handler to avoid stale-read race condition

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@wwwillchen
Copy link
Copy Markdown
Collaborator

@BugBot run

@dyad-assistant dyad-assistant bot removed the needs-human:review-issue ai agent flagged an issue that requires human review label Mar 12, 2026
@dyad-assistant
Copy link
Copy Markdown
Contributor

✅ Claude Code completed successfully

Summary

  • Fixed 13 unresolved review comments (all resolved)
  • 2 pre-existing E2E test failures in mcp.spec.ts (unrelated to this PR - locator ambiguity issue)

Review Comments Addressed

Issue Fix Source
Guard condition allows mismatched Supabase mode in chat streaming Check mode-specific credentials instead of OR devin-ai, chatgpt-codex, dyad-assistant (3 duplicate threads)
Missing supabaseMode in useEffect dependency array Added app?.supabaseMode to deps devin-ai
Self-hosted URL not JSON-escaped in generated client code Used JSON.stringify(projectUrl) dyad-assistant
Duplicate disconnect buttons in self-hosted mode Removed redundant outline button dyad-assistant
Empty branch selector for self-hosted projects Replaced with "branching not available" message dyad-assistant
No field-level validation for paired fields Added aria-invalid and inline error messages github-actions
Self-hosted section lacks parent context when disconnected Added Supabase heading and description github-actions
Stale-read race in disconnect handler Use settings from hook instead of fresh IPC read devin-ai
Self-hosted controls disappear when config cleared Show UI when app is in self-hosted mode regardless of config chatgpt-codex
Hardcoded English strings in SupabaseConnector Added i18n translation keys dyad-assistant
Supabase CLI state files committed Added to .gitignore and removed from tracking dyad-assistant

CI Failures

  • mcp.spec.ts (2 failures): Pre-existing issue - getByRole('textbox', { name: 'Key' }) resolves to 3 elements. Unrelated to this PR's Supabase changes.
Files Changed
  • src/ipc/handlers/chat_stream_handlers.ts - Mode-specific credential check
  • src/supabase_admin/supabase_context.ts - JSON.stringify for URL
  • src/components/preview_panel/PreviewPanel.tsx - useEffect dependency fix
  • src/components/SupabaseConnector.tsx - Duplicate button, branch selector, i18n, self-hosted recovery
  • src/components/SupabaseIntegration.tsx - Field validation, parent context, race condition fix
  • src/i18n/locales/en/home.json - New translation keys
  • .gitignore - Supabase CLI state exclusions

Workflow run

@dyad-assistant
Copy link
Copy Markdown
Contributor

🤖 Claude Code Review Summary

PR Confidence: 3/5

All review comments addressed, but the MCP E2E test failures (pre-existing, unrelated) and missing i18n translations in pt-BR/zh-CN locales warrant attention before merge.

Unresolved Threads

No unresolved threads

Resolved Threads

Issue Rationale Link
Mode-specific credential check in chat streaming (3 threads) Fixed OR condition to check cloud vs self-hosted credentials based on app's effective mode View, View, View
Missing useEffect dependency for supabaseMode Added app?.supabaseMode to dependency array to prevent stale mode in edge log polling View
URL not JSON-escaped in generated client code Used JSON.stringify(projectUrl) consistently with publishableKey View
Duplicate disconnect buttons Removed redundant outline button per Principle #3: Intuitive But Power-User Friendly View
Empty branch selector for self-hosted Hidden with explanatory message per Principle #4: Transparent Over Magical View
No field-level validation for paired fields Added aria-invalid and inline errors per Principle #6: Delightful View
Self-hosted section orphaned when disconnected Added parent heading per Principle #4: Transparent Over Magical View
Stale-read race in disconnect handler Simplified to use hook state instead of async IPC read View
Self-hosted controls disappear when config cleared Show UI when app mode is self-hosted regardless of config state View
Hardcoded English strings Added i18n translation keys for self-hosted UI strings View
Supabase CLI state files committed Added to .gitignore and removed from tracking View
Product Principle Suggestions

No suggestions - principles were clear enough for all decisions made.


🤖 Generated by Claude Code

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4c7de05470

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const supabaseClientCode = await getSupabaseClientCode({
projectId: chat.app.supabaseProjectId,
organizationSlug: chat.app.supabaseOrganizationSlug ?? null,
mode: chat.app.supabaseMode,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Guard self-hosted token counting when config is missing

countTokens now passes chat.app.supabaseMode into the Supabase context helpers, but this path still runs for any chat with supabaseProjectId and does not check whether self-hosted settings are actually configured first. In the reachable case where an app is still linked in self-hosted mode after API URL/secret were cleared in Settings, getSupabaseClientCode throws (Self-hosted Supabase project URL is not configured), causing token counting to fail repeatedly while typing. Add the same mode-aware credential gate used in chat_stream_handlers before calling these helpers.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants