diff --git a/apps/docs/content/guides/api/securing-your-api.mdx b/apps/docs/content/guides/api/securing-your-api.mdx
index 894b2e8179b56..477e7c0755767 100644
--- a/apps/docs/content/guides/api/securing-your-api.mdx
+++ b/apps/docs/content/guides/api/securing-your-api.mdx
@@ -68,6 +68,12 @@ alter default privileges for role postgres in schema public
revoke execute on functions from public;
```
+
+
+These default privileges pose no direct security risk. They appear in [pg_default_acl](https://www.postgresql.org/docs/current/catalog-pg-default-acl.html), granted by [supabase_admin](/docs/guides/database/postgres/roles#supabaseadmin) to `anon`, `authenticated`, and `service_role`. The default privileges are intentional and part of Supabase's standard permission model. The `supabase_admin` role is an internal management role that can't authenticate through the Data API.
+
+
+
## Use a dedicated API schema
If you want an extra boundary around your Data API, lock down the `public` schema and expose a dedicated schema, such as `api`, instead. You can control access with grants in any schema, but this can make the surface easier to reason about: objects in `api` represent your Data API, while internal tables and helper functions stay in schemas that are not exposed. See [Using Custom Schemas](/docs/guides/api/using-custom-schemas) for setup steps.
diff --git a/apps/studio/components/interfaces/Account/AccessTokens/Scoped/ViewTokenSheet.tsx b/apps/studio/components/interfaces/Account/AccessTokens/Scoped/ViewTokenSheet.tsx
index e3a6a51075c7a..e6f16ba25c08d 100644
--- a/apps/studio/components/interfaces/Account/AccessTokens/Scoped/ViewTokenSheet.tsx
+++ b/apps/studio/components/interfaces/Account/AccessTokens/Scoped/ViewTokenSheet.tsx
@@ -22,6 +22,7 @@ import { formatAccessText, getRealAccess } from '../AccessToken.utils'
import { useOrgAndProjectData } from '../hooks/useOrgAndProjectData'
import { DocsButton } from '@/components/ui/DocsButton'
import { useScopedAccessTokenQuery } from '@/data/scoped-access-tokens/scoped-access-token-query'
+import { DOCS_URL } from '@/lib/constants'
interface ViewTokenSheetProps {
visible: boolean
@@ -110,7 +111,7 @@ export function ViewTokenSheet({ visible, tokenId, onClose }: ViewTokenSheetProp
View access for {token?.name}
-
+
diff --git a/apps/studio/components/interfaces/Auth/AuthProvidersForm/index.tsx b/apps/studio/components/interfaces/Auth/AuthProvidersForm/index.tsx
index 9d801843c987e..4950b329c37bf 100644
--- a/apps/studio/components/interfaces/Auth/AuthProvidersForm/index.tsx
+++ b/apps/studio/components/interfaces/Auth/AuthProvidersForm/index.tsx
@@ -18,6 +18,7 @@ import { AlertError } from '@/components/ui/AlertError'
import { ResourceList } from '@/components/ui/Resource/ResourceList'
import { HorizontalShimmerWithIcon } from '@/components/ui/Shimmers'
import { useAuthConfigQuery } from '@/data/auth/auth-config-query'
+import { DOCS_URL } from '@/lib/constants'
export const AuthProvidersForm = () => {
const { ref: projectRef } = useParams()
@@ -59,7 +60,7 @@ export const AuthProvidersForm = () => {
hour.
}>
-
+
View security recommendations
@@ -89,17 +90,17 @@ export const AuthProvidersForm = () => {
: provider
let isActive = false
if (providerSchema.title === 'SAML 2.0') {
- isActive = authConfig && (authConfig as any)['SAML_ENABLED']
+ isActive = authConfig && authConfig['SAML_ENABLED']
} else if (providerSchema.title === 'LinkedIn (OIDC)') {
- isActive = authConfig && (authConfig as any)['EXTERNAL_LINKEDIN_OIDC_ENABLED']
+ isActive = authConfig && authConfig['EXTERNAL_LINKEDIN_OIDC_ENABLED']
} else if (providerSchema.title === 'Slack (OIDC)') {
- isActive = authConfig && (authConfig as any)['EXTERNAL_SLACK_OIDC_ENABLED']
+ isActive = authConfig && authConfig['EXTERNAL_SLACK_OIDC_ENABLED']
} else if (providerSchema.title.includes('Web3')) {
- isActive = authConfig && (authConfig as any)['EXTERNAL_WEB3_SOLANA_ENABLED']
+ isActive = authConfig && authConfig['EXTERNAL_WEB3_SOLANA_ENABLED']
} else if (providerSchema.title.includes('X / Twitter (OAuth 2.0)')) {
isActive = authConfig && (authConfig as any)['EXTERNAL_X_ENABLED']
} else if (providerSchema.title === 'Twitter (Deprecated)') {
- isActive = authConfig && (authConfig as any)['EXTERNAL_TWITTER_ENABLED']
+ isActive = authConfig && authConfig['EXTERNAL_TWITTER_ENABLED']
} else {
isActive =
authConfig &&
diff --git a/apps/studio/components/interfaces/ConnectSheet/ConnectStepsSection.tsx b/apps/studio/components/interfaces/ConnectSheet/ConnectStepsSection.tsx
index aa472c7d04c21..947e24b4c75c1 100644
--- a/apps/studio/components/interfaces/ConnectSheet/ConnectStepsSection.tsx
+++ b/apps/studio/components/interfaces/ConnectSheet/ConnectStepsSection.tsx
@@ -286,10 +286,7 @@ export function ConnectStepsSection({ steps, state, projectKeys }: ConnectStepsS
title="MCP for self-hosted Supabase requires extra setup"
description="The configuration below points at the hosted Supabase MCP server. To use MCP against your self-hosted instance, follow the self-hosted MCP guide."
actions={[
- ,
+ ,
]}
/>
)}
diff --git a/apps/studio/components/interfaces/ConnectSheet/content/steps/direct-connection/content.tsx b/apps/studio/components/interfaces/ConnectSheet/content/steps/direct-connection/content.tsx
index 64687afd8423e..6c1be4ba4b97d 100644
--- a/apps/studio/components/interfaces/ConnectSheet/content/steps/direct-connection/content.tsx
+++ b/apps/studio/components/interfaces/ConnectSheet/content/steps/direct-connection/content.tsx
@@ -28,12 +28,14 @@ import {
} from '@/components/interfaces/ConnectSheet/ConnectionString.utils'
import { PasswordEncodingNote } from '@/components/interfaces/ConnectSheet/PasswordEncodingNote'
import { ResetDbPasswordDialog } from '@/components/interfaces/Settings/Database/DatabaseSettings/ResetDbPasswordDialog'
+import { InlineLink } from '@/components/ui/InlineLink'
import { usePgbouncerConfigQuery } from '@/data/database/pgbouncer-config-query'
import { useSupavisorConfigurationQuery } from '@/data/database/supavisor-configuration-query'
import { useReadReplicasQuery } from '@/data/read-replicas/replicas-query'
import { useProjectAddonsQuery } from '@/data/subscriptions/project-addons-query'
import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements'
import { useIsHighAvailability } from '@/hooks/misc/useSelectedProject'
+import { DOCS_URL } from '@/lib/constants'
import { pluckObjectFields } from '@/lib/helpers'
import { useTrack } from '@/lib/telemetry/track'
@@ -275,14 +277,11 @@ function DirectConnectionContent({ state, deploymentMode }: StepContentProps) {
{showSelfHostedDirectNotice && (
+ Launching a project on compute size "{instanceLabel(instanceSize)}" increases your
+ monthly costs by ${additionalMonthlySpend}, independent of how actively you use it. By
+ clicking "I understand", you agree to the additional costs.{' '}
+
+ Compute Costs
+ {' '}
+ are non-refundable.
+
+
+
+
+
+ )
+}
diff --git a/apps/studio/components/interfaces/Settings/General/DeleteProjectPanel/DeleteProjectModal.tsx b/apps/studio/components/interfaces/Settings/General/DeleteProjectPanel/DeleteProjectModal.tsx
index 759d36847e5e0..1876bc1035e4b 100644
--- a/apps/studio/components/interfaces/Settings/General/DeleteProjectPanel/DeleteProjectModal.tsx
+++ b/apps/studio/components/interfaces/Settings/General/DeleteProjectPanel/DeleteProjectModal.tsx
@@ -1,11 +1,13 @@
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import { toast } from 'sonner'
-import { TextArea } from 'ui'
+import { Card, CardContent, cn, TextArea } from 'ui'
+import { CollapsibleCardSection } from 'ui-patterns/CollapsibleCardSection'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { CANCELLATION_REASONS } from '@/components/interfaces/Billing/Billing.constants'
import { LogicalBackupCliInstructions } from '@/components/layouts/ProjectLayout/LogicalBackupCliInstructions'
+import { InlineLink } from '@/components/ui/InlineLink'
import { TextConfirmModal } from '@/components/ui/TextConfirmModalWrapper'
import { useSendDowngradeFeedbackMutation } from '@/data/feedback/exit-survey-send'
import type { OrgProject } from '@/data/projects/org-projects-infinite-query'
@@ -14,6 +16,7 @@ import { useOrgSubscriptionQuery } from '@/data/subscriptions/org-subscription-q
import { useLastVisitedOrganization } from '@/hooks/misc/useLastVisitedOrganization'
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
+import { DOCS_URL } from '@/lib/constants'
import type { Organization } from '@/types'
export const DeleteProjectModal = ({
@@ -125,14 +128,9 @@ export const DeleteProjectModal = ({
<>
{!isFree && 'All project data will be lost, and cannot be undone. '}
Read the{' '}
-
+
documentation
- {' '}
+ {' '}
for prerequisites, implications, and recovery information.
>
),
@@ -151,7 +149,19 @@ export const DeleteProjectModal = ({
}}
>
-
+
+ div>button]:tracking-normal',
+ '[&>div>button]:text-foreground [&>div>button]:hover:text-foreground',
+ '[&>div>button]:data-open:text-foreground [&>div>button]:text-sm'
+ )}
+ >
+
+
+
+
+
{/*
[Joshen] This is basically ExitSurvey.tsx, ideally we have one shared component but the one
@@ -181,6 +191,7 @@ export const DeleteProjectModal = ({
].join(' ')}
>