Unverified 提交 a4775c39 authored 作者: wwwillchen-bot's avatar wwwillchen-bot 提交者: GitHub

fix: handle Supabase 403 error when listing branches for free tier users (#2527)

## Summary - Handle 403 Forbidden errors gracefully when listing Supabase branches - Return empty array instead of throwing error for free tier users without branch access - Allows free tier users to continue using other Supabase features without error toasts Fixes #2525 ## Test plan - Verified all 784 unit tests pass - Existing E2E test (`supabase_branch.spec.ts`) continues to work for paid tier (test mode returns mock data) - Free tier users should now see no branches available instead of an error toast 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- devin-review-badge-begin --> --- <a href="https://app.devin.ai/review/dyad-sh/dyad/pull/2527" target="_blank"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1"> <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin"> </picture> </a> <!-- devin-review-badge-end --> <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Show a clear inline message when Supabase branch listing returns 403 for free‑tier projects, and suppress error toasts so users can keep using other Supabase features (addresses #2525). - **Bug Fixes** - Detect 403 from branches API and show “Branches are only supported for Supabase paid customers” in the UI; no toast. - Removed toast meta from the branches query and added getErrorMessage to clean up IPC-wrapped errors. - Other statuses keep existing handling; paid tier behavior unchanged. <sup>Written for commit eb414f84c5c4ff44a4f6f152aae5c395903430b3. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. --> --------- Co-authored-by: 's avatarWill Chen <willchen90@gmail.com> Co-authored-by: 's avatarClaude Opus 4.5 <noreply@anthropic.com>
上级 58451425
差异被折叠。
......@@ -37,7 +37,9 @@ import connectSupabaseDark from "../../assets/supabase/connect-supabase-dark.svg
// @ts-ignore
import connectSupabaseLight from "../../assets/supabase/connect-supabase-light.svg";
import { ExternalLink, Plus, RefreshCw, Trash2 } from "lucide-react";
import { ExternalLink, Info, Plus, RefreshCw, Trash2 } from "lucide-react";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { getErrorMessage } from "@/lib/errors";
import {
Tooltip,
TooltipTrigger,
......@@ -66,6 +68,7 @@ export function SupabaseConnector({ appId }: { appId: number }) {
isFetchingProjects,
projectsError,
isLoadingBranches,
branchesError,
isSettingAppProject,
refetchOrganizations,
refetchProjects,
......@@ -205,49 +208,58 @@ export function SupabaseConnector({ appId }: { appId: number }) {
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="supabase-branch-select">Database Branch</Label>
<Select
value={app.supabaseProjectId || ""}
onValueChange={async (supabaseBranchProjectId) => {
try {
const branch = branches.find(
(b) => b.projectRef === supabaseBranchProjectId,
);
if (!branch) {
throw new Error("Branch not found");
{branchesError ? (
<Alert>
<Info className="h-4 w-4" />
<AlertDescription>
{getErrorMessage(branchesError)}
</AlertDescription>
</Alert>
) : (
<Select
value={app.supabaseProjectId || ""}
onValueChange={async (supabaseBranchProjectId) => {
try {
const branch = branches.find(
(b) => b.projectRef === supabaseBranchProjectId,
);
if (!branch) {
throw new Error("Branch not found");
}
// Keep the same organizationSlug from the app
await setAppProject({
projectId: branch.projectRef,
parentProjectId: branch.parentProjectRef,
appId,
organizationSlug: app.supabaseOrganizationSlug,
});
toast.success("Branch selected");
await refreshApp();
} catch (error) {
toast.error("Failed to set branch: " + error);
}
// Keep the same organizationSlug from the app
await setAppProject({
projectId: branch.projectRef,
parentProjectId: branch.parentProjectRef,
appId,
organizationSlug: app.supabaseOrganizationSlug,
});
toast.success("Branch selected");
await refreshApp();
} catch (error) {
toast.error("Failed to set branch: " + error);
}
}}
disabled={isLoadingBranches || isSettingAppProject}
>
<SelectTrigger
id="supabase-branch-select"
data-testid="supabase-branch-select"
}}
disabled={isLoadingBranches || isSettingAppProject}
>
<SelectValue placeholder="Select a branch" />
</SelectTrigger>
<SelectContent>
{branches.map((branch) => (
<SelectItem
key={branch.projectRef}
value={branch.projectRef}
>
{branch.name}
{branch.isDefault && " (Default)"}
</SelectItem>
))}
</SelectContent>
</Select>
<SelectTrigger
id="supabase-branch-select"
data-testid="supabase-branch-select"
>
<SelectValue placeholder="Select a branch" />
</SelectTrigger>
<SelectContent>
{branches.map((branch) => (
<SelectItem
key={branch.projectRef}
value={branch.projectRef}
>
{branch.name}
{branch.isDefault && " (Default)"}
</SelectItem>
))}
</SelectContent>
</Select>
)}
</div>
<Button variant="destructive" onClick={handleUnsetProject}>
......
......@@ -103,7 +103,6 @@ export function useSupabase(options: UseSupabaseOptions = {}) {
return Array.isArray(list) ? list : [];
},
enabled: !!branchesProjectId,
meta: { showErrorToast: true },
});
// Mutation: Load edge function logs for a Supabase project
......
/**
* Extracts a user-friendly error message from an Error object.
*
* Electron IPC errors are wrapped as:
* "Error invoking remote method '<channel>': Error: <actual message>"
*
* This strips the IPC wrapper and returns just the meaningful message.
*/
export function getErrorMessage(error: unknown): string {
const raw =
error instanceof Error ? error.message : String(error ?? "Unknown error");
return raw.replace(/^Error invoking remote method '.*?':\s*Error:\s*/, "");
}
......@@ -704,6 +704,14 @@ export async function listSupabaseBranches({
`List Supabase branches for ${supabaseProjectId}`,
);
if (response.status === 403) {
// 403 Forbidden means the user doesn't have access to branches (e.g., free tier)
logger.info(
`Branches not available for project ${supabaseProjectId} (403 Forbidden - likely free tier)`,
);
throw new Error("Branches are only supported for Supabase paid customers");
}
if (response.status !== 200) {
throw await createResponseError(response, "list branches");
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论