From 9f8cc1b43890840402874fbb4866981a0d334dba Mon Sep 17 00:00:00 2001 From: Thomas <31189692+ecktoteckto@users.noreply.github.com> Date: Tue, 24 Jun 2025 04:24:44 +0200 Subject: [PATCH 1/5] Show credit balance also for partner billed organizations. * When a Vercel customer downgrades in the middle of the billing cycle the get credits (no refund to card). To make that clearer, they need to be able to see their balance. --- .../interfaces/Organization/BillingSettings/BillingSettings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/BillingSettings.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/BillingSettings.tsx index 445b2c2ba7bd1..6a942301803c2 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/BillingSettings.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/BillingSettings.tsx @@ -82,7 +82,7 @@ export const BillingSettings = () => { )} - {isBillingCreditsEnabledOnProfileLevel && isNotOrgWithPartnerBilling && ( + {isBillingCreditsEnabledOnProfileLevel && ( <> From 3d5870f3426a6e1f368453cdf9326cdb2a74179c Mon Sep 17 00:00:00 2001 From: Stojan Dimitrovski Date: Tue, 24 Jun 2025 04:48:00 +0200 Subject: [PATCH 2/5] fix: api docs with new api keys (#36531) * fix: api docs with new api keys * Smol UI refactor --------- Co-authored-by: Joshen Lim --- .../interfaces/Docs/LangSelector.tsx | 147 ++++++++++++------ .../data/config/project-settings-v2-query.ts | 3 + apps/studio/pages/project/[ref]/api/index.tsx | 18 ++- 3 files changed, 114 insertions(+), 54 deletions(-) diff --git a/apps/studio/components/interfaces/Docs/LangSelector.tsx b/apps/studio/components/interfaces/Docs/LangSelector.tsx index b523ded9d5312..341071c2b7ecb 100644 --- a/apps/studio/components/interfaces/Docs/LangSelector.tsx +++ b/apps/studio/components/interfaces/Docs/LangSelector.tsx @@ -1,15 +1,18 @@ -import { PermissionAction } from '@supabase/shared-types/out/constants' import { Key } from 'lucide-react' +import { useMemo } from 'react' import { useParams } from 'common' import type { showApiKey } from 'components/interfaces/Docs/Docs.types' -import { getAPIKeys, useProjectSettingsV2Query } from 'data/config/project-settings-v2-query' -import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' +import { useAPIKeysQuery } from 'data/api-keys/api-keys-query' import { Button, DropdownMenu, DropdownMenuContent, - DropdownMenuItem, + DropdownMenuGroup, + DropdownMenuLabel, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, DropdownMenuTrigger, } from 'ui' @@ -17,25 +20,30 @@ const DEFAULT_KEY = { name: 'hide', key: 'SUPABASE_KEY' } interface LangSelectorProps { selectedLang: string - showApiKey: showApiKey + selectedApiKey: showApiKey setSelectedLang: (selectedLang: string) => void - setShowApiKey: (showApiKey: showApiKey) => void + setSelectedApiKey: (showApiKey: showApiKey) => void } const LangSelector = ({ selectedLang, - showApiKey, + selectedApiKey, setSelectedLang, - setShowApiKey, + setSelectedApiKey, }: LangSelectorProps) => { const { ref: projectRef } = useParams() - const canReadServiceKey = useCheckPermissions( - PermissionAction.READ, - 'service_api_keys.service_role_key' - ) - const { data: settings } = useProjectSettingsV2Query({ projectRef }) - const { anonKey: anonApiKey, serviceKey: serviceApiKey } = getAPIKeys(settings) + const { data: apiKeys = [], isLoading: isLoadingAPIKeys } = useAPIKeysQuery({ + projectRef, + reveal: false, + }) + + const legacyKeys = useMemo(() => apiKeys.filter(({ type }) => type === 'legacy'), [apiKeys]) + const publishableKeys = useMemo( + () => apiKeys.filter(({ type }) => type === 'publishable'), + [apiKeys] + ) + const secretKeys = useMemo(() => apiKeys.filter(({ type }) => type === 'secret'), [apiKeys]) return (
@@ -62,48 +70,93 @@ const LangSelector = ({ > Bash - {selectedLang == 'bash' && ( -
+ {selectedLang == 'bash' && !isLoadingAPIKeys && apiKeys && apiKeys.length > 0 && ( +
- Project API key : + Project API key:
- + - <> - setShowApiKey(DEFAULT_KEY)}> - hide - - {anonApiKey && ( - - setShowApiKey({ - key: anonApiKey.api_key ?? '-', - name: 'anon (public)', - }) - } - > -

anon (public)

-
+ + setSelectedApiKey(DEFAULT_KEY)} + > + Hide keys + + + {publishableKeys.length > 0 && ( + <> + + Publishable keys + {publishableKeys.map((key) => { + const value = key.api_key + return ( + + setSelectedApiKey({ + name: `Publishable key: ${key.name}`, + key: value, + }) + } + > + {key.name} + + ) + })} + )} - {canReadServiceKey && ( - - setShowApiKey({ - key: serviceApiKey?.api_key ?? '-', - name: 'service_role (secret)', - }) - } - > -

service_role (secret)

-
+ + {secretKeys.length > 0 && ( + <> + + Secret keys + {secretKeys.map((key) => { + const value = key.prefix + '...' + return ( + + setSelectedApiKey({ name: `Secret key: ${key.name}`, key: value }) + } + > + {key.name} + + ) + })} + )} - + + + + + JWT-based legacy keys + {legacyKeys.map((key) => { + const value = key.api_key + return ( + + setSelectedApiKey({ name: `Legacy key: ${key.name}`, key: value }) + } + > + {key.name} + + ) + })} + +
diff --git a/apps/studio/data/config/project-settings-v2-query.ts b/apps/studio/data/config/project-settings-v2-query.ts index b76d50f85f945..9b8d65e071d41 100644 --- a/apps/studio/data/config/project-settings-v2-query.ts +++ b/apps/studio/data/config/project-settings-v2-query.ts @@ -63,6 +63,9 @@ export const useProjectSettingsV2Query = ( ) } +/** + * @deprecated Use api-keys-query instead! + */ export const getAPIKeys = (settings?: ProjectSettings) => { const anonKey = (settings?.service_api_keys ?? []).find((x) => x.tags === 'anon') const serviceKey = (settings?.service_api_keys ?? []).find((x) => x.tags === 'service_role') diff --git a/apps/studio/pages/project/[ref]/api/index.tsx b/apps/studio/pages/project/[ref]/api/index.tsx index 9e34e08a2b7d0..b324749bdb1bd 100644 --- a/apps/studio/pages/project/[ref]/api/index.tsx +++ b/apps/studio/pages/project/[ref]/api/index.tsx @@ -3,13 +3,13 @@ import { useState } from 'react' import { GeneralContent, ResourceContent, RpcContent } from 'components/interfaces/Docs' import LangSelector from 'components/interfaces/Docs/LangSelector' +import DefaultLayout from 'components/layouts/DefaultLayout' import DocsLayout from 'components/layouts/DocsLayout/DocsLayout' import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query' import { useCustomDomainsQuery } from 'data/custom-domains/custom-domains-query' import { useProjectJsonSchemaQuery } from 'data/docs/project-json-schema-query' import { snakeToCamel } from 'lib/helpers' import type { NextPageWithLayout } from 'types' -import DefaultLayout from 'components/layouts/DefaultLayout' const PageConfig: NextPageWithLayout = () => { return @@ -29,7 +29,7 @@ const DocView = () => { const { ref: projectRef, page, resource, rpc } = useParams() const [selectedLang, setSelectedLang] = useState('js') - const [showApiKey, setShowApiKey] = useState(DEFAULT_KEY) + const [selectedApikey, setSelectedApiKey] = useState(DEFAULT_KEY) const { data: settings, error: settingsError } = useProjectSettingsV2Query({ projectRef }) const { @@ -112,9 +112,9 @@ const DocView = () => {
@@ -124,7 +124,7 @@ const DocView = () => { selectedLang={selectedLang} resourceId={resource} resources={resources} - showApiKey={showApiKey.key} + showApiKey={selectedApikey.key} refreshDocs={refreshDocs} /> ) : rpc ? ( @@ -133,11 +133,15 @@ const DocView = () => { rpcId={rpc} paths={paths} rpcs={rpcs} - showApiKey={showApiKey.key} + showApiKey={selectedApikey.key} refreshDocs={refreshDocs} /> ) : ( - + )}
From ae9ce3d01015e64815b5b8013dde889e830aec7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kevin=20Gr=C3=BCneberg?= Date: Tue, 24 Jun 2025 11:23:56 +0800 Subject: [PATCH 3/5] fix: payment method selection (#36623) --- .../Subscription/SubscriptionPlanUpdateDialog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx index 1fa2c2d6af279..5dafba5472326 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx @@ -554,7 +554,7 @@ export const SubscriptionPlanUpdateDialog = ({ {}} + onSelectPaymentMethod={(pm) => setSelectedPaymentMethod(pm)} createPaymentMethodInline={ subscriptionPreview.pending_subscription_flow === true } From 4e3264dc45d11a8a8e795e1d170a3c2758a6e6db Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Tue, 24 Jun 2025 01:05:10 -0230 Subject: [PATCH 4/5] Update nextjs logo (#36620) * Update nextjs logo * Update link for Next.js subscription and Auth example --------- Co-authored-by: Joshen Lim --- apps/studio/components/interfaces/Home/Home.constants.ts | 2 +- apps/studio/public/img/libraries/nextjs-dark-icon.svg | 4 +--- apps/studio/public/img/libraries/nextjs-icon.svg | 4 +--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/apps/studio/components/interfaces/Home/Home.constants.ts b/apps/studio/components/interfaces/Home/Home.constants.ts index dc3aa38e38750..46181f40fdcc9 100644 --- a/apps/studio/components/interfaces/Home/Home.constants.ts +++ b/apps/studio/components/interfaces/Home/Home.constants.ts @@ -63,7 +63,7 @@ export const EXAMPLE_PROJECTS = [ framework: 'nextjs', title: 'Next.js Subscription and Auth', description: 'The all-in-one starter kit for high-performance SaaS applications.', - url: 'https://github.com/vercel/nextjs-subscription-payments', + url: 'https://github.com/nextjs/saas-starter', type: 'app', }, { diff --git a/apps/studio/public/img/libraries/nextjs-dark-icon.svg b/apps/studio/public/img/libraries/nextjs-dark-icon.svg index 99afa3413d30f..60ef95ea6b075 100644 --- a/apps/studio/public/img/libraries/nextjs-dark-icon.svg +++ b/apps/studio/public/img/libraries/nextjs-dark-icon.svg @@ -1,3 +1 @@ - - - + \ No newline at end of file diff --git a/apps/studio/public/img/libraries/nextjs-icon.svg b/apps/studio/public/img/libraries/nextjs-icon.svg index c45d6e63fb907..60ef95ea6b075 100644 --- a/apps/studio/public/img/libraries/nextjs-icon.svg +++ b/apps/studio/public/img/libraries/nextjs-icon.svg @@ -1,3 +1 @@ - - - + \ No newline at end of file From 8c8110a34cd3cba51b5b4a5d10e1650b2c79e617 Mon Sep 17 00:00:00 2001 From: Alaister Young Date: Tue, 24 Jun 2025 12:01:33 +0800 Subject: [PATCH 5/5] chore: disable role impersonation for edge functions (#36599) * chore: disable role impersonation for edge functions * allow service role and anon --- .../EdgeFunctionTesterSheet.tsx | 12 +++++- .../RoleImpersonationPopover.tsx | 7 +++- .../RoleImpersonationSelector.tsx | 39 ++++++++++++------- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionTesterSheet.tsx b/apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionTesterSheet.tsx index 11cd1dd9e04c1..7c65503db22ec 100644 --- a/apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionTesterSheet.tsx +++ b/apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionTesterSheet.tsx @@ -15,7 +15,10 @@ import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' import { IS_PLATFORM } from 'lib/constants' import { prettifyJSON } from 'lib/helpers' import { getRoleImpersonationJWT } from 'lib/role-impersonation' -import { useGetImpersonatedRoleState } from 'state/role-impersonation-state' +import { + RoleImpersonationStateContextProvider, + useGetImpersonatedRoleState, +} from 'state/role-impersonation-state' import { Badge, Button, @@ -416,7 +419,12 @@ export const EdgeFunctionTesterSheet = ({ visible, onClose }: EdgeFunctionTester
- + {/* [Alaister]: We're using a fresh context here as edge functions don't allow impersonating users. */} + + +