Skip to content

fix: update supabase settings and locale metadata#2889

Open
wwwillchen wants to merge 1 commit intodyad-sh:mainfrom
wwwillchen-bot:agent--1772652885338-1772651979
Open

fix: update supabase settings and locale metadata#2889
wwwillchen wants to merge 1 commit intodyad-sh:mainfrom
wwwillchen-bot:agent--1772652885338-1772651979

Conversation

@wwwillchen
Copy link
Collaborator

@wwwillchen wwwillchen commented Mar 4, 2026

(codex)


Open with Devin

@wwwillchen
Copy link
Collaborator Author

@BugBot run

@gemini-code-assist
Copy link
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 significantly enhances the Supabase integration by introducing support for self-hosted instances. Users can now provide custom API endpoints and secret keys, allowing for greater flexibility in how Supabase is connected and managed within the application. The changes include new UI elements for these settings, robust handling of sensitive credentials, and updates to the underlying API client to ensure seamless operation with both cloud and self-hosted Supabase environments.

Highlights

  • Self-Hosted Supabase Support: Introduced the ability to configure and use self-hosted Supabase instances by providing custom API URLs and secret keys.
  • User Interface Enhancements: Added new input fields and controls within the Supabase integration settings for entering and managing self-hosted API URL and secret key.
  • Credential Management and Validation: Implemented logic to store, retrieve, encrypt, and decrypt self-hosted Supabase credentials securely, ensuring that the API URL and secret key are always paired.
  • API Client Integration: Updated the Supabase management client to prioritize and utilize self-hosted credentials for all API calls when configured, falling back to standard authentication otherwise.
  • Internationalization Updates: Added new localization strings across multiple languages to support the new self-hosted Supabase settings.
Changelog
  • src/components/SupabaseIntegration.tsx
    • Updated Supabase integration component to include UI for self-hosted API URL and secret key, and refined disconnect logic.
    • Added state management for self-hosted API URL, secret key, saving status, and error handling.
    • Modified the 'Disconnect All' functionality to specifically clear Supabase organization and token data while preserving self-hosted settings.
  • src/i18n/locales/en/home.json
    • Added new localization strings for self-hosted Supabase settings, including labels, placeholders, descriptions, and error messages.
  • src/i18n/locales/pt-BR/home.json
    • Added new localization strings for self-hosted Supabase settings in Portuguese.
  • src/i18n/locales/zh-CN/home.json
    • Added new localization strings for self-hosted Supabase settings in Chinese.
  • src/ipc/handlers/app_handlers.ts
    • Updated credential check logic to include self-hosted Supabase credentials when determining if Supabase is connected.
  • src/lib/schemas.ts
    • Extended Supabase settings schema to support selfHostedSupabaseApiUrl and selfHostedSupabaseSecretKey.
    • Updated isSupabaseConnected function to check for the presence of self-hosted credentials as a valid connection state.
  • src/lib/settingsSearchIndex.ts
    • Added new SETTING_IDS and SETTINGS_SEARCH_INDEX entries for the self-hosted Supabase API URL and secret key to improve searchability within settings.
  • src/main/settings.ts
    • Introduced normalizeSupabaseOverrideSettings function to ensure that self-hosted API URL and secret key are always treated as a pair, clearing one if the other is missing.
    • Updated readSettings to apply normalization and decrypt self-hosted Supabase secret keys upon loading.
    • Updated writeSettings to apply normalization and encrypt self-hosted Supabase secret keys before saving.
  • src/supabase_admin/supabase_management_client.ts
    • Implemented helper functions (getSelfHostedSupabaseApiConfig, getSelfHostedSupabaseAccessToken, getSupabaseManagementApiUrl) to abstract self-hosted Supabase API configuration.
    • Modified all Supabase management API calls to dynamically use the self-hosted base URL and access token if configured, otherwise falling back to the default Supabase cloud API.
Activity
  • The pull request was authored by wwwillchen.
  • The pull request description includes a Devin AI review badge, suggesting AI assistance in its creation or review.
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. ↩

Copy link

@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: 24c34378c6

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 πŸ‘.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +62 to +63
const hasApiUrl = normalizedApiUrl !== "";
const hasSecret = normalizedSecretKey !== "";

Choose a reason for hiding this comment

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

P1 Badge Treat absent override fields as unset before normalization

normalizeSupabaseOverrideSettings computes hasApiUrl/hasSecret with normalizedX !== "", which evaluates to true when the value is undefined; for any existing user who has a supabase object but no self-hosted override, this makes hasSecret truthy and later dereferences supabase.selfHostedSupabaseSecretKey!.value, throwing at runtime during writeSettings. Because writeSettings catches and logs errors, this can silently block settings updates instead of persisting them.

Useful? React with πŸ‘Β / πŸ‘Ž.

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 ON, but it could not run because the branch was deleted or merged before autofix could start.

const normalizedSecretKey =
supabase.selfHostedSupabaseSecretKey?.value?.trim();
const hasApiUrl = normalizedApiUrl !== "";
const hasSecret = normalizedSecretKey !== "";
Copy link

Choose a reason for hiding this comment

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

Undefined comparison causes crash in normalization function

High Severity

normalizedApiUrl and normalizedSecretKey can be undefined (from optional chaining), and undefined !== "" evaluates to true in JavaScript. This means hasApiUrl and hasSecret are true even when the fields don't exist. In writeSettings, normalizeSupabaseOverrideSettings is called unconditionally when supabase exists, so for any user with a Supabase config but no self-hosted overrides, the code reaches supabase.selfHostedSupabaseSecretKey!.value = normalizedSecretKey! and throws a TypeError because selfHostedSupabaseSecretKey is undefined.

Additional Locations (1)

Fix in CursorΒ Fix in Web

showSuccess(t("integrations.supabase.selfHostedOverridesCleared"));
}
} else {
showError(t("integrations.supabase.failedDisconnect"));
Copy link

Choose a reason for hiding this comment

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

Wrong error message shown when saving overrides fails

Low Severity

The saveSelfHostedOverrides function shows t("integrations.supabase.failedDisconnect") ("Failed to disconnect from Supabase") when the save operation returns a falsy result. This is a copy-paste from the disconnect handler and displays a misleading error message for a save-override operation.

Fix in CursorΒ Fix in Web

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for self-hosted Supabase instances. My review identified several issues: a critical bug in the settings normalization logic that could cause a crash, multiple medium-severity security vulnerabilities related to URL path traversal due to unencoded variables in the Supabase management client, and medium-severity issues with error message handling in the UI. All identified comments are retained as they do not contradict any provided rules and highlight important areas for improvement.

Comment on lines +52 to +77
function normalizeSupabaseOverrideSettings(
supabase: Supabase | undefined,
): void {
if (!supabase) {
return;
}

const normalizedApiUrl = supabase.selfHostedSupabaseApiUrl?.trim();
const normalizedSecretKey =
supabase.selfHostedSupabaseSecretKey?.value?.trim();
const hasApiUrl = normalizedApiUrl !== "";
const hasSecret = normalizedSecretKey !== "";

if (hasApiUrl) {
supabase.selfHostedSupabaseApiUrl = normalizedApiUrl;
}

if (hasSecret) {
supabase.selfHostedSupabaseSecretKey!.value = normalizedSecretKey!;
}

if (hasApiUrl !== hasSecret) {
delete supabase.selfHostedSupabaseApiUrl;
supabase.selfHostedSupabaseSecretKey = undefined;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

There is a potential crash in normalizeSupabaseOverrideSettings. If selfHostedSupabaseSecretKey is undefined, normalizedSecretKey will also be undefined, causing hasSecret to evaluate to true (undefined !== ""). This can lead to a crash on line 70 when trying to access supabase.selfHostedSupabaseSecretKey!.value.

The logic can be simplified to fix this bug and improve readability. The refactored version ensures that both URL and key are treated as empty strings if they are missing, and then checks for their presence before attempting to assign or clear them.

function normalizeSupabaseOverrideSettings(
  supabase: Supabase | undefined,
): void {
  if (!supabase) {
    return;
  }

  const normalizedApiUrl = supabase.selfHostedSupabaseApiUrl?.trim() ?? "";
  const normalizedSecretKey =
    supabase.selfHostedSupabaseSecretKey?.value?.trim() ?? "";

  if (normalizedApiUrl && normalizedSecretKey) {
    supabase.selfHostedSupabaseApiUrl = normalizedApiUrl;
    supabase.selfHostedSupabaseSecretKey!.value = normalizedSecretKey;
  } else {
    // Ensure both are cleared if one is missing
    delete supabase.selfHostedSupabaseApiUrl;
    supabase.selfHostedSupabaseSecretKey = undefined;
  }
}


const response = await fetchWithRetry(
`https://api.supabase.com/v1/organizations/${organizationSlug}/members`,
getSupabaseManagementApiUrl(`/organizations/${organizationSlug}/members`),
Copy link
Contributor

Choose a reason for hiding this comment

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

security-medium medium

The organizationSlug variable is inserted directly into the URL path without being URL-encoded. If the slug contains special characters (like /, ?, or #), it could lead to an incorrect URL structure and potential path traversal within the API. It is recommended to use encodeURIComponent(organizationSlug) here.


const response = await fetchWithRetry(
`https://api.supabase.com/v1/organizations/${organizationSlug}`,
getSupabaseManagementApiUrl(`/organizations/${organizationSlug}`),
Copy link
Contributor

Choose a reason for hiding this comment

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

security-medium medium

The organizationSlug variable is inserted directly into the URL path without being URL-encoded. This can lead to malformed URLs or unintended endpoint access if the slug contains special characters. Use encodeURIComponent(organizationSlug) to ensure the URL is correctly constructed.

Comment on lines +634 to +636
const url = getSupabaseManagementApiUrl(
`/projects/${projectId}/analytics/endpoints/logs.all?sql=${encodedSql}&iso_timestamp_start=${isoTimestampStart}&iso_timestamp_end=${isoTimestampEnd}`,
);
Copy link
Contributor

Choose a reason for hiding this comment

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

security-medium medium

The projectId variable is used in the URL path without URL encoding. While other parameters like sqlQuery are correctly encoded, the projectId should also be wrapped in encodeURIComponent(projectId) to prevent path traversal or malformed URL issues if the ID contains special characters.


const response = await fetchWithRetry(
`https://api.supabase.com/v1/projects/${supabaseProjectId}/functions`,
getSupabaseManagementApiUrl(`/projects/${supabaseProjectId}/functions`),
Copy link
Contributor

Choose a reason for hiding this comment

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

security-medium medium

The supabaseProjectId is used in the URL path without encoding. In other parts of this file (e.g., lines 885 and 933), this same variable is correctly encoded using encodeURIComponent. For consistency and security, please use encodeURIComponent(supabaseProjectId) here as well.


const response = await fetchWithRetry(
`https://api.supabase.com/v1/projects/${supabaseProjectId}/branches`,
getSupabaseManagementApiUrl(`/projects/${supabaseProjectId}/branches`),
Copy link
Contributor

Choose a reason for hiding this comment

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

security-medium medium

The supabaseProjectId is inserted into the URL path without URL encoding. This inconsistency (compared to other encoded usages in the same file) should be addressed by using encodeURIComponent(supabaseProjectId) to prevent potential URL manipulation or errors with special characters.

showSuccess(t("integrations.supabase.selfHostedOverridesCleared"));
}
} else {
showError(t("integrations.supabase.failedDisconnect"));
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The error message t("integrations.supabase.failedDisconnect") is incorrect for this context, as this operation is saving settings, not disconnecting. This could confuse the user. A more appropriate message like "Failed to save override settings" should be used. For internationalization, you could add a new translation key.

Suggested change
showError(t("integrations.supabase.failedDisconnect"));
showError(t("integrations.supabase.failedSaveOverrides", "Failed to save override settings"));

Comment on lines +144 to +145
err.message ||
t("integrations.supabase.selfHostedOverridesPairRequired"),
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The fallback error message t("integrations.supabase.selfHostedOverridesPairRequired") in this catch block might not be accurate for all possible errors that could be caught here (e.g., network issues). A more generic fallback, such as "Failed to save override settings", would be more appropriate for unexpected errors.

Suggested change
err.message ||
t("integrations.supabase.selfHostedOverridesPairRequired"),
err.message ||
t("integrations.supabase.failedSaveOverrides", "Failed to save override settings")

Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

View 4 additional findings in Devin Review.

Open in Devin Review

Comment on lines +62 to +63
const hasApiUrl = normalizedApiUrl !== "";
const hasSecret = normalizedSecretKey !== "";
Copy link
Contributor

Choose a reason for hiding this comment

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

πŸ”΄ normalizeSupabaseOverrideSettings treats undefined as non-empty, causing crash on every writeSettings call for Supabase users

The comparisons normalizedApiUrl !== "" and normalizedSecretKey !== "" evaluate to true when the values are undefined (since undefined !== "" is true). This means hasApiUrl and hasSecret are both true even when neither field exists. The if (hasSecret) block then executes supabase.selfHostedSupabaseSecretKey!.value = normalizedSecretKey!, which at runtime is undefined.value = undefined β€” a TypeError.

In writeSettings (src/main/settings.ts:236-237), normalizeSupabaseOverrideSettings is called for ANY user with a supabase object (e.g., connected organizations), with no guard for whether self-hosted fields exist. The TypeError is caught by the outer try-catch (src/main/settings.ts:306), silently preventing the settings file from being written. This means all settings writes silently fail for existing Supabase users who haven't configured self-hosted overrides.

Fix: use Boolean() or explicit undefined checks

The hasApiUrl and hasSecret checks should be:

const hasApiUrl = Boolean(normalizedApiUrl);
const hasSecret = Boolean(normalizedSecretKey);

This correctly treats both undefined and "" as falsy.

Suggested change
const hasApiUrl = normalizedApiUrl !== "";
const hasSecret = normalizedSecretKey !== "";
const hasApiUrl = Boolean(normalizedApiUrl);
const hasSecret = Boolean(normalizedSecretKey);
Open in Devin Review

Was this helpful? React with πŸ‘ or πŸ‘Ž to provide feedback.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 4, 2026

Greptile Summary

This PR adds self-hosted Supabase Management API support with custom API URL and secret key configuration, along with i18n updates and schema/search indexing changes. The implementation has solid architecture in most places, but contains a critical bug in src/main/settings.ts that will crash the app for all existing Supabase OAuth users.

Key verified issues:

  1. Critical: Logic bug in normalizeSupabaseOverrideSettings (lines 62–63)

    • Undefined values are incorrectly compared with !== "", evaluating to true
    • Causes TypeError crash when writeSettings is called by any Supabase OAuth user
    • Also breaks mismatch detection, preventing validation of paired fields
    • Must be fixed before shipping
  2. UX: Wrong error message on self-hosted save failure (line 140 in SupabaseIntegration.tsx)

    • Uses failedDisconnect i18n key when saving overrides fails, confusing users
    • Should show a dedicated save error message

Well-implemented aspects:

  • supabase_management_client.ts: Correctly abstracts hardcoded API URLs into a helper function
  • schemas.ts, settingsSearchIndex.ts, app_handlers.ts: All correct and backward-compatible
  • UI component logic in SupabaseIntegration.tsx: Pair validation and dirty tracking are sound
  • Encryption/decryption flow: Structurally correct for the new credential field

Confidence Score: 1/5

  • Not safe to merge β€” a logic bug in normalizeSupabaseOverrideSettings will crash the app for all existing Supabase OAuth users on their next settings update.
  • The undefined !== "" comparison at lines 62–63 evaluates to true when fields are absent, causing line 70 to attempt property mutation on undefined, leading to TypeError. Since writeSettings calls this function unconditionally for any Supabase user, this will crash immediately upon any settings update by existing OAuth users (toggle migration setting, disconnect, etc.). This is a blocking defect that must be fixed before this PR can ship.
  • src/main/settings.ts β€” the normalizeSupabaseOverrideSettings function (lines 52–77) must have its undefined comparison logic fixed before merge.

Last reviewed commit: 24c3437

Comment on lines +62 to +63
const hasApiUrl = normalizedApiUrl !== "";
const hasSecret = normalizedSecretKey !== "";
Copy link
Contributor

Choose a reason for hiding this comment

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

Undefined comparison logic causes runtime crash and mismatch detection failure.

The boolean checks at lines 62–63 use !== "" to test field presence, but since optional-chaining returns undefined (not empty string) when a field is absent, both expressions evaluate to true even when the fields don't exist.

Problem 1: Crash on any settings update by Supabase OAuth users
When writeSettings (line 237) calls this normalization function, if the credential field is absent, the boolean still evaluates to true. This causes line 70 to attempt property mutation on an undefined object, crashing with TypeError: Cannot set properties of undefined.

Problem 2: Mismatch detection disabled
When only one field is configured, both booleans become true, so the mismatch check at line 73 never triggers, leaving incomplete/inconsistent state.

Fix: Use truthiness checks instead: Boolean(normalizedValue) instead of normalizedValue !== "". Also add a null check before attempting property mutation at line 70.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/main/settings.ts
Line: 62-63

Comment:
Undefined comparison logic causes runtime crash and mismatch detection failure.

The boolean checks at lines 62–63 use `!== ""` to test field presence, but since optional-chaining returns `undefined` (not empty string) when a field is absent, both expressions evaluate to `true` even when the fields don't exist.

**Problem 1: Crash on any settings update by Supabase OAuth users**
When `writeSettings` (line 237) calls this normalization function, if the credential field is absent, the boolean still evaluates to `true`. This causes line 70 to attempt property mutation on an `undefined` object, crashing with `TypeError: Cannot set properties of undefined`.

**Problem 2: Mismatch detection disabled**  
When only one field is configured, both booleans become `true`, so the mismatch check at line 73 never triggers, leaving incomplete/inconsistent state.

**Fix:** Use truthiness checks instead: `Boolean(normalizedValue)` instead of `normalizedValue !== ""`. Also add a null check before attempting property mutation at line 70.

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

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.

βœ… Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 9 files

Confidence score: 2/5

  • There is a high-confidence regression risk in src/main/settings.ts: the new presence checks can treat undefined as present and dereference selfHostedSupabaseSecretKey, which may break settings writes for affected configurations.
  • src/components/SupabaseIntegration.tsx has a user-facing consistency issue where Clear updates the UI before persistence succeeds, so failed saves can leave the form appearing cleared when data was not actually saved.
  • The score is lowered because both findings are concrete and user-impacting (one severe backend write-path failure risk, one state/persistence mismatch), so this is not yet a low-risk merge.
  • Pay close attention to src/main/settings.ts, src/components/SupabaseIntegration.tsx - prevent undefined dereference in settings writes and align UI clearing with persistence outcomes.
Prompt for AI agents (unresolved issues)

Check if these issues are valid β€” if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/main/settings.ts">

<violation number="1" location="src/main/settings.ts:62">
P1: The new presence checks treat `undefined` as present, which can dereference `selfHostedSupabaseSecretKey` when it is missing and break settings writes.</violation>
</file>

<file name="src/components/SupabaseIntegration.tsx">

<violation number="1" location="src/components/SupabaseIntegration.tsx:175">
P2: Clear action updates the UI optimistically before persistence, which can leave inputs showing cleared values even when saving fails.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment on lines +62 to +63
const hasApiUrl = normalizedApiUrl !== "";
const hasSecret = normalizedSecretKey !== "";
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 4, 2026

Choose a reason for hiding this comment

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

P1: The new presence checks treat undefined as present, which can dereference selfHostedSupabaseSecretKey when it is missing and break settings writes.

Prompt for AI agents
Check if this issue is valid β€” if so, understand the root cause and fix it. At src/main/settings.ts, line 62:

<comment>The new presence checks treat `undefined` as present, which can dereference `selfHostedSupabaseSecretKey` when it is missing and break settings writes.</comment>

<file context>
@@ -48,6 +49,33 @@ const DEFAULT_SETTINGS: UserSettings = {
+  const normalizedApiUrl = supabase.selfHostedSupabaseApiUrl?.trim();
+  const normalizedSecretKey =
+    supabase.selfHostedSupabaseSecretKey?.value?.trim();
+  const hasApiUrl = normalizedApiUrl !== "";
+  const hasSecret = normalizedSecretKey !== "";
+
</file context>
Suggested change
const hasApiUrl = normalizedApiUrl !== "";
const hasSecret = normalizedSecretKey !== "";
const hasApiUrl = normalizedApiUrl !== undefined && normalizedApiUrl !== "";
const hasSecret = normalizedSecretKey !== undefined && normalizedSecretKey !== "";
Fix with Cubic

Comment on lines +175 to +177
setSelfHostedApiUrlInput("");
setSelfHostedSecretKeyInput("");
await saveSelfHostedOverrides("", "");
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 4, 2026

Choose a reason for hiding this comment

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

P2: Clear action updates the UI optimistically before persistence, which can leave inputs showing cleared values even when saving fails.

Prompt for AI agents
Check if this issue is valid β€” if so, understand the root cause and fix it. At src/components/SupabaseIntegration.tsx, line 175:

<comment>Clear action updates the UI optimistically before persistence, which can leave inputs showing cleared values even when saving fails.</comment>

<file context>
@@ -15,42 +14,169 @@ import { useSettings } from "@/hooks/useSettings";
+      return;
+    }
+
+    setSelfHostedApiUrlInput("");
+    setSelfHostedSecretKeyInput("");
+    await saveSelfHostedOverrides("", "");
</file context>
Suggested change
setSelfHostedApiUrlInput("");
setSelfHostedSecretKeyInput("");
await saveSelfHostedOverrides("", "");
await saveSelfHostedOverrides("", "");
Fix with Cubic

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Multi-agent review: 3 issue(s) found (1 HIGH, 2 MEDIUM)

const normalizedSecretKey =
supabase.selfHostedSupabaseSecretKey?.value?.trim();
const hasApiUrl = normalizedApiUrl !== "";
const hasSecret = normalizedSecretKey !== "";
Copy link
Contributor

Choose a reason for hiding this comment

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

πŸ”΄ HIGH | logic / crash

normalizeSupabaseOverrideSettings has undefined !== "" bug that causes crashes

When either field is absent (undefined), the check normalizedApiUrl !== "" evaluates to true because undefined !== "" is true in JavaScript. This means hasApiUrl and hasSecret are both true even when the fields don't exist.

Concrete crash scenario: if only the API URL is saved but the secret key is absent, hasSecret is true, so the code executes supabase.selfHostedSupabaseSecretKey!.value = normalizedSecretKey! β€” which tries to set .value on undefined, throwing a TypeError.

Additionally, because hasApiUrl !== hasSecret will be false when both are undefined (both evaluate to true), the cleanup branch that should delete incomplete pairs never fires.

πŸ’‘ Suggestion: Use Boolean() checks:

const hasApiUrl = Boolean(normalizedApiUrl);
const hasSecret = Boolean(normalizedSecretKey);

This correctly treats both undefined and empty string as absent.

showError(
err.message ||
t("integrations.supabase.selfHostedOverridesPairRequired"),
);
Copy link
Contributor

Choose a reason for hiding this comment

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

🟑 MEDIUM | error-states

Wrong fallback error message in catch block

When saveSelfHostedOverrides throws an unexpected exception (e.g. network/persistence failure), the catch block falls back to t('integrations.supabase.selfHostedOverridesPairRequired') β€” a validation message ("Please provide both the API URL and secret key"). A user who hits a backend error will see a misleading message suggesting they need to fix their input.

πŸ’‘ Suggestion: Use a generic save-failure message as the fallback, or add a new i18n key like selfHostedOverridesSaveFailed.

{(overrideError ||
(hasSelfHostedOverrideInput && !hasValidSelfHostedOverridePair)) && (
<p className="text-xs text-red-600 dark:text-red-400">
{overrideError ||
Copy link
Contributor

Choose a reason for hiding this comment

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

🟑 MEDIUM | error-states / UX

Inline validation error shown before user finishes typing

The error paragraph renders whenever hasSelfHostedOverrideInput && !hasValidSelfHostedOverridePair is true, which fires the moment the user starts typing in the first field β€” before they've had a chance to fill in the second. This creates a jarring premature error message.

πŸ’‘ Suggestion: Only show this inline error after the user has explicitly attempted to save (rely on the overrideError state set on submit), or introduce a touched/blurred flag so the error only appears after interacting with both fields.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 4, 2026

πŸ” Dyadbot Code Review Summary

Verdict: β›” NO - Do NOT merge

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

Issues Summary

Severity File Issue
πŸ”΄ HIGH src/main/settings.ts:62-63 normalizeSupabaseOverrideSettings has undefined !== "" bug β€” crashes when only one of the pair fields is set
🟑 MEDIUM src/components/SupabaseIntegration.tsx:146 Wrong fallback error message in catch block (shows validation msg instead of save-failure msg)
🟑 MEDIUM src/components/SupabaseIntegration.tsx:281 Inline validation error shown prematurely before user fills both fields
🟒 Low Priority Notes (2 items)
  • No show/hide toggle for secret key input β€” src/components/SupabaseIntegration.tsx:264 β€” Users can't verify a pasted service key before saving
  • Clear button has no loading indicator β€” src/components/SupabaseIntegration.tsx:297 β€” Unlike Save button which shows "Saving...", Clear gives no visual feedback during async operation
🚫 Dropped False Positives (7 items)
  • normalizeSupabaseOverrideSettings called before decryption β€” Dropped: The encrypted value is overwritten by decryption afterward; normalization on encrypted ciphertext is harmless in practice
  • Multiple readSettings calls / TOCTOU β€” Dropped: Settings file reads are fast in Electron; theoretical TOCTOU between reads in same async op is extremely unlikely
  • getSupabaseClientForOrganization ignores organizationSlug β€” Dropped: This is intentional behavior β€” self-hosted config IS the single override for all orgs by design
  • Asymmetry between delete and undefined assignment β€” Dropped: JSON.stringify omits undefined values, so both approaches produce identical serialized output
  • getSelfHostedSupabaseAccessToken unnecessary wrapper β€” Dropped: Minor code organization preference, not a real issue
  • Pair validation logic duplicated β€” Dropped: Defense-in-depth across UI handler and backend normalization is acceptable
  • useEffect double re-render on clear β€” Dropped: Functionally correct, no visible user impact

Generated by Dyadbot multi-agent code review

@github-actions github-actions bot added the needs-human:review-issue ai agent flagged an issue that requires human review label Mar 4, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 4, 2026

🎭 Playwright Test Results

❌ Some tests failed

OS Passed Failed Flaky Skipped
🍎 macOS 235 4 8 6

Summary: 235 passed, 4 failed, 8 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 2 elements:
  • mcp.spec.ts > mcp - call calculator via http
    • Error: locator.fill: Error: strict mode violation: getByRole('textbox', { name: 'Key' }) resolved to 2 elements:
  • supabase_branch.spec.ts > supabase branch selection works
    • TimeoutError: locator.click: Timeout 30000ms exceeded.
  • supabase_stale_ui.spec.ts > supabase - stale ui
    • Error: expect(locator).toMatchAriaSnapshot(expected) failed

πŸ“‹ Re-run Failing Tests (macOS)

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

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

⚠️ Flaky Tests

🍎 macOS

  • copy_app.spec.ts > copy app with history (passed after 1 retry)
  • local_agent_code_search.spec.ts > local-agent - code search (passed after 1 retry)
  • select_component.spec.ts > upgrade app to select component (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)
  • switch_versions.spec.ts > switch versions (isomorphic git) (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)

πŸ“Š View full report

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

Labels

needs-human:review-issue ai agent flagged an issue that requires human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant