From 7aa111b863530149f6aaa62b368c2bb29df25599 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 23 Jun 2025 13:47:35 +0800 Subject: [PATCH 1/4] Chore/misc fixes 200625 (#36545) * Fix database functions editor not resetting maximized state when panel is closed * Small update to copy in compute costs confirmation modal --- .../interfaces/Database/Functions/CreateFunction/index.tsx | 1 + apps/studio/pages/new/[slug].tsx | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/studio/components/interfaces/Database/Functions/CreateFunction/index.tsx b/apps/studio/components/interfaces/Database/Functions/CreateFunction/index.tsx index d32e9aaae95af..fd966494bdfc6 100644 --- a/apps/studio/components/interfaces/Database/Functions/CreateFunction/index.tsx +++ b/apps/studio/components/interfaces/Database/Functions/CreateFunction/index.tsx @@ -134,6 +134,7 @@ const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => { useEffect(() => { if (visible) { + setFocusedEditor(false) form.reset({ name: func?.name ?? '', schema: func?.schema ?? 'public', diff --git a/apps/studio/pages/new/[slug].tsx b/apps/studio/pages/new/[slug].tsx index 91039295485a0..ce537860dca93 100644 --- a/apps/studio/pages/new/[slug].tsx +++ b/apps/studio/pages/new/[slug].tsx @@ -951,8 +951,8 @@ const Wizard: NextPageWithLayout = () => { size="large" loading={false} visible={isComputeCostsConfirmationModalVisible} - title={<>Confirm compute costs} - confirmLabel="Confirm" + title="Confirm compute costs" + confirmLabel="I understand" onCancel={() => setIsComputeCostsConfirmationModalVisible(false)} onConfirm={async () => { const values = form.getValues() @@ -965,7 +965,7 @@ const Wizard: NextPageWithLayout = () => {

Launching a project on compute size "{instanceLabel(instanceSize)}" increases your monthly costs by ${additionalMonthlySpend}, independent of how actively you use it. By - clicking "Confirm", you agree to the additional costs.{' '} + clicking "I understand", you agree to the additional costs.{' '} Date: Mon, 23 Jun 2025 13:58:12 +0800 Subject: [PATCH 2/4] Update warnings in bucket modals (#36547) --- .../interfaces/Storage/CreateBucketModal.tsx | 22 ++++++--- .../interfaces/Storage/EditBucketModal.tsx | 49 +++++++++++++------ 2 files changed, 48 insertions(+), 23 deletions(-) diff --git a/apps/studio/components/interfaces/Storage/CreateBucketModal.tsx b/apps/studio/components/interfaces/Storage/CreateBucketModal.tsx index f0c41e6e9513c..552e59f16e8c8 100644 --- a/apps/studio/components/interfaces/Storage/CreateBucketModal.tsx +++ b/apps/studio/components/interfaces/Storage/CreateBucketModal.tsx @@ -13,7 +13,8 @@ import { import { useProjectStorageConfigQuery } from 'data/config/project-storage-config-query' import { useBucketCreateMutation } from 'data/storage/bucket-create-mutation' import { IS_PLATFORM } from 'lib/constants' -import { Alert, Button, Collapsible, Form, Input, Listbox, Modal, Toggle, cn } from 'ui' +import { Button, Collapsible, Form, Input, Listbox, Modal, Toggle, cn } from 'ui' +import { Admonition } from 'ui-patterns' export interface CreateBucketModalProps { visible: boolean @@ -115,29 +116,36 @@ const CreateBucketModal = ({ visible, onClose }: CreateBucketModalProps) => { onSubmit={onSubmit} > {({ values }: { values: any }) => { + const isPublicBucket = values.public + return ( <> - + -

+
- {values.public && ( - + {isPublicBucket && ( +

Users can read objects in public buckets without any authorization.

@@ -145,7 +153,7 @@ const CreateBucketModal = ({ visible, onClose }: CreateBucketModalProps) => { Row level security (RLS) policies are still required for other operations such as object uploads and deletes.

-
+ )}
diff --git a/apps/studio/components/interfaces/Storage/EditBucketModal.tsx b/apps/studio/components/interfaces/Storage/EditBucketModal.tsx index 53815b39e6399..aff15a75ef7ed 100644 --- a/apps/studio/components/interfaces/Storage/EditBucketModal.tsx +++ b/apps/studio/components/interfaces/Storage/EditBucketModal.tsx @@ -3,16 +3,18 @@ import { ChevronDown } from 'lucide-react' import Link from 'next/link' import { useEffect, useState } from 'react' import { toast } from 'sonner' -import { Alert, Button, Collapsible, Form, Input, Listbox, Modal, Toggle, cn } from 'ui' +import { Button, Collapsible, Form, Input, Listbox, Modal, Toggle, cn } from 'ui' import { StorageSizeUnits } from 'components/to-be-cleaned/Storage/StorageSettings/StorageSettings.constants' import { convertFromBytes, convertToBytes, } from 'components/to-be-cleaned/Storage/StorageSettings/StorageSettings.utils' +import { InlineLink } from 'components/ui/InlineLink' import { useProjectStorageConfigQuery } from 'data/config/project-storage-config-query' import { useBucketUpdateMutation } from 'data/storage/bucket-update-mutation' import { IS_PLATFORM } from 'lib/constants' +import { Admonition } from 'ui-patterns' import type { StorageBucket } from './Storage.types' export interface EditBucketModalProps { @@ -84,6 +86,10 @@ const EditBucketModal = ({ visible, bucket, onClose }: EditBucketModalProps) => >
{({ values, resetForm }: { values: any; resetForm: any }) => { + const isChangingBucketVisibility = bucket?.public !== values.public + const isMakingBucketPrivate = bucket?.public && !values.public + const isMakingBucketPublic = !bucket?.public && values.public + // [Alaister] although this "technically" is breaking the rules of React hooks // it won't error because the hooks are always rendered in the same order // eslint-disable-next-line react-hooks/rules-of-hooks @@ -106,45 +112,56 @@ const EditBucketModal = ({ visible, bucket, onClose }: EditBucketModalProps) => return ( <> - + -
+
- {bucket?.public !== values.public && ( - -

- {!bucket?.public && values.public - ? `This will make all objects in the bucket "${bucket?.name}" public` - : bucket?.public && !values.public - ? `All objects in "${bucket?.name}" will be made private and will only be accessible via signed URLs or downloaded with the right authorisation headers` +

+ {isMakingBucketPublic + ? `This will make all objects in your bucket publicly accessible.` + : isMakingBucketPrivate + ? `All objects in your bucket will be private and only accessible via signed URLs, or downloaded with the right authorisation headers.` : ''}

-
+ {isMakingBucketPrivate && ( +

+ Assets cached in the CDN may still be publicly accessible. You can + consider{' '} + + purging the cache + {' '} + or moving your assets to a new bucket. +

+ )} + )}
From 04c5484bcdc4d9af5c615489b077f6e1c922d16f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kevin=20Gr=C3=BCneberg?= Date: Mon, 23 Jun 2025 14:40:39 +0800 Subject: [PATCH 3/4] docs: fix links (#36595) --- apps/docs/content/guides/database/extensions/timescaledb.mdx | 2 +- .../guides/database/postgres/setup-replication-external.mdx | 2 +- apps/docs/content/guides/database/replication.mdx | 2 +- apps/docs/content/guides/database/replication/faq.mdx | 4 ++-- .../guides/database/replication/monitoring-replication.mdx | 4 ++-- .../guides/database/replication/setting-up-replication.mdx | 2 +- apps/www/_blog/2025-03-07-dedicated-poolers.mdx | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/docs/content/guides/database/extensions/timescaledb.mdx b/apps/docs/content/guides/database/extensions/timescaledb.mdx index 5d4120418ae9a..8f06cc54bdf42 100644 --- a/apps/docs/content/guides/database/extensions/timescaledb.mdx +++ b/apps/docs/content/guides/database/extensions/timescaledb.mdx @@ -6,7 +6,7 @@ description: 'Scalable time-series data storage and analysis' -The `timescaledb` extension is deprecated in projects using Postgres 17. It continues to be supported in projects using Postgres 15, but will need to dropped before those projects are upgraded to Postgres 17. See the [Upgrading to Postgres 17 notes](/guides/platform/upgrading#upgrading-to-postgres-17) for more information. +The `timescaledb` extension is deprecated in projects using Postgres 17. It continues to be supported in projects using Postgres 15, but will need to dropped before those projects are upgraded to Postgres 17. See the [Upgrading to Postgres 17 notes](/docs/guides/platform/upgrading#upgrading-to-postgres-17) for more information. diff --git a/apps/docs/content/guides/database/postgres/setup-replication-external.mdx b/apps/docs/content/guides/database/postgres/setup-replication-external.mdx index e802f490e7987..b1a10c3a7192b 100644 --- a/apps/docs/content/guides/database/postgres/setup-replication-external.mdx +++ b/apps/docs/content/guides/database/postgres/setup-replication-external.mdx @@ -29,7 +29,7 @@ select pg_create_logical_replication_slot('example_slot', 'pgoutput'); This will need a **direct** connection (not a Connection Pooler) to your database and you can find the connection info in the [Dashboard](https://supabase.com/dashboard/project/_/settings/database). -You will also need to ensure that IPv6 is supported by your replication destination (or you can enable the [IPv4 add-on](/guides/platform/ipv4-address)) +You will also need to ensure that IPv6 is supported by your replication destination (or you can enable the [IPv4 add-on](/docs/guides/platform/ipv4-address)) If you would prefer not to use the `postgres` user, then you can run `CREATE ROLE WITH REPLICATION;` using the `postgres` user. diff --git a/apps/docs/content/guides/database/replication.mdx b/apps/docs/content/guides/database/replication.mdx index ee5445662ff32..57bd900485d06 100644 --- a/apps/docs/content/guides/database/replication.mdx +++ b/apps/docs/content/guides/database/replication.mdx @@ -59,7 +59,7 @@ Logical replication is typically output in 2 forms, `pgoutput` and `wal2json`. T When using logical replication, Postgres is then configured to keep WAL files around for longer than it needs them. If the files are removed too quickly, then your `replication slot` will become inactive and, if the database receives a large number of changes in a short time, then the `replication slot` can become lost as it was not able to keep up. -In order to mitigate this, Postgres has many options and settings that can be [tweaked](/guides/database/custom-postgres-config) to manage the WAL usage effectively. Not all of these settings are user configurable as they can impact the stability of your database. For those that are, these should be considered as advanced configuration and not changed without understanding that they can cause additional disk space and resources to be used, as well as incur additional costs. +In order to mitigate this, Postgres has many options and settings that can be [tweaked](/docs/guides/database/custom-postgres-config) to manage the WAL usage effectively. Not all of these settings are user configurable as they can impact the stability of your database. For those that are, these should be considered as advanced configuration and not changed without understanding that they can cause additional disk space and resources to be used, as well as incur additional costs. | Setting | Description | User-facing | Default | | ---------------------------------------------------------------------------------------- | ------------------------------------------------------ | ----------- | ------- | diff --git a/apps/docs/content/guides/database/replication/faq.mdx b/apps/docs/content/guides/database/replication/faq.mdx index f0bdd97f465f4..804976d09235a 100644 --- a/apps/docs/content/guides/database/replication/faq.mdx +++ b/apps/docs/content/guides/database/replication/faq.mdx @@ -13,7 +13,7 @@ Connections through a pooler, such as Supavisor, will not work. # The tool in use does not support IPv6 -You can enable the [IPv4 add-on](/guides/platform/ipv4-address) for your project. +You can enable the [IPv4 add-on](/docs/guides/platform/ipv4-address) for your project. # What is XMIN and should it be used? @@ -29,7 +29,7 @@ You can view [publications](https://supabase.com/dashboard/project/default/datab # How to configure database settings for replication? -Yes. Using the Supabase CLI, you can [configure database settings](/guides/database/custom-postgres-config#cli-configurable-settings) to optimize them for your replication needs. These values can vary depending on the activity of your database size and activity. +Yes. Using the Supabase CLI, you can [configure database settings](/docs/guides/database/custom-postgres-config#cli-configurable-settings) to optimize them for your replication needs. These values can vary depending on the activity of your database size and activity. # What are some important configuration options? diff --git a/apps/docs/content/guides/database/replication/monitoring-replication.mdx b/apps/docs/content/guides/database/replication/monitoring-replication.mdx index a49b8a8f27ad3..300ca96689822 100644 --- a/apps/docs/content/guides/database/replication/monitoring-replication.mdx +++ b/apps/docs/content/guides/database/replication/monitoring-replication.mdx @@ -4,12 +4,12 @@ title: 'Monitoring replication' Monitoring replication lag is important and there are 3 ways to do this: -1. Dashboard - Under the [Reports](/guides/platform/reports) of the dashboard, you can view the replication lag of your project +1. Dashboard - Under the [Reports](/docs/guides/platform/reports) of the dashboard, you can view the replication lag of your project 2. Database - - pg_stat_subscription (subscriber) - if PID is null, then the subscription is not active - pg_stat_subscription_stats - look here for error_count to see if there were issues applying or syncing (if yes, check the logs for why) - pg_replication_slots - use this to check if the slot is active and you can also calculate the lag from here -3. [Metrics](/guides/telemetry/metrics) - Using the prometheus endpoint for your project +3. [Metrics](/docs/guides/telemetry/metrics) - Using the prometheus endpoint for your project - replication_slots_max_lag_bytes - this is the more important one - pg_stat_replication_replay_lag - lag to replay WAL files from the source DB on the target DB (throttled by disk or high activity) - pg_stat_replication_send_lag - lag in sending WAL files from the source DB (a high lag means that the publisher is not being asked to send new WAL files OR a network issues) diff --git a/apps/docs/content/guides/database/replication/setting-up-replication.mdx b/apps/docs/content/guides/database/replication/setting-up-replication.mdx index 36d4d5287052a..665356ed99f14 100644 --- a/apps/docs/content/guides/database/replication/setting-up-replication.mdx +++ b/apps/docs/content/guides/database/replication/setting-up-replication.mdx @@ -68,7 +68,7 @@ Materialize has the following [documentation](https://materialize.com/docs/sql/c You can follow those steps with the following modifications: -1. Follow the steps in our [guide](/guides/database/postgres/setup-replication-external) to create a publication slot +1. Follow the steps in our [guide](/docs/guides/database/postgres/setup-replication-external) to create a publication slot diff --git a/apps/www/_blog/2025-03-07-dedicated-poolers.mdx b/apps/www/_blog/2025-03-07-dedicated-poolers.mdx index b82a9ccdd62be..7fcad0837c69d 100644 --- a/apps/www/_blog/2025-03-07-dedicated-poolers.mdx +++ b/apps/www/_blog/2025-03-07-dedicated-poolers.mdx @@ -25,7 +25,7 @@ This is available today for select customers, and will be generally available by ### Supabase Dedicated Pooler -The Dedicated Pooler is a [PgBouncer](https://www.pgbouncer.org/) instance that's co-located with your Postgres database. This will require you to connect with IPv6 or, if that's not an option, you can use the [IPv4 add-on](/guides/platform/ipv4-address). +The Dedicated Pooler is a [PgBouncer](https://www.pgbouncer.org/) instance that's co-located with your Postgres database. This will require you to connect with IPv6 or, if that's not an option, you can use the [IPv4 add-on](/docs/guides/platform/ipv4-address). The dedicated pooler is isolated to your own project and grants you fine-grained control over the configuration. From c8dbc57051ba007491c2419d4d1616e7ecd88ec0 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 23 Jun 2025 15:28:07 +0800 Subject: [PATCH 4/4] Minor update to new API keys modal for disabling / reenabling legacy keys (#36554) * Minor update to new API keys modal for disabling / reenabling legacy keys * Minor update * Refresh user count as well when clicking refresh in auth users management --- .../interfaces/Auth/Users/UsersV2.tsx | 7 +- .../ProjectSettings/ToggleLegacyApiKeys.tsx | 110 ++++++++++-------- .../src/Dialogs/TextConfirmModal.tsx | 2 +- 3 files changed, 69 insertions(+), 50 deletions(-) diff --git a/apps/studio/components/interfaces/Auth/Users/UsersV2.tsx b/apps/studio/components/interfaces/Auth/Users/UsersV2.tsx index 495ab33959629..63ee824ffb819 100644 --- a/apps/studio/components/interfaces/Auth/Users/UsersV2.tsx +++ b/apps/studio/components/interfaces/Auth/Users/UsersV2.tsx @@ -119,7 +119,7 @@ export const UsersV2 = () => { } ) - const { data: countData } = useUsersCountQuery({ + const { data: countData, refetch: refetchCount } = useUsersCountQuery({ projectRef, connectionString: project?.connectionString, keywords: filterKeywords, @@ -437,7 +437,10 @@ export const UsersV2 = () => { icon={} type="default" loading={isRefetching && !isFetchingNextPage} - onClick={() => refetch()} + onClick={() => { + refetch() + refetchCount() + }} > Refresh diff --git a/apps/studio/components/ui/ProjectSettings/ToggleLegacyApiKeys.tsx b/apps/studio/components/ui/ProjectSettings/ToggleLegacyApiKeys.tsx index 9a8e4f1856348..a24bc09ef8901 100644 --- a/apps/studio/components/ui/ProjectSettings/ToggleLegacyApiKeys.tsx +++ b/apps/studio/components/ui/ProjectSettings/ToggleLegacyApiKeys.tsx @@ -1,14 +1,14 @@ +import { PermissionAction } from '@supabase/shared-types/out/constants' import { useState } from 'react' import { toast } from 'sonner' -import { PermissionAction } from '@supabase/shared-types/out/constants' import { useParams } from 'common' import { ButtonTooltip } from 'components/ui/ButtonTooltip' import { useToggleLegacyAPIKeysMutation } from 'data/api-keys/legacy-api-key-toggle-mutation' import { useLegacyAPIKeysStatusQuery } from 'data/api-keys/legacy-api-keys-status-query' import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions' -import { Alert_Shadcn_, AlertDescription_Shadcn_, AlertTitle_Shadcn_, CriticalIcon } from 'ui' import TextConfirmModal from 'ui-patterns/Dialogs/TextConfirmModal' +import Panel from '../Panel' export const ToggleLegacyApiKeysPanel = () => { const { ref: projectRef } = useParams() @@ -20,44 +20,49 @@ export const ToggleLegacyApiKeysPanel = () => { const { can: canUpdateAPIKeys, isSuccess: isPermissionsSuccess } = useAsyncCheckProjectPermissions(PermissionAction.SECRETS_WRITE, '*') + const { enabled: isLegacyKeysEnabled } = legacyAPIKeysStatusData || {} + if (!(isLegacyAPIKeysStatusSuccess && isPermissionsSuccess)) { return null } return (
- - - - {legacyAPIKeysStatusData.enabled - ? 'Disabling your legacy API keys may cause your applications to break.' - : 'Re-enabling your legacy API keys may expose your applications to security risks.'} - - - {legacyAPIKeysStatusData.enabled - ? 'Make sure you are no longer using your legacy API keys before proceeding.' - : "Make sure you've tested your RLS policies."} - -
- setIsConfirmOpen(true)} - disabled={!canUpdateAPIKeys} - tooltip={{ - content: { - side: 'bottom', - text: !canUpdateAPIKeys - ? 'You need additional permissions to enable or disable JWT-based API keys' - : undefined, - }, - }} - > - {legacyAPIKeysStatusData.enabled - ? 'Disable JWT-based API keys' - : 'Re-enable JWT-based API keys'} - -
-
+ + +
+
+

+ {isLegacyKeysEnabled ? 'Disable legacy API keys' : 'Re-enabling legacy API keys'} +

+

+ {isLegacyKeysEnabled + ? 'Make sure you are no longer using your legacy API keys before proceeding.' + : 'Ensure that your RLS policies are in place prior to re-enabling legacy keys.'} +

+
+
+ setIsConfirmOpen(true)} + disabled={!canUpdateAPIKeys} + tooltip={{ + content: { + side: 'bottom', + text: !canUpdateAPIKeys + ? 'You need additional permissions to enable or disable JWT-based API keys' + : undefined, + }, + }} + > + {legacyAPIKeysStatusData.enabled + ? 'Disable JWT-based API keys' + : 'Re-enable JWT-based API keys'} + +
+
+
+
{ const { ref: projectRef } = useParams() + const { enabled: isLegacyKeysEnabled } = legacyAPIKeysStatusData || {} const { mutate: toggleLegacyAPIKey, isLoading: isTogglingLegacyAPIKey } = useToggleLegacyAPIKeysMutation() @@ -102,27 +108,37 @@ const ToggleApiKeysModal = ({ return ( onClose()} onConfirm={onToggleLegacyAPIKeysEnabled} - title={ - legacyAPIKeysStatusData.enabled ? 'Disable JWT-based keys' : 'Re-enable JWT-based keys' - } - confirmString={legacyAPIKeysStatusData.enabled ? 'disable' : 're-enable'} - confirmLabel={`Yes, ${legacyAPIKeysStatusData.enabled ? 'disable' : 're-enable'} anon and service_role`} - confirmPlaceholder={legacyAPIKeysStatusData.enabled ? 'disable' : 're-enable'} + title={isLegacyKeysEnabled ? 'Disable JWT-based keys' : 'Re-enable JWT-based keys'} + confirmString={isLegacyKeysEnabled ? 'disable' : 're-enable'} + confirmLabel={`Confirm to ${isLegacyKeysEnabled ? 'disable' : 're-enable'} anon and service_role`} + confirmPlaceholder={isLegacyKeysEnabled ? 'disable' : 're-enable'} loading={isTogglingLegacyAPIKey} - variant={legacyAPIKeysStatusData.enabled ? 'destructive' : 'default'} + variant={isLegacyKeysEnabled ? 'destructive' : 'default'} alert={ - legacyAPIKeysStatusData.enabled + isLegacyKeysEnabled ? { - title: 'Disabling can cause downtime!', - description: `If you disable your anon and service_role keys while they are in use, your applications will stop functioning. All API endpoints will receive HTTP 401 Unauthorized. Make sure you are no longer using them before proceeding.`, + title: 'Ensure legacy keys are no longer in use before disabling', + description: ( + + Disabling anon and service_role keys while they are in + use will cause downtime for your application. Ensure they are no longer in use + before proceeding. + + ), } : { - title: 'Prefer publishable and secret keys', - description: - 'While re-enabling anon and service_role keys makes sense in some cases, a better and more secure alternative is the publishable or secret key. Consider using those before proceeding!', + title: 'Publishable and secret keys are preferred', + description: ( + + Re-enabling anon and service_role keys may be + appropriate in certain cases, but using a publishable and secret key is more + secure. We recommend against re-enabling legacy API keys + + ), } } /> diff --git a/packages/ui-patterns/src/Dialogs/TextConfirmModal.tsx b/packages/ui-patterns/src/Dialogs/TextConfirmModal.tsx index 69b281d57592b..178b1ea7a593e 100644 --- a/packages/ui-patterns/src/Dialogs/TextConfirmModal.tsx +++ b/packages/ui-patterns/src/Dialogs/TextConfirmModal.tsx @@ -41,7 +41,7 @@ export interface TextConfirmModalProps { alert?: { base?: React.ComponentProps title?: string - description?: string + description?: string | ReactNode } input?: React.ComponentProps label?: React.ComponentProps