From f9cbf661ca6e47b62669ab77742cdf758b590d44 Mon Sep 17 00:00:00 2001 From: Ivan Vasilov Date: Mon, 5 Jan 2026 07:00:49 +0200 Subject: [PATCH 1/5] fix: Fix a bug in Edit FDW sheet (#41685) * Add types to the getDecryptedValues function. * Refactor the edit-wrapper-sheet to fetch the secrets by id, instead of name. * Add try/catch for the secret values fetching. * Minor CodeRabbit nitpicks. * Small improvement for types in EditWrapperSheet * Small fix for WrapperRow icon space issue hiding ChevronRight * Update comment * Only fetch encrypted IDs if value is a valid UUID * Nit --------- Co-authored-by: Joshen Lim --- .../Wrappers/EditWrapperSheet.tsx | 110 ++++++++++-------- .../Integrations/Wrappers/WrapperRow.tsx | 25 ++-- .../Integrations/Wrappers/WrapperTable.tsx | 3 +- apps/studio/data/auth/user-query.ts | 3 +- .../vault-secret-decrypted-value-query.ts | 14 ++- apps/studio/lib/constants/index.ts | 2 + 6 files changed, 88 insertions(+), 69 deletions(-) diff --git a/apps/studio/components/interfaces/Integrations/Wrappers/EditWrapperSheet.tsx b/apps/studio/components/interfaces/Integrations/Wrappers/EditWrapperSheet.tsx index b159095c48aee..c659e6d4e3935 100644 --- a/apps/studio/components/interfaces/Integrations/Wrappers/EditWrapperSheet.tsx +++ b/apps/studio/components/interfaces/Integrations/Wrappers/EditWrapperSheet.tsx @@ -1,15 +1,15 @@ import { useQueryClient } from '@tanstack/react-query' -import { isEmpty } from 'lodash' +import { compact, isEmpty, mapValues } from 'lodash' import { Edit, Trash } from 'lucide-react' import { useCallback, useEffect, useRef, useState } from 'react' import { toast } from 'sonner' +import { UUID_REGEX } from '@/lib/constants' import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui/Forms/FormSection' import { invalidateSchemasQuery } from 'data/database/schemas-query' import { useFDWUpdateMutation } from 'data/fdw/fdw-update-mutation' import { FDW } from 'data/fdw/fdws-query' -import { getDecryptedValue } from 'data/vault/vault-secret-decrypted-value-query' -import { useVaultSecretsQuery } from 'data/vault/vault-secrets-query' +import { getDecryptedValues } from 'data/vault/vault-secret-decrypted-value-query' import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' import { useConfirmOnClose, type ConfirmOnCloseModalProps } from 'hooks/ui/useConfirmOnClose' import { Button, Form, Input, SheetFooter, SheetHeader, SheetTitle } from 'ui' @@ -44,11 +44,6 @@ export const EditWrapperSheet = ({ const queryClient = useQueryClient() const { data: project } = useSelectedProjectQuery() - const { data: secrets, isPending: isSecretsLoading } = useVaultSecretsQuery({ - projectRef: project?.ref, - connectionString: project?.connectionString, - }) - const { mutate: updateFDW, isPending: isSaving } = useFDWUpdateMutation({ onSuccess: () => { toast.success(`Successfully updated ${wrapperMeta?.label} foreign data wrapper`) @@ -75,7 +70,7 @@ export const EditWrapperSheet = ({ ...convertKVStringArrayToJson(wrapper?.server_options ?? []), } - const onUpdateTable = (values: any) => { + const onUpdateTable = (values: FormattedWrapperTable) => { setWrapperTables((prev) => { // if the new values have tableIndex, we are editing an existing table if (values.tableIndex !== undefined) { @@ -91,9 +86,9 @@ export const EditWrapperSheet = ({ setSelectedTableToEdit(undefined) } - const onSubmit = async (values: any) => { + const onSubmit = async (values: Record) => { const validate = makeValidateRequired(wrapperMeta.server.options) - const errors: any = validate(values) + const errors = validate(values) const { wrapper_name } = values if (wrapper_name.length === 0) errors.name = 'Please provide a name for your wrapper' @@ -137,7 +132,15 @@ export const EditWrapperSheet = ({ onSubmit={onSubmit} className="h-full flex flex-col" > - {({ values, initialValues, resetForm }: any) => { + {({ + values, + initialValues, + resetForm, + }: { + values: Record + initialValues: Record + resetForm: (value: Record>) => void + }) => { // [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 @@ -152,50 +155,60 @@ export const EditWrapperSheet = ({ const hasChanges = hasFormChanges || hasTableChanges hasChangesRef.current = hasChanges - const encryptedOptions = wrapperMeta.server.options.filter((option) => option.encrypted) - // [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 useEffect(() => { - const fetchEncryptedValues = async () => { - setLoadingSecrets(true) - // If the secrets haven't loaded, escape and run the effect again when they're loaded - if (isSecretsLoading) { - return - } + const fetchEncryptedValues = async (ids: string[]) => { + try { + setLoadingSecrets(true) + // If the secrets haven't loaded, escape and run the effect again when they're loaded + const decryptedValues = await getDecryptedValues({ + projectRef: project?.ref, + connectionString: project?.connectionString, + ids: ids, + }) - const res = await Promise.all( - encryptedOptions.map(async (option) => { - const secret = secrets?.find( - (secret) => secret.name === `${wrapper.name}_${option.name}` - ) - if (secret !== undefined) { - const value = await getDecryptedValue({ - projectRef: project?.ref, - connectionString: project?.connectionString, - id: secret.id, - }) - return { [option.name]: value[0]?.decrypted_secret ?? '' } - } else { - return { [option.name]: '' } - } + // replace all values which are in the decryptedValues object with the decrypted value + const transformValues = (values: Record) => { + return mapValues(values, (value) => { + return decryptedValues[value] ?? value + }) + } + + resetForm({ + values: transformValues(values), + initialValues: transformValues(initialValues), }) - ) - const secretValues = res.reduce((a: any, b: any) => { - const [key] = Object.keys(b) - return { ...a, [key]: b[key] } - }, {}) + } catch (error) { + toast.error('Failed to fetch encrypted values') + } finally { + setLoadingSecrets(false) + } + } - resetForm({ - values: { ...values, ...secretValues }, - initialValues: { ...initialValues, ...secretValues }, + const encryptedOptions = wrapperMeta.server.options.filter( + (option) => option.encrypted + ) + + const encryptedIdsToFetch = compact( + encryptedOptions.map((option) => { + const value = initialValues[option.name] + return value ?? null }) - setLoadingSecrets(false) - } + ).filter((x) => UUID_REGEX.test(x)) + // [Joshen] ^ Validate UUID to filter out already decrypted values - if (encryptedOptions.length > 0) fetchEncryptedValues() - }, [isSecretsLoading]) + if (encryptedIdsToFetch.length > 0) { + fetchEncryptedValues(encryptedIdsToFetch) + } + /** + * [Joshen] We're deliberately not adding values and initialValues to the dependency array here + * as we only want to fetch the encrypted values once on load + values and initialValues will be updated + * as a result of that + */ + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [project?.ref, project?.connectionString]) return ( <> @@ -279,8 +292,7 @@ export const EditWrapperSheet = ({ Target: {target}

- Columns:{' '} - {table.columns.map((column: any) => column.name).join(', ')} + Columns: {table.columns.map((column) => column.name).join(', ')}

diff --git a/apps/studio/components/interfaces/Integrations/Wrappers/WrapperRow.tsx b/apps/studio/components/interfaces/Integrations/Wrappers/WrapperRow.tsx index ef5f4772d275e..43d6b8d200c0d 100644 --- a/apps/studio/components/interfaces/Integrations/Wrappers/WrapperRow.tsx +++ b/apps/studio/components/interfaces/Integrations/Wrappers/WrapperRow.tsx @@ -25,7 +25,6 @@ import { formatWrapperTables } from './Wrappers.utils' interface WrapperRowProps { wrapper: FDW - wrappers: FDW[] selectedWrapperToEdit?: FDW selectedWrapperToDelete?: FDW setSelectedWrapperToEdit: (value: string | null) => void @@ -33,9 +32,8 @@ interface WrapperRowProps { deletingWrapperIdRef: MutableRefObject } -const WrapperRow = ({ +export const WrapperRow = ({ wrapper, - wrappers, selectedWrapperToEdit, selectedWrapperToDelete, setSelectedWrapperToEdit, @@ -88,17 +86,20 @@ const WrapperRow = ({ {_tables?.map((table) => { - const target = table.table ?? table.object + const target = table.table ?? table.object ?? table.src_key return ( -
- +
+
{integration.icon({ className: 'p-0' })}
{target} - + {target} @@ -110,13 +111,16 @@ const WrapperRow = ({
- + {table.schema}.{table.table_name} - + {table.schema}.{table.table_name} @@ -129,7 +133,6 @@ const WrapperRow = ({ {encryptedMetadata.map((metadata) => (
- {/*

{metadata.label}:

*/} ) } - -export default WrapperRow diff --git a/apps/studio/components/interfaces/Integrations/Wrappers/WrapperTable.tsx b/apps/studio/components/interfaces/Integrations/Wrappers/WrapperTable.tsx index 46af1b1529a19..431fdf15aa67d 100644 --- a/apps/studio/components/interfaces/Integrations/Wrappers/WrapperTable.tsx +++ b/apps/studio/components/interfaces/Integrations/Wrappers/WrapperTable.tsx @@ -17,7 +17,7 @@ import { TableRow, } from 'ui' import { INTEGRATIONS } from '../Landing/Integrations.constants' -import WrapperRow from './WrapperRow' +import { WrapperRow } from './WrapperRow' import { wrapperMetaComparator } from './Wrappers.utils' interface WrapperTableProps { @@ -91,7 +91,6 @@ export const WrapperTable = ({ isLatest = false }: WrapperTableProps) => { { const sql = vaultSecretDecryptedValuesQuery(ids) - const { result } = await executeSql({ projectRef, connectionString, sql }, signal) - return result.reduce((a: any, b: any) => { - return { ...a, [b.id]: b.decrypted_secret } - }, {}) + const { result } = await executeSql<{ id: string; decrypted_secret: string }[]>( + { projectRef, connectionString, sql }, + signal + ) + return result.reduce( + (a, b) => { + return { ...a, [b.id]: b.decrypted_secret } + }, + {} as Record + ) } diff --git a/apps/studio/lib/constants/index.ts b/apps/studio/lib/constants/index.ts index 4a95864039e97..3bf961dec49d9 100644 --- a/apps/studio/lib/constants/index.ts +++ b/apps/studio/lib/constants/index.ts @@ -56,3 +56,5 @@ export const OPT_IN_TAGS = { export const GB = 1024 * 1024 * 1024 export const MB = 1024 * 1024 export const KB = 1024 + +export const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i From a88c88fa3641de17c3826b58faa5c30455a6f21a Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 5 Jan 2026 12:14:19 +0700 Subject: [PATCH 2/5] Hide RLS disabled CTA in table editor header if no lints (#41517) * Hide RLS disabled CTA in table editor header if no lints * Clean * Update e2e tests * INvlidate lints when creating tbale * Revert * Invalidate lints when duplicating table --- .../interfaces/Settings/API/PostgrestConfig.tsx | 2 -- .../TableGridEditor/GridHeaderActions.tsx | 12 ++++++++++-- .../SidePanelEditor/SidePanelEditor.tsx | 3 +++ .../project-postgrest-config-update-mutation.ts | 6 +++++- e2e/studio/features/table-editor.spec.ts | 13 +++++++++---- 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/apps/studio/components/interfaces/Settings/API/PostgrestConfig.tsx b/apps/studio/components/interfaces/Settings/API/PostgrestConfig.tsx index 1d88dcdf0d554..454d3fcf9075e 100644 --- a/apps/studio/components/interfaces/Settings/API/PostgrestConfig.tsx +++ b/apps/studio/components/interfaces/Settings/API/PostgrestConfig.tsx @@ -35,11 +35,9 @@ import { Form_Shadcn_, Input_Shadcn_, PrePostTab, - Separator, Skeleton, Switch, WarningIcon, - cn, } from 'ui' import { GenericSkeletonLoader } from 'ui-patterns' import { Admonition } from 'ui-patterns/admonition' diff --git a/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx b/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx index fc77f12c9c4f7..0c91f6e92a066 100644 --- a/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx @@ -161,6 +161,14 @@ export const GridHeaderActions = ({ table, isRefetching }: GridHeaderActionsProp // This will change when we allow autogenerated API docs for schemas other than `public` const doesHaveAutoGeneratedAPIDocs = table.schema === 'public' + const { hasLint: tableHasLints } = getEntityLintDetails( + table.name, + 'rls_disabled_in_public', + ['ERROR'], + lints, + table.schema + ) + const { hasLint: viewHasLints, matchingLint: matchingViewLint } = getEntityLintDetails( table.name, 'security_definer_view', @@ -301,7 +309,7 @@ export const GridHeaderActions = ({ table, isRefetching }: GridHeaderActionsProp )} - ) : ( + ) : tableHasLints ? (
- ) + ) : null ) : null} {isTable && isIndexAdvisorAvailable && !isIndexAdvisorEnabled && ( diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx index f56ec5c682059..70daf4bde1dd6 100644 --- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx @@ -15,6 +15,7 @@ import type { ForeignKeyConstraint } from 'data/database/foreign-key-constraints import { databaseKeys } from 'data/database/keys' import { ENTITY_TYPE } from 'data/entity-types/entity-type-constants' import { entityTypeKeys } from 'data/entity-types/keys' +import { lintKeys } from 'data/lint/keys' import { privilegeKeys } from 'data/privileges/keys' import { tableEditorKeys } from 'data/table-editor/keys' import { isTableLike } from 'data/table-editor/table-editor-types' @@ -530,6 +531,7 @@ export const SidePanelEditor = ({ queryClient.invalidateQueries({ queryKey: privilegeKeys.tablePrivilegesList(project?.ref), }), + queryClient.invalidateQueries({ queryKey: lintKeys.lint(project?.ref) }), ]) // Show success toast after everything is complete @@ -583,6 +585,7 @@ export const SidePanelEditor = ({ queryClient.invalidateQueries({ queryKey: privilegeKeys.tablePrivilegesList(project?.ref), }), + queryClient.invalidateQueries({ queryKey: lintKeys.lint(project?.ref) }), ]) toast.success( diff --git a/apps/studio/data/config/project-postgrest-config-update-mutation.ts b/apps/studio/data/config/project-postgrest-config-update-mutation.ts index 238a7cc0a30a7..44aa1f70dfca3 100644 --- a/apps/studio/data/config/project-postgrest-config-update-mutation.ts +++ b/apps/studio/data/config/project-postgrest-config-update-mutation.ts @@ -3,6 +3,7 @@ import { toast } from 'sonner' import { components } from 'api-types' import { handleError, patch } from 'data/fetchers' +import { lintKeys } from 'data/lint/keys' import type { ResponseError, UseCustomMutationOptions } from 'types' import { configKeys } from './keys' @@ -63,7 +64,10 @@ export const useProjectPostgrestConfigUpdateMutation = ({ mutationFn: (vars) => updateProjectPostgrestConfig(vars), async onSuccess(data, variables, context) { const { projectRef } = variables - queryClient.invalidateQueries({ queryKey: configKeys.postgrest(projectRef) }) + await Promise.all([ + queryClient.invalidateQueries({ queryKey: configKeys.postgrest(projectRef) }), + queryClient.invalidateQueries({ queryKey: lintKeys.lint(projectRef) }), + ]) await onSuccess?.(data, variables, context) }, async onError(data, variables, context) { diff --git a/e2e/studio/features/table-editor.spec.ts b/e2e/studio/features/table-editor.spec.ts index d4e3445ee92fd..ec66e4b8a8e12 100644 --- a/e2e/studio/features/table-editor.spec.ts +++ b/e2e/studio/features/table-editor.spec.ts @@ -1,7 +1,7 @@ -import { expect, Locator, Page } from '@playwright/test' +import { expect, Page } from '@playwright/test' import fs from 'fs' import path from 'path' -import { isCLI } from '../utils/is-cli.js' +import { env } from '../env.config.js' import { releaseFileOnceCleanup, withFileOnceSetup } from '../utils/once-per-file.js' import { resetLocalStorage } from '../utils/reset-local-storage.js' import { test } from '../utils/test.js' @@ -13,7 +13,6 @@ import { waitForGridDataToLoad, waitForTableToLoad, } from '../utils/wait-for-response.js' -import { env } from '../env.config.js' const tableNamePrefix = 'pw_table' const columnName = 'pw_column' @@ -244,14 +243,20 @@ testRunner('table editor', () => { await page.getByTestId('table-name-input').fill(tableNameRlsDisabled) await page.getByLabel('Enable Row Level Security (').click() await page.getByRole('button', { name: 'Confirm' }).click() + + // Wait for table creation const apiPromise = waitForApiResponse( page, 'pg-meta', ref, 'tables?include_columns=false&included_schemas=public' - ) // wait for table creation + ) + // Wait for lints refresh + const lintsPromise = waitForApiResponse(page, 'projects', ref, 'run-lints') + await page.getByRole('button', { name: 'Save' }).click() await apiPromise + await lintsPromise await page.getByRole('button', { name: `View ${tableNameRlsDisabled}` }).click() await expect(page.getByRole('button', { name: 'RLS disabled' })).toBeVisible() }) From 90b77c3b23f7d8a858bac276ea378d9d8795b978 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 5 Jan 2026 12:17:10 +0700 Subject: [PATCH 3/5] Mark useQueryStateWithSelect as deprecated (#41707) * Mark useQueryStateWithSelect as deprecated * update comment --- apps/studio/hooks/misc/useQueryStateWithSelect.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/studio/hooks/misc/useQueryStateWithSelect.ts b/apps/studio/hooks/misc/useQueryStateWithSelect.ts index 46f7a0624ce3c..31cbe6fa6bd8c 100644 --- a/apps/studio/hooks/misc/useQueryStateWithSelect.ts +++ b/apps/studio/hooks/misc/useQueryStateWithSelect.ts @@ -13,6 +13,10 @@ import { toast } from 'sonner' * @returns Object with: * - value: The result of select(selectedId) or undefined * - setValue: Function to set/clear the selected ID in the URL + * + * @deprecated Avoid using this hook, and use nuqs directly + * Refer to this PR for more information: https://github.com/supabase/supabase/pull/41380 + * as well as context on how to refactor to remove usage of this hook */ export function useQueryStateWithSelect({ enabled, From 3336b2245175a6a68abf4f625567649cfae6039b Mon Sep 17 00:00:00 2001 From: Pamela Chia Date: Mon, 5 Jan 2026 15:22:06 +0700 Subject: [PATCH 4/5] chore(flags): migrate homeNew flag from configcat to posthog (#41101) Switch homeNew flag to usePHFlag. --- apps/studio/components/layouts/ProjectLayout/index.tsx | 5 +++-- apps/studio/pages/new/[slug].tsx | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/studio/components/layouts/ProjectLayout/index.tsx b/apps/studio/components/layouts/ProjectLayout/index.tsx index 3ffa96b4852bc..e2f9b39341fc6 100644 --- a/apps/studio/components/layouts/ProjectLayout/index.tsx +++ b/apps/studio/components/layouts/ProjectLayout/index.tsx @@ -2,7 +2,7 @@ import Head from 'next/head' import { useRouter } from 'next/router' import { forwardRef, Fragment, PropsWithChildren, ReactNode, useEffect } from 'react' -import { mergeRefs, useFlag, useParams } from 'common' +import { mergeRefs, useParams } from 'common' import { CreateBranchModal } from 'components/interfaces/BranchManagement/CreateBranchModal' import { ProjectAPIDocs } from 'components/interfaces/ProjectAPIDocs/ProjectAPIDocs' import { ResourceExhaustionWarningBanner } from 'components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner' @@ -11,6 +11,7 @@ import { useCustomContent } from 'hooks/custom-content/useCustomContent' import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' import { withAuth } from 'hooks/misc/withAuth' +import { usePHFlag } from 'hooks/ui/useFlag' import { PROJECT_STATUS } from 'lib/constants' import { useAppStateSnapshot } from 'state/app-state' import { useDatabaseSelectorStateSnapshot } from 'state/database-selector' @@ -275,7 +276,7 @@ const ContentWrapper = ({ isLoading, isBlocking = true, children }: ContentWrapp const { ref } = useParams() const state = useDatabaseSelectorStateSnapshot() const { data: selectedProject } = useSelectedProjectQuery() - const isHomeNewFlag = useFlag('homeNew') + const isHomeNewFlag = usePHFlag('homeNew') const isBranchesPage = router.pathname.includes('/project/[ref]/branches') const isSettingsPages = router.pathname.includes('/project/[ref]/settings') diff --git a/apps/studio/pages/new/[slug].tsx b/apps/studio/pages/new/[slug].tsx index 29494cd39f19a..c93e0dc64aad1 100644 --- a/apps/studio/pages/new/[slug].tsx +++ b/apps/studio/pages/new/[slug].tsx @@ -51,6 +51,7 @@ import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' import { withAuth } from 'hooks/misc/withAuth' +import { usePHFlag } from 'hooks/ui/useFlag' import { DOCS_URL, PROJECT_STATUS, PROVIDERS, useDefaultProvider } from 'lib/constants' import { useTrack } from 'lib/telemetry/track' import { AWS_REGIONS, type CloudProvider } from 'shared-data' @@ -82,7 +83,7 @@ const Wizard: NextPageWithLayout = () => { const projectCreationDisabled = useFlag('disableProjectCreationAndUpdate') const showPostgresVersionSelector = useFlag('showPostgresVersionSelector') const cloudProviderEnabled = useFlag('enableFlyCloudProvider') - const isHomeNew = useFlag('homeNew') + const isHomeNew = usePHFlag('homeNew') const showNonProdFields = process.env.NEXT_PUBLIC_ENVIRONMENT !== 'prod' const isNotOnHigherPlan = !['team', 'enterprise', 'platform'].includes(currentOrg?.plan.id ?? '') From 351ba14d3c5a34275c18a1a296cc2e681d13ca34 Mon Sep 17 00:00:00 2001 From: Andrew Valleteau Date: Mon, 5 Jan 2026 09:28:16 +0100 Subject: [PATCH 5/5] docs(branching): add the list of all valid secrets fields (#41640) * docs(branching): add the list of all valid secrets fields * chore(docs): allow Captcha to the rules spelling lints * docs(branching): make external secret a star field --- .../deployment/branching/configuration.mdx | 54 ++++++++++++++++++- supa-mdx-lint/Rule003Spelling.toml | 1 + 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/apps/docs/content/guides/deployment/branching/configuration.mdx b/apps/docs/content/guides/deployment/branching/configuration.mdx index 9e5559a506b86..937edbec6fe6d 100644 --- a/apps/docs/content/guides/deployment/branching/configuration.mdx +++ b/apps/docs/content/guides/deployment/branching/configuration.mdx @@ -154,7 +154,59 @@ secret = "env(SUPABASE_AUTH_EXTERNAL_GITHUB_SECRET)" -The `encrypted:` syntax only works for designated "secret" fields in the configuration (like `secret` in auth providers). Using encrypted values in other fields will not be automatically decrypted and may cause issues. For non-secret fields, use environment variables with the `env()` syntax instead. +The `encrypted:` syntax only works for designated "secret" fields in the configuration. Using encrypted values in other fields will not be automatically decrypted and may cause issues. For non-secret fields, use environment variables with the `env()` syntax instead. + +The following fields support the `encrypted:` syntax: + +**Studio** + +- `studio.openai_api_key` + +**Database** + +- `db.root_key` +- `db.vault.*` (any key in the vault map) + +**Auth - Core Keys** + +- `auth.publishable_key` +- `auth.secret_key` +- `auth.jwt_secret` +- `auth.anon_key` +- `auth.service_role_key` + +**Auth - Email (SMTP)** + +- `auth.email.smtp.pass` + +**Auth - Captcha** + +- `auth.captcha.secret` + +**Auth - Hooks** + +- `auth.hook.mfa_verification_attempt.secrets` +- `auth.hook.password_verification_attempt.secrets` +- `auth.hook.custom_access_token.secrets` +- `auth.hook.send_sms.secrets` +- `auth.hook.send_email.secrets` +- `auth.hook.before_user_created.secrets` + +**Auth - SMS Providers** + +- `auth.sms.twilio.auth_token` +- `auth.sms.twilio_verify.auth_token` +- `auth.sms.messagebird.access_key` +- `auth.sms.textlocal.api_key` +- `auth.sms.vonage.api_secret` + +**Auth - External OAuth Providers** + +- `auth.external.*.secret` + +**Edge Runtime** + +- `edge_runtime.secrets.*` (any key in the secrets map) diff --git a/supa-mdx-lint/Rule003Spelling.toml b/supa-mdx-lint/Rule003Spelling.toml index 5da86acc7a327..912a50d853111 100644 --- a/supa-mdx-lint/Rule003Spelling.toml +++ b/supa-mdx-lint/Rule003Spelling.toml @@ -358,6 +358,7 @@ allow_list = [ "gte-small", "halfvec", "hCaptcha", + "Captcha", "https?:\\/\\/\\S+", "i.e.", "imgproxy",