For secret keys, see API settings.
diff --git a/apps/studio/components/interfaces/Connect/Connect.tsx b/apps/studio/components/interfaces/Connect/Connect.tsx
index e2e91db087405..9ccd9c8d47d48 100644
--- a/apps/studio/components/interfaces/Connect/Connect.tsx
+++ b/apps/studio/components/interfaces/Connect/Connect.tsx
@@ -5,7 +5,6 @@ import { DatabaseConnectionString } from 'components/interfaces/Connect/Database
import { McpTabContent } from 'components/interfaces/Connect/McpTabContent'
import Panel from 'components/ui/Panel'
import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
-import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
import { BASE_PATH } from 'lib/constants'
@@ -35,6 +34,7 @@ import { CONNECTION_TYPES, ConnectionType, FRAMEWORKS, MOBILES, ORMS } from './C
import { getContentFilePath, inferConnectTabFromParentKey } from './Connect.utils'
import { ConnectDropdown } from './ConnectDropdown'
import { ConnectTabContent } from './ConnectTabContent'
+import { useProjectApiUrl } from '@/data/config/project-endpoint-query'
export const Connect = () => {
const router = useRouter()
@@ -97,7 +97,8 @@ export const Connect = () => {
?.children.find((child) => child.key === selectedChild)?.children[0]?.key || ''
)
- const { data: settings } = useProjectSettingsV2Query({ projectRef }, { enabled: open })
+ const { data: resolvedEndpoint } = useProjectApiUrl({ projectRef })
+
const { can: canReadAPIKeys } = useAsyncCheckPermissions(
PermissionAction.READ,
'service_api_keys'
@@ -216,22 +217,12 @@ export const Connect = () => {
: { anonKey: null, publishableKey: null }
const projectKeys = useMemo(() => {
- const protocol = settings?.app_config?.protocol ?? 'https'
- const endpoint = settings?.app_config?.endpoint ?? ''
- const apiHost = canReadAPIKeys ? `${protocol}://${endpoint ?? '-'}` : ''
-
return {
- apiUrl: apiHost ?? null,
+ apiUrl: resolvedEndpoint ?? null,
anonKey: anonKey?.api_key ?? null,
publishableKey: publishableKey?.api_key ?? null,
}
- }, [
- settings?.app_config?.protocol,
- settings?.app_config?.endpoint,
- canReadAPIKeys,
- anonKey?.api_key,
- publishableKey?.api_key,
- ])
+ }, [resolvedEndpoint, anonKey?.api_key, publishableKey?.api_key])
const filePath = getContentFilePath({
connectionObject,
diff --git a/apps/studio/components/interfaces/ConnectSheet/ConnectSheet.tsx b/apps/studio/components/interfaces/ConnectSheet/ConnectSheet.tsx
index 60d3d1f5e941a..02f605fc838ad 100644
--- a/apps/studio/components/interfaces/ConnectSheet/ConnectSheet.tsx
+++ b/apps/studio/components/interfaces/ConnectSheet/ConnectSheet.tsx
@@ -1,7 +1,6 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useParams } from 'common'
import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
-import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { parseAsBoolean, parseAsString, useQueryState } from 'nuqs'
import { useMemo } from 'react'
@@ -11,9 +10,12 @@ import type { ProjectKeys } from './Connect.types'
import { ConnectConfigSection, ModeSelector } from './ConnectConfigSection'
import { ConnectStepsSection } from './ConnectStepsSection'
import { useConnectState } from './useConnectState'
+import { useProjectApiUrl } from '@/data/config/project-endpoint-query'
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
export const ConnectSheet = () => {
+ const { ref: projectRef } = useParams()
+
const {
projectConnectionShowAppFrameworks: showAppFrameworks,
projectConnectionShowMobileFrameworks: showMobileFrameworks,
@@ -40,9 +42,8 @@ export const ConnectSheet = () => {
const { state, activeFields, resolvedSteps, schema, getFieldOptions, setMode, updateField } =
useConnectState()
- // Project keys for step components
- const { ref: projectRef } = useParams()
- const { data: settings } = useProjectSettingsV2Query({ projectRef }, { enabled: showConnect })
+ const { data: endpoint = '' } = useProjectApiUrl({ projectRef }, { enabled: showConnect })
+
const { can: canReadAPIKeys } = useAsyncCheckPermissions(
PermissionAction.READ,
'service_api_keys'
@@ -53,22 +54,12 @@ export const ConnectSheet = () => {
: { anonKey: null, publishableKey: null }
const projectKeys: ProjectKeys = useMemo(() => {
- const protocol = settings?.app_config?.protocol ?? 'https'
- const endpoint = settings?.app_config?.endpoint ?? ''
- const apiHost = canReadAPIKeys ? `${protocol}://${endpoint ?? '-'}` : ''
-
return {
- apiUrl: apiHost ?? null,
+ apiUrl: endpoint,
anonKey: anonKey?.api_key ?? null,
publishableKey: publishableKey?.api_key ?? null,
}
- }, [
- settings?.app_config?.protocol,
- settings?.app_config?.endpoint,
- canReadAPIKeys,
- anonKey?.api_key,
- publishableKey?.api_key,
- ])
+ }, [endpoint, anonKey?.api_key, publishableKey?.api_key])
const availableModeIds = useMemo(() => {
const modes: string[] = []
diff --git a/apps/studio/components/interfaces/Docs/ResourceContent.tsx b/apps/studio/components/interfaces/Docs/ResourceContent.tsx
index f5f58076ab591..fd4093974e0cb 100644
--- a/apps/studio/components/interfaces/Docs/ResourceContent.tsx
+++ b/apps/studio/components/interfaces/Docs/ResourceContent.tsx
@@ -7,13 +7,12 @@ import Description from '@/components/interfaces/Docs/Description'
import Param from '@/components/interfaces/Docs/Param'
import Snippets from '@/components/interfaces/Docs/Snippets'
import { InlineLink } from '@/components/ui/InlineLink'
-import { useCustomDomainsQuery } from '@/data/custom-domains/custom-domains-query'
+import { useProjectApiUrl } from '@/data/config/project-endpoint-query'
import { useProjectJsonSchemaQuery } from '@/data/docs/project-json-schema-query'
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
import { DOCS_URL } from '@/lib/constants'
interface ResourceContentProps {
- apiEndpoint: string
resourceId: string
resources: { [key: string]: { id: string; displayName: string; camelCase: string } }
selectedLang: 'bash' | 'js'
@@ -22,7 +21,6 @@ interface ResourceContentProps {
}
export const ResourceContent = ({
- apiEndpoint,
resourceId,
resources,
selectedLang,
@@ -30,16 +28,12 @@ export const ResourceContent = ({
refreshDocs,
}: ResourceContentProps) => {
const { ref } = useParams()
- const { data: customDomainData } = useCustomDomainsQuery({ projectRef: ref })
const { realtimeAll: realtimeEnabled } = useIsFeatureEnabled(['realtime:all'])
const { data: jsonSchema } = useProjectJsonSchemaQuery({ projectRef: ref })
const { paths, definitions } = jsonSchema || {}
- const endpoint =
- customDomainData?.customDomain?.status === 'active'
- ? `https://${customDomainData.customDomain.hostname}`
- : apiEndpoint
+ const { data: endpoint = '' } = useProjectApiUrl({ projectRef: ref })
const keyToShow = !!showApiKey ? showApiKey : 'SUPABASE_KEY'
const resourcePaths = paths?.[`/${resourceId}`]
diff --git a/apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionDetails.tsx b/apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionDetails.tsx
index 40f8fd83fa068..3da5c1a0adaed 100644
--- a/apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionDetails.tsx
+++ b/apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionDetails.tsx
@@ -9,29 +9,30 @@ import { useEffect, useMemo, useState } from 'react'
import { SubmitHandler, useForm } from 'react-hook-form'
import { toast } from 'sonner'
import {
+ Alert_Shadcn_,
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
- Alert_Shadcn_,
Button,
Card,
CardContent,
CardFooter,
+ cn,
CodeBlock,
+ copyToClipboard,
CriticalIcon,
+ Form_Shadcn_,
FormControl_Shadcn_,
FormField_Shadcn_,
- Form_Shadcn_,
Switch,
Tabs_Shadcn_ as Tabs,
TabsContent_Shadcn_ as TabsContent,
TabsList_Shadcn_ as TabsList,
TabsTrigger_Shadcn_ as TabsTrigger,
- cn,
- copyToClipboard,
} from 'ui'
import { GenericSkeletonLoader } from 'ui-patterns'
import { Input } from 'ui-patterns/DataInputs/Input'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
+import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { PageContainer } from 'ui-patterns/PageContainer'
import {
PageSection,
@@ -40,7 +41,6 @@ import {
PageSectionSummary,
PageSectionTitle,
} from 'ui-patterns/PageSection'
-import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import z from 'zod'
import CommandRender from '../CommandRender'
@@ -48,8 +48,7 @@ import { INVOCATION_TABS } from './EdgeFunctionDetails.constants'
import { generateCLICommands } from './EdgeFunctionDetails.utils'
import AlertError from '@/components/ui/AlertError'
import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
-import { useProjectSettingsV2Query } from '@/data/config/project-settings-v2-query'
-import { useCustomDomainsQuery } from '@/data/custom-domains/custom-domains-query'
+import { useProjectApiUrl } from '@/data/config/project-endpoint-query'
import { useEdgeFunctionQuery } from '@/data/edge-functions/edge-function-query'
import { useEdgeFunctionDeleteMutation } from '@/data/edge-functions/edge-functions-delete-mutation'
import { useEdgeFunctionUpdateMutation } from '@/data/edge-functions/edge-functions-update-mutation'
@@ -86,24 +85,18 @@ export const EdgeFunctionDetails = () => {
const canUpdateEdgeFunction = IS_PLATFORM && canUpdateEdgeFunctionPermission
const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*')
- const { data: apiKeys } = useAPIKeysQuery(
- {
- projectRef,
- },
- { enabled: canReadAPIKeys }
- )
- const { data: settings } = useProjectSettingsV2Query({ projectRef })
- const { data: customDomainData } = useCustomDomainsQuery({ projectRef })
+ const { data: apiKeys } = useAPIKeysQuery({ projectRef }, { enabled: canReadAPIKeys })
+
const {
data: selectedFunction,
error,
isPending: isLoading,
isError,
isSuccess,
- } = useEdgeFunctionQuery({
- projectRef,
- slug: functionSlug,
- })
+ } = useEdgeFunctionQuery({ projectRef, slug: functionSlug })
+
+ const { data: endpoint } = useProjectApiUrl({ projectRef })
+ const functionUrl = `${endpoint}/functions/v1/${selectedFunction?.slug}`
const { mutate: updateEdgeFunction, isPending: isUpdating } = useEdgeFunctionUpdateMutation()
const { mutate: deleteEdgeFunction, isPending: isDeleting } = useEdgeFunctionDeleteMutation({
@@ -121,12 +114,6 @@ export const EdgeFunctionDetails = () => {
const { anonKey, publishableKey } = getKeys(apiKeys)
const apiKey = publishableKey?.api_key ?? anonKey?.api_key ?? '[YOUR ANON KEY]'
- const protocol = settings?.app_config?.protocol ?? 'https'
- const endpoint = settings?.app_config?.endpoint ?? ''
- const functionUrl =
- customDomainData?.customDomain?.status === 'active'
- ? `https://${customDomainData.customDomain.hostname}/functions/v1/${selectedFunction?.slug}`
- : `${protocol}://${endpoint}/functions/v1/${selectedFunction?.slug}`
const hasImportMap = useMemo(
() => selectedFunction?.import_map || selectedFunction?.import_map_path,
[selectedFunction]
diff --git a/apps/studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx b/apps/studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx
index 359948879a717..910d4f419488b 100644
--- a/apps/studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx
+++ b/apps/studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx
@@ -4,11 +4,10 @@ import dayjs from 'dayjs'
import { Check, Copy } from 'lucide-react'
import { useRouter } from 'next/router'
import { useState } from 'react'
-import { TableCell, TableRow, copyToClipboard } from 'ui'
+import { copyToClipboard, TableCell, TableRow } from 'ui'
import { TimestampInfo } from 'ui-patterns'
-import { useProjectSettingsV2Query } from '@/data/config/project-settings-v2-query'
-import { useCustomDomainsQuery } from '@/data/custom-domains/custom-domains-query'
+import { useProjectApiUrl } from '@/data/config/project-endpoint-query'
import type { EdgeFunctionsResponse } from '@/data/edge-functions/edge-functions-query'
import { createNavigationHandler } from '@/lib/navigation'
@@ -21,15 +20,8 @@ export const EdgeFunctionsListItem = ({ function: item }: EdgeFunctionsListItemP
const { ref } = useParams()
const [isCopied, setIsCopied] = useState(false)
- const { data: settings } = useProjectSettingsV2Query({ projectRef: ref })
- const { data: customDomainData } = useCustomDomainsQuery({ projectRef: ref })
-
- const protocol = settings?.app_config?.protocol ?? 'https'
- const endpoint = settings?.app_config?.endpoint ?? ''
- const functionUrl =
- customDomainData?.customDomain?.status === 'active'
- ? `https://${customDomainData.customDomain.hostname}/functions/v1/${item.slug}`
- : `${protocol}://${endpoint}/functions/v1/${item.slug}`
+ const { data: endpoint } = useProjectApiUrl({ projectRef: ref })
+ const functionUrl = `${endpoint}/functions/v1/${item.slug}`
const handleNavigation = createNavigationHandler(
`/project/${ref}/functions/${item.slug}${IS_PLATFORM ? '' : `/details`}`,
diff --git a/apps/studio/components/interfaces/Functions/TerminalInstructions.tsx b/apps/studio/components/interfaces/Functions/TerminalInstructions.tsx
index 79e77e112b091..f4d82bb4c029b 100644
--- a/apps/studio/components/interfaces/Functions/TerminalInstructions.tsx
+++ b/apps/studio/components/interfaces/Functions/TerminalInstructions.tsx
@@ -1,24 +1,23 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
-import { ExternalLink, Maximize2, Minimize2, Terminal } from 'lucide-react'
-import { useRouter } from 'next/router'
-import { ComponentPropsWithoutRef, ElementRef, forwardRef, useState } from 'react'
-
import { useParams } from 'common'
import CommandRender from 'components/interfaces/Functions/CommandRender'
import { DocsButton } from 'components/ui/DocsButton'
import { useAccessTokensQuery } from 'data/access-tokens/access-tokens-query'
import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
-import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
-import { useCustomDomainsQuery } from 'data/custom-domains/custom-domains-query'
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { DOCS_URL } from 'lib/constants'
+import { ExternalLink, Maximize2, Minimize2, Terminal } from 'lucide-react'
+import { useRouter } from 'next/router'
+import { ComponentPropsWithoutRef, ElementRef, forwardRef, useState } from 'react'
import {
Button,
+ Collapsible_Shadcn_,
CollapsibleContent_Shadcn_,
CollapsibleTrigger_Shadcn_,
- Collapsible_Shadcn_,
} from 'ui'
+
import type { Commands } from './Functions.types'
+import { useProjectApiUrl } from '@/data/config/project-endpoint-query'
interface TerminalInstructionsProps extends ComponentPropsWithoutRef
{
closable?: boolean
@@ -36,19 +35,13 @@ export const TerminalInstructions = forwardRef<
const { data: tokens } = useAccessTokensQuery()
const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*')
const { data: apiKeys } = useAPIKeysQuery({ projectRef }, { enabled: canReadAPIKeys })
- const { data: settings } = useProjectSettingsV2Query({ projectRef })
- const { data: customDomainData } = useCustomDomainsQuery({ projectRef })
+
+ const { data: endpoint } = useProjectApiUrl({ projectRef })
+ const functionsEndpoint = `${endpoint}/functions/v1`
const { anonKey, publishableKey } = getKeys(apiKeys)
const apiKey = publishableKey?.api_key ?? anonKey?.api_key ?? '[YOUR ANON KEY]'
- const protocol = settings?.app_config?.protocol ?? 'https'
- const endpoint = settings?.app_config?.endpoint ?? ''
- const functionsEndpoint =
- customDomainData?.customDomain?.status === 'active'
- ? `https://${customDomainData.customDomain.hostname}/functions/v1`
- : `${protocol}://${endpoint}/functions/v1`
-
// get the .co or .net TLD from the restUrl
const restUrl = `https://${endpoint}`
const restUrlTld = !!endpoint ? new URL(restUrl).hostname.split('.').pop() : 'co'
diff --git a/apps/studio/components/interfaces/HomeNew/ProjectConnectionHoverCard.tsx b/apps/studio/components/interfaces/HomeNew/ProjectConnectionHoverCard.tsx
index 91aa79824e5c3..82c8e4bdf8f38 100644
--- a/apps/studio/components/interfaces/HomeNew/ProjectConnectionHoverCard.tsx
+++ b/apps/studio/components/interfaces/HomeNew/ProjectConnectionHoverCard.tsx
@@ -2,7 +2,6 @@ import { PermissionAction } from '@supabase/shared-types/out/constants'
import { Label } from '@ui/components/shadcn/ui/label'
import { getConnectionStrings } from 'components/interfaces/Connect/DatabaseSettings.utils'
import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
-import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
import { useReadReplicasQuery } from 'data/read-replicas/replicas-query'
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { pluckObjectFields } from 'lib/helpers'
@@ -10,8 +9,11 @@ import { Plug } from 'lucide-react'
import { parseAsBoolean, useQueryState } from 'nuqs'
import { useMemo, useState, type ReactNode } from 'react'
import { Button, HoverCard, HoverCardContent, HoverCardTrigger } from 'ui'
+import { ShimmeringLoader } from 'ui-patterns'
import { Input } from 'ui-patterns/DataInputs/Input'
+import { useProjectApiUrl } from '@/data/config/project-endpoint-query'
+
const DB_FIELDS = ['db_host', 'db_name', 'db_port', 'db_user'] as const
const EMPTY_CONNECTION_INFO = {
db_user: '',
@@ -37,32 +39,23 @@ export const ProjectConnectionHoverCard = ({ projectRef }: ProjectConnectionHove
const [open, setOpen] = useState(false)
const [, setShowConnect] = useQueryState('showConnect', parseAsBoolean.withDefault(false))
- const { data: settings, isPending: isLoadingSettings } = useProjectSettingsV2Query(
- { projectRef },
- { enabled: !!projectRef }
- )
-
- const protocol = settings?.app_config?.protocol ?? 'https'
- const endpoint = settings?.app_config?.endpoint
- const projectUrl = endpoint ? `${protocol}://${endpoint}` : undefined
-
const { isLoading: isLoadingPermissions, can: canReadAPIKeys } = useAsyncCheckPermissions(
PermissionAction.READ,
'service_api_keys'
)
+ const { data: projectUrl, isPending: isLoadingApiUrl } = useProjectApiUrl({ projectRef })
+
const { data: apiKeys, isLoading: isLoadingKeys } = useAPIKeysQuery(
{ projectRef },
{ enabled: open && canReadAPIKeys }
)
-
const { publishableKey } = canReadAPIKeys ? getKeys(apiKeys) : { publishableKey: null }
const { data: databases, isLoading: isLoadingDatabases } = useReadReplicasQuery(
{ projectRef },
{ enabled: open && !!projectRef }
)
-
const primaryDatabase = databases?.find((db) => db.identifier === projectRef)
const directConnectionString = useMemo(() => {
@@ -81,9 +74,6 @@ export const ProjectConnectionHoverCard = ({ projectRef }: ProjectConnectionHove
}).direct.uri
}, [primaryDatabase, projectRef])
- const projectUrlLabel =
- projectUrl ?? (isLoadingSettings ? 'Loading project URL...' : 'Project URL unavailable')
-
return (
@@ -91,9 +81,13 @@ export const ProjectConnectionHoverCard = ({ projectRef }: ProjectConnectionHove
-
- {projectUrlLabel}
-
+ {isLoadingApiUrl ? (
+
+ ) : (
+
+ {projectUrl ?? 'Project URL unavailable'}
+
+ )}
diff --git a/apps/studio/components/interfaces/Integrations/DataApi/DataApi.utils.test.ts b/apps/studio/components/interfaces/Integrations/DataApi/DataApi.utils.test.ts
index 838bc0f72330c..f39388b27dc90 100644
--- a/apps/studio/components/interfaces/Integrations/DataApi/DataApi.utils.test.ts
+++ b/apps/studio/components/interfaces/Integrations/DataApi/DataApi.utils.test.ts
@@ -1,28 +1,10 @@
import { describe, expect, it } from 'vitest'
-import { buildEntityMaps, getApiEndpoint, getProjectApiEndpoint } from './DataApi.utils'
-import type { ProjectSettings } from '@/data/config/project-settings-v2-query'
-import type {
- CustomDomainResponse,
- CustomDomainsData,
-} from '@/data/custom-domains/custom-domains-query'
+import { buildEntityMaps, getApiEndpoint } from './DataApi.utils'
import type { ProjectJsonSchemaPaths } from '@/data/docs/project-json-schema-query'
import type { LoadBalancer } from '@/data/read-replicas/load-balancers-query'
import type { Database } from '@/data/read-replicas/replicas-query'
-const makeCustomDomainData = (hostname: string): CustomDomainsData => ({
- customDomain: {
- id: '',
- ssl: {} as CustomDomainResponse['ssl'],
- hostname,
- status: 'active',
- created_at: '',
- custom_metadata: null,
- custom_origin_server: '',
- },
- status: '5_services_reconfigured',
-})
-
const makeDatabase = (
identifier: string,
restUrl: string
@@ -30,71 +12,13 @@ const makeDatabase = (
const makeLoadBalancer = (endpoint: string): Pick => ({ endpoint })
-describe('getProjectApiEndpoint', () => {
- it('returns custom domain URL when custom domain is active', () => {
- expect(
- getProjectApiEndpoint({
- settings: undefined,
- customDomainData: makeCustomDomainData('api.example.com'),
- })
- ).toBe('https://api.example.com')
- })
-
- it('returns settings-based URL when no custom domain', () => {
- expect(
- getProjectApiEndpoint({
- settings: {
- app_config: { protocol: 'https', endpoint: 'abc.supabase.co' },
- } as ProjectSettings,
- customDomainData: undefined,
- })
- ).toBe('https://abc.supabase.co')
- })
-
- it('respects protocol from settings', () => {
- expect(
- getProjectApiEndpoint({
- settings: {
- app_config: { protocol: 'http', endpoint: 'localhost:54321' },
- } as ProjectSettings,
- customDomainData: undefined,
- })
- ).toBe('http://localhost:54321')
- })
-
- it('returns placeholder when settings are undefined', () => {
- expect(
- getProjectApiEndpoint({
- settings: undefined,
- customDomainData: undefined,
- })
- ).toBe('https://-')
- })
-
- it('ignores inactive custom domain', () => {
- const inactiveCustomDomain: CustomDomainsData = {
- customDomain: null,
- status: '0_no_hostname_configured',
- }
-
- expect(
- getProjectApiEndpoint({
- settings: {
- app_config: { protocol: 'https', endpoint: 'abc.supabase.co' },
- } as ProjectSettings,
- customDomainData: inactiveCustomDomain,
- })
- ).toBe('https://abc.supabase.co')
- })
-})
-
describe('getApiEndpoint', () => {
it('returns custom domain URL when custom domain is active and primary database is selected', () => {
expect(
getApiEndpoint({
selectedDatabaseId: 'project-ref',
projectRef: 'project-ref',
- customDomainData: makeCustomDomainData('api.example.com'),
+ resolvedEndpoint: 'https://api.example.com',
loadBalancers: undefined,
selectedDatabase: makeDatabase(
'project-ref',
@@ -109,7 +33,7 @@ describe('getApiEndpoint', () => {
getApiEndpoint({
selectedDatabaseId: 'replica-1',
projectRef: 'project-ref',
- customDomainData: makeCustomDomainData('api.example.com'),
+ resolvedEndpoint: 'https://api.example.com',
loadBalancers: undefined,
selectedDatabase: makeDatabase(
'replica-1',
@@ -124,7 +48,7 @@ describe('getApiEndpoint', () => {
getApiEndpoint({
selectedDatabaseId: 'load-balancer',
projectRef: 'project-ref',
- customDomainData: undefined,
+ resolvedEndpoint: 'https://project-ref.supabase.co',
loadBalancers: [makeLoadBalancer('https://lb.supabase.co') as LoadBalancer],
selectedDatabase: undefined,
})
@@ -136,46 +60,26 @@ describe('getApiEndpoint', () => {
getApiEndpoint({
selectedDatabaseId: 'load-balancer',
projectRef: 'project-ref',
- customDomainData: undefined,
+ resolvedEndpoint: 'https://project-ref.supabase.co',
loadBalancers: undefined,
selectedDatabase: undefined,
})
).toBe('')
})
- it('returns database restUrl for a normal database selection', () => {
+ it('returns database restUrl for a replica database selection', () => {
expect(
getApiEndpoint({
- selectedDatabaseId: 'project-ref',
+ selectedDatabaseId: 'replica-2',
projectRef: 'project-ref',
- customDomainData: undefined,
+ resolvedEndpoint: 'https://project-ref.supabase.co',
loadBalancers: undefined,
selectedDatabase: makeDatabase(
- 'project-ref',
- 'https://project-ref.supabase.co/rest/v1'
- ) as Database,
- })
- ).toBe('https://project-ref.supabase.co/rest/v1')
- })
-
- it('ignores custom domain when it is not active', () => {
- const inactiveCustomDomain: CustomDomainsData = {
- customDomain: null,
- status: '0_no_hostname_configured',
- }
-
- expect(
- getApiEndpoint({
- selectedDatabaseId: 'project-ref',
- projectRef: 'project-ref',
- customDomainData: inactiveCustomDomain,
- loadBalancers: undefined,
- selectedDatabase: makeDatabase(
- 'project-ref',
- 'https://project-ref.supabase.co/rest/v1'
+ 'replica-2',
+ 'https://replica-2.supabase.co/rest/v1'
) as Database,
})
- ).toBe('https://project-ref.supabase.co/rest/v1')
+ ).toBe('https://replica-2.supabase.co/rest/v1')
})
})
diff --git a/apps/studio/components/interfaces/Integrations/DataApi/DataApi.utils.ts b/apps/studio/components/interfaces/Integrations/DataApi/DataApi.utils.ts
index 61990329ea510..da0fd5d6be101 100644
--- a/apps/studio/components/interfaces/Integrations/DataApi/DataApi.utils.ts
+++ b/apps/studio/components/interfaces/Integrations/DataApi/DataApi.utils.ts
@@ -1,29 +1,8 @@
-import type { ProjectSettings } from '@/data/config/project-settings-v2-query'
-import type { CustomDomainsData } from '@/data/custom-domains/custom-domains-query'
import type { ProjectJsonSchemaPaths } from '@/data/docs/project-json-schema-query'
import type { LoadBalancer } from '@/data/read-replicas/load-balancers-query'
import type { Database } from '@/data/read-replicas/replicas-query'
import { snakeToCamel } from '@/lib/helpers'
-/**
- * Resolves the primary project API endpoint, respecting custom domains.
- */
-export function getProjectApiEndpoint({
- settings,
- customDomainData,
-}: {
- settings: ProjectSettings | undefined
- customDomainData: CustomDomainsData | undefined
-}): string {
- if (customDomainData?.customDomain?.status === 'active') {
- return `https://${customDomainData.customDomain.hostname}`
- }
-
- const protocol = settings?.app_config?.protocol ?? 'https'
- const endpoint = settings?.app_config?.endpoint
- return `${protocol}://${endpoint ?? '-'}`
-}
-
/**
* Resolves the API endpoint URL based on the selected database, custom domain
* status, and load balancer configuration.
@@ -31,21 +10,20 @@ export function getProjectApiEndpoint({
export function getApiEndpoint({
selectedDatabaseId,
projectRef,
- customDomainData,
+ resolvedEndpoint,
loadBalancers,
selectedDatabase,
}: {
selectedDatabaseId: string | undefined
projectRef: string | undefined
- customDomainData: CustomDomainsData | undefined
+ resolvedEndpoint: string | undefined
loadBalancers: Array | undefined
selectedDatabase: Database | undefined
}): string {
- const isCustomDomainActive = customDomainData?.customDomain?.status === 'active'
const loadBalancerSelected = selectedDatabaseId === 'load-balancer'
- if (isCustomDomainActive && selectedDatabaseId === projectRef) {
- return `https://${customDomainData.customDomain.hostname}`
+ if (selectedDatabaseId === projectRef && !!resolvedEndpoint) {
+ return resolvedEndpoint
}
if (loadBalancerSelected) {
diff --git a/apps/studio/components/interfaces/Integrations/DataApi/DocView.tsx b/apps/studio/components/interfaces/Integrations/DataApi/DocView.tsx
index 7316245f00e49..8950db2a349ce 100644
--- a/apps/studio/components/interfaces/Integrations/DataApi/DocView.tsx
+++ b/apps/studio/components/interfaces/Integrations/DataApi/DocView.tsx
@@ -4,14 +4,10 @@ import type { ShowApiKey } from '../../Docs/Docs.types'
import { GeneralContent } from '@/components/interfaces/Docs/GeneralContent'
import { ResourceContent } from '@/components/interfaces/Docs/ResourceContent'
import { RpcContent } from '@/components/interfaces/Docs/RpcContent'
-import {
- buildEntityMaps,
- getProjectApiEndpoint,
-} from '@/components/interfaces/Integrations/DataApi/DataApi.utils'
+import { buildEntityMaps } from '@/components/interfaces/Integrations/DataApi/DataApi.utils'
import { DocViewError } from '@/components/interfaces/Integrations/DataApi/DocViewError'
import { DocViewLoading } from '@/components/interfaces/Integrations/DataApi/DocViewLoading'
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'
interface DocViewProps {
@@ -29,9 +25,6 @@ export const DocView = ({ selectedLang, selectedApiKey }: DocViewProps) => {
isPending: isLoading,
refetch,
} = useProjectJsonSchemaQuery({ projectRef })
- const { data: customDomainData } = useCustomDomainsQuery({ projectRef })
-
- const endpoint = getProjectApiEndpoint({ settings, customDomainData })
const { paths } = jsonSchema || {}
const PAGE_KEY = resource || rpc || page || 'index'
@@ -51,7 +44,6 @@ export const DocView = ({ selectedLang, selectedApiKey }: DocViewProps) => {
{resource ? (
{
if (isLogsNotAvailableBasedOnPlan) {
return (
-
+
{
return (
<>
-
+
{showFilters && (
diff --git a/apps/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx b/apps/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx
index 54194192c4446..dd76e2e0d4deb 100644
--- a/apps/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx
+++ b/apps/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx
@@ -1,10 +1,12 @@
import { NoProjectsOnPaidOrgInfo } from 'components/interfaces/Billing/NoProjectsOnPaidOrgInfo'
-import {
- ScaffoldContainer,
- ScaffoldSection,
- ScaffoldSectionTitle,
-} from 'components/layouts/Scaffold'
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
+import {
+ PageSection,
+ PageSectionContent,
+ PageSectionMeta,
+ PageSectionSummary,
+ PageSectionTitle,
+} from 'ui-patterns/PageSection'
import { OrganizationDeletePanel } from './OrganizationDeletePanel'
import { DataPrivacyForm } from './DataPrivacyForm'
@@ -14,20 +16,43 @@ export const GeneralSettings = () => {
const organizationDeletionEnabled = useIsFeatureEnabled('organizations:delete')
return (
-
+ <>
-
- Organization Details
-
-
+
+
+
+ Organization details
+
+
+
+
+
+
-
- Data Privacy
-
-
+
+
+
+ Data privacy
+
+
+
+
+
+
- {organizationDeletionEnabled && }
-
+ {organizationDeletionEnabled && (
+
+
+
+ Danger zone
+
+
+
+
+
+
+ )}
+ >
)
}
diff --git a/apps/studio/components/interfaces/Organization/GeneralSettings/OrganizationDeletePanel.tsx b/apps/studio/components/interfaces/Organization/GeneralSettings/OrganizationDeletePanel.tsx
index d484b76993bea..a9ddf0d076b2f 100644
--- a/apps/studio/components/interfaces/Organization/GeneralSettings/OrganizationDeletePanel.tsx
+++ b/apps/studio/components/interfaces/Organization/GeneralSettings/OrganizationDeletePanel.tsx
@@ -1,4 +1,3 @@
-import { ScaffoldSection, ScaffoldSectionTitle } from 'components/layouts/Scaffold'
import PartnerManagedResource from 'components/ui/PartnerManagedResource'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { MANAGED_BY } from 'lib/constants/infrastructure'
@@ -8,28 +7,23 @@ import { DeleteOrganizationButton } from './DeleteOrganizationButton'
export const OrganizationDeletePanel = () => {
const { data: selectedOrganization } = useSelectedOrganizationQuery()
- return (
-
- Danger Zone
- {selectedOrganization?.managed_by !== 'vercel-marketplace' ? (
-
-
-
- ) : (
-
- )}
-
+ return selectedOrganization?.managed_by !== 'vercel-marketplace' ? (
+
+
+
+ ) : (
+
)
}
diff --git a/apps/studio/components/interfaces/Organization/OAuthApps/OAuthApps.tsx b/apps/studio/components/interfaces/Organization/OAuthApps/OAuthApps.tsx
index 17312e0b462d5..c340127cf3c99 100644
--- a/apps/studio/components/interfaces/Organization/OAuthApps/OAuthApps.tsx
+++ b/apps/studio/components/interfaces/Organization/OAuthApps/OAuthApps.tsx
@@ -68,7 +68,7 @@ export const OAuthApps = () => {
return (
<>
-
+
diff --git a/apps/studio/components/interfaces/Organization/SSO/SSOConfig.tsx b/apps/studio/components/interfaces/Organization/SSO/SSOConfig.tsx
index 753af528fcc2d..f6a454b40ed3b 100644
--- a/apps/studio/components/interfaces/Organization/SSO/SSOConfig.tsx
+++ b/apps/studio/components/interfaces/Organization/SSO/SSOConfig.tsx
@@ -153,7 +153,7 @@ export const SSOConfig = () => {
}, [ssoConfig, form])
return (
-
+
{isLoadingEntitlement || (hasAccessToSso && isLoadingSSOConfig) ? (
diff --git a/apps/studio/components/interfaces/Organization/SecuritySettings.tsx b/apps/studio/components/interfaces/Organization/SecuritySettings.tsx
index 50162239359a9..51af92d27de89 100644
--- a/apps/studio/components/interfaces/Organization/SecuritySettings.tsx
+++ b/apps/studio/components/interfaces/Organization/SecuritySettings.tsx
@@ -105,7 +105,7 @@ export const SecuritySettings = () => {
}
return (
-
+
{!isPaidPlan ? (
{
{ projectRef: ref },
{ enabled: snap.showProjectApiDocs && canReadAPIKeys }
)
- const { data: settings } = useProjectSettingsV2Query(
- { projectRef: ref },
- { enabled: snap.showProjectApiDocs }
- )
- const { data: customDomainData } = useCustomDomainsQuery(
+
+ const { data: endpoint } = useProjectApiUrl(
{ projectRef: ref },
{ enabled: snap.showProjectApiDocs }
)
@@ -63,12 +59,6 @@ export const ProjectAPIDocs = () => {
const apikey = showKeys
? anonKey?.api_key ?? 'SUPABASE_CLIENT_ANON_KEY'
: 'SUPABASE_CLIENT_ANON_KEY'
- const protocol = settings?.app_config?.protocol ?? 'https'
- const hostEndpoint = settings?.app_config?.endpoint
- const endpoint =
- customDomainData?.customDomain?.status === 'active'
- ? `https://${customDomainData.customDomain?.hostname}`
- : `${protocol}://${hostEndpoint ?? ''}`
return (
{
const { isPending: isLoading } = useSelectedProjectQuery()
@@ -31,7 +31,7 @@ export const DataApiProjectUrlCard = () => {
const [querySource, setQuerySource] = useQueryState('source', parseAsString)
- const { data: customDomainData } = useCustomDomainsQuery({ projectRef })
+ const { data: resolvedEndpoint } = useProjectApiUrl({ projectRef })
const {
data: databases,
isError,
@@ -55,7 +55,7 @@ export const DataApiProjectUrlCard = () => {
const endpoint = getApiEndpoint({
selectedDatabaseId: state.selectedDatabaseId,
projectRef,
- customDomainData,
+ resolvedEndpoint,
loadBalancers,
selectedDatabase,
})
diff --git a/apps/studio/components/interfaces/Settings/Addons/Addons.tsx b/apps/studio/components/interfaces/Settings/Addons/Addons.tsx
index 68dca08580a08..66bcbd2b07af8 100644
--- a/apps/studio/components/interfaces/Settings/Addons/Addons.tsx
+++ b/apps/studio/components/interfaces/Settings/Addons/Addons.tsx
@@ -418,7 +418,9 @@ export const Addons = () => {
!isProjectActive || projectUpdateDisabled || !(canUpdateIPv4 || ipv4)
}
>
- Change dedicated IPv4 address
+ {!!ipv4
+ ? 'Toggle dedicated IPv4 address'
+ : 'Enable dedicated IPv4 address'}
@@ -528,7 +530,7 @@ export const Addons = () => {
},
}}
>
- Change point in time recovery
+ Enable point in time recovery
) : (
{
hasHipaaAddon
}
>
- Change point in time recovery
+ {!!pitr ? 'Change recovery duration' : 'Enable point in time recovery'}
)}
@@ -608,19 +610,28 @@ export const Addons = () => {
? 'Custom domain is enabled'
: 'Custom domain is not enabled'}
-
-
diff --git a/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainActivate.tsx b/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainActivate.tsx
index 7ae4948710253..0b35de9131628 100644
--- a/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainActivate.tsx
+++ b/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainActivate.tsx
@@ -1,6 +1,3 @@
-import { useState } from 'react'
-import { toast } from 'sonner'
-
import { DocsButton } from 'components/ui/DocsButton'
import Panel from 'components/ui/Panel'
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
@@ -9,16 +6,18 @@ import { useCustomDomainActivateMutation } from 'data/custom-domains/custom-doma
import { useCustomDomainDeleteMutation } from 'data/custom-domains/custom-domains-delete-mutation'
import type { CustomDomainResponse } from 'data/custom-domains/custom-domains-query'
import { DOCS_URL } from 'lib/constants'
+import { useState } from 'react'
+import { toast } from 'sonner'
import { Button } from 'ui'
-import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { Admonition } from 'ui-patterns/admonition'
+import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
export type CustomDomainActivateProps = {
projectRef?: string
customDomain: CustomDomainResponse
}
-const CustomDomainActivate = ({ projectRef, customDomain }: CustomDomainActivateProps) => {
+export const CustomDomainActivate = ({ projectRef, customDomain }: CustomDomainActivateProps) => {
const [isActivateConfirmModalVisible, setIsActivateConfirmModalVisible] = useState(false)
const { data: settings } = useProjectSettingsV2Query({ projectRef })
@@ -134,5 +133,3 @@ const CustomDomainActivate = ({ projectRef, customDomain }: CustomDomainActivate
>
)
}
-
-export default CustomDomainActivate
diff --git a/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainConfig.tsx b/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainConfig.tsx
index bf3c82fc730b4..5edd3a16d38ed 100644
--- a/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainConfig.tsx
+++ b/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainConfig.tsx
@@ -1,5 +1,3 @@
-import { AlertCircle } from 'lucide-react'
-
import { SupportCategories } from '@supabase/shared-types/out/constants'
import { useFlag, useParams } from 'common'
import { SupportLink } from 'components/interfaces/Support/SupportLink'
@@ -11,6 +9,7 @@ import {
} from 'data/custom-domains/custom-domains-query'
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
+import { AlertCircle } from 'lucide-react'
import { Card, CardContent } from 'ui'
import {
PageSection,
@@ -20,11 +19,12 @@ import {
PageSectionSummary,
PageSectionTitle,
} from 'ui-patterns/PageSection'
-import CustomDomainActivate from './CustomDomainActivate'
-import CustomDomainDelete from './CustomDomainDelete'
-import CustomDomainVerify from './CustomDomainVerify'
-import CustomDomainsConfigureHostname from './CustomDomainsConfigureHostname'
-import CustomDomainsShimmerLoader from './CustomDomainsShimmerLoader'
+
+import { CustomDomainActivate } from './CustomDomainActivate'
+import { CustomDomainDelete } from './CustomDomainDelete'
+import { CustomDomainsConfigureHostname } from './CustomDomainsConfigureHostname'
+import { CustomDomainsShimmerLoader } from './CustomDomainsShimmerLoader'
+import { CustomDomainVerify } from './CustomDomainVerify'
export const CustomDomainConfig = () => {
const { ref } = useParams()
diff --git a/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainDelete.tsx b/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainDelete.tsx
index c0e128e9fd44c..da82b6dc8284c 100644
--- a/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainDelete.tsx
+++ b/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainDelete.tsx
@@ -1,12 +1,11 @@
-import { useState } from 'react'
-import { toast } from 'sonner'
-
import { DocsButton } from 'components/ui/DocsButton'
import Panel from 'components/ui/Panel'
import { useCustomDomainDeleteMutation } from 'data/custom-domains/custom-domains-delete-mutation'
import type { CustomDomainResponse } from 'data/custom-domains/custom-domains-query'
import { DOCS_URL } from 'lib/constants'
import { Trash } from 'lucide-react'
+import { useState } from 'react'
+import { toast } from 'sonner'
import { Button } from 'ui'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
@@ -15,7 +14,7 @@ export type CustomDomainDeleteProps = {
customDomain: CustomDomainResponse
}
-const CustomDomainDelete = ({ projectRef, customDomain }: CustomDomainDeleteProps) => {
+export const CustomDomainDelete = ({ projectRef, customDomain }: CustomDomainDeleteProps) => {
const [isDeleteConfirmModalVisible, setIsDeleteConfirmModalVisible] = useState(false)
const { mutate: deleteCustomDomain, isPending: isDeletingCustomDomain } =
useCustomDomainDeleteMutation({
@@ -85,5 +84,3 @@ const CustomDomainDelete = ({ projectRef, customDomain }: CustomDomainDeleteProp
>
)
}
-
-export default CustomDomainDelete
diff --git a/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainVerify.tsx b/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainVerify.tsx
index 943ac726035f2..0303780c2752a 100644
--- a/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainVerify.tsx
+++ b/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainVerify.tsx
@@ -1,6 +1,3 @@
-import { AlertCircle, RefreshCw } from 'lucide-react'
-import { toast } from 'sonner'
-
import { useParams } from 'common'
import { DocsButton } from 'components/ui/DocsButton'
import { InlineLink } from 'components/ui/InlineLink'
@@ -10,19 +7,22 @@ import { useCustomDomainDeleteMutation } from 'data/custom-domains/custom-domain
import { useCustomDomainsQuery } from 'data/custom-domains/custom-domains-query'
import { useCustomDomainReverifyQuery } from 'data/custom-domains/custom-domains-reverify-query'
import { DOCS_URL } from 'lib/constants'
+import { AlertCircle, RefreshCw } from 'lucide-react'
import { useEffect } from 'react'
+import { toast } from 'sonner'
import {
+ Alert_Shadcn_,
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
- Alert_Shadcn_,
Button,
WarningIcon,
} from 'ui'
import { Admonition } from 'ui-patterns/admonition'
+
import DNSRecord from './DNSRecord'
import { DNSTableHeaders } from './DNSTableHeaders'
-const CustomDomainVerify = () => {
+export const CustomDomainVerify = () => {
const { ref: projectRef } = useParams()
const { data: settings } = useProjectSettingsV2Query({ projectRef })
@@ -208,5 +208,3 @@ const CustomDomainVerify = () => {
>
)
}
-
-export default CustomDomainVerify
diff --git a/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainsConfigureHostname.tsx b/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainsConfigureHostname.tsx
index cd0ad8a982d90..5555efc79f6c4 100644
--- a/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainsConfigureHostname.tsx
+++ b/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainsConfigureHostname.tsx
@@ -1,8 +1,5 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { PermissionAction } from '@supabase/shared-types/out/constants'
-import { useForm } from 'react-hook-form'
-import { z } from 'zod'
-
import { useParams } from 'common'
import { DocsButton } from 'components/ui/DocsButton'
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
@@ -11,6 +8,7 @@ import { useCustomDomainCreateMutation } from 'data/custom-domains/custom-domain
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { DOCS_URL } from 'lib/constants'
+import { useForm } from 'react-hook-form'
import {
Button,
Card,
@@ -18,19 +16,19 @@ import {
CardFooter,
CardHeader,
CardTitle,
+ Form_Shadcn_,
FormControl_Shadcn_,
FormField_Shadcn_,
- FormMessage_Shadcn_,
- Form_Shadcn_,
Input_Shadcn_,
} from 'ui'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
+import { z } from 'zod'
const schema = z.object({
domain: z.string().trim().min(1, 'A value for your custom domain is required'),
})
-const CustomDomainsConfigureHostname = () => {
+export const CustomDomainsConfigureHostname = () => {
const { ref } = useParams()
const { data: project } = useSelectedProjectQuery()
@@ -102,7 +100,6 @@ const CustomDomainsConfigureHostname = () => {
autoComplete="off"
/>
-
)}
/>
@@ -154,5 +151,3 @@ const CustomDomainsConfigureHostname = () => {
)
}
-
-export default CustomDomainsConfigureHostname
diff --git a/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainsShimmerLoader.tsx b/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainsShimmerLoader.tsx
index c4f5af2aeb3dd..c942e8e48f9c3 100644
--- a/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainsShimmerLoader.tsx
+++ b/apps/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainsShimmerLoader.tsx
@@ -1,4 +1,4 @@
-const CustomDomainsShimmerLoader = () => {
+export const CustomDomainsShimmerLoader = () => {
return (
@@ -11,5 +11,3 @@ const CustomDomainsShimmerLoader = () => {
)
}
-
-export default CustomDomainsShimmerLoader
diff --git a/apps/studio/components/interfaces/Sidebar.tsx b/apps/studio/components/interfaces/Sidebar.tsx
index d5686415597bc..ef2229ea81ac9 100644
--- a/apps/studio/components/interfaces/Sidebar.tsx
+++ b/apps/studio/components/interfaces/Sidebar.tsx
@@ -373,6 +373,14 @@ const OrganizationLinks = () => {
const showBilling = useIsFeatureEnabled('billing:all')
const activeRoute = router.pathname.split('/')[3]
+ const organizationSettingsRoutes = new Set([
+ 'general',
+ 'security',
+ 'sso',
+ 'apps',
+ 'audit',
+ 'documents',
+ ])
const navMenuItems = [
{
@@ -410,7 +418,7 @@ const OrganizationLinks = () => {
]
: []),
{
- label: 'Organization settings',
+ label: 'Organization Settings',
href: `/org/${organizationSlug}/general`,
key: 'settings',
icon:
,
@@ -429,11 +437,7 @@ const OrganizationLinks = () => {
i === 0
? activeRoute === undefined
: item.key === 'settings'
- ? router.pathname.includes('/general') ||
- router.pathname.includes('/apps') ||
- router.pathname.includes('/audit') ||
- router.pathname.includes('/documents') ||
- router.pathname.includes('/security')
+ ? organizationSettingsRoutes.has(activeRoute ?? '')
: activeRoute === item.key
}
route={{
diff --git a/apps/studio/components/interfaces/Storage/StorageExplorer/useCopyUrl.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/useCopyUrl.tsx
index ed94cfbddcc7d..c6c70036a0160 100644
--- a/apps/studio/components/interfaces/Storage/StorageExplorer/useCopyUrl.tsx
+++ b/apps/studio/components/interfaces/Storage/StorageExplorer/useCopyUrl.tsx
@@ -1,22 +1,18 @@
import { useCallback } from 'react'
import { toast } from 'sonner'
-
-import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
-import { useCustomDomainsQuery } from 'data/custom-domains/custom-domains-query'
import { useStorageExplorerStateSnapshot } from 'state/storage-explorer'
import { copyToClipboard } from 'ui'
+
import { URL_EXPIRY_DURATION } from '../Storage.constants'
import { getPathAlongOpenedFolders } from './StorageExplorer.utils'
import { fetchFileUrl } from './useFetchFileUrlQuery'
+import { useProjectApiUrl } from '@/data/config/project-endpoint-query'
export const useCopyUrl = () => {
const { projectRef, selectedBucket, openedFolders } = useStorageExplorerStateSnapshot()
- const { data: customDomainData } = useCustomDomainsQuery({ projectRef: projectRef })
- const { data: settings } = useProjectSettingsV2Query({ projectRef: projectRef })
- const protocol = settings?.app_config?.protocol ?? 'https'
- const endpoint = settings?.app_config?.endpoint
- const apiUrl = `${protocol}://${endpoint ?? '-'}`
+ const { hostEndpoint, customEndpoint } = useProjectApiUrl({ projectRef })
+ const isCustomDomainActive = !!customEndpoint
const getFileUrl = useCallback(
(fileName: string, expiresIn?: URL_EXPIRY_DURATION) => {
@@ -37,8 +33,8 @@ export const useCopyUrl = () => {
const onCopyUrl = useCallback(
(name: string, expiresIn?: URL_EXPIRY_DURATION) => {
const formattedUrl = getFileUrl(name, expiresIn).then((url) => {
- return customDomainData?.customDomain?.status === 'active'
- ? url.replace(apiUrl, `https://${customDomainData.customDomain.hostname}`)
+ return isCustomDomainActive && hostEndpoint
+ ? url.replace(hostEndpoint, customEndpoint)
: url
})
@@ -46,12 +42,7 @@ export const useCopyUrl = () => {
toast.success(`Copied URL for ${name} to clipboard.`)
})
},
- [
- apiUrl,
- customDomainData?.customDomain?.hostname,
- customDomainData?.customDomain?.status,
- getFileUrl,
- ]
+ [customEndpoint, getFileUrl, hostEndpoint, isCustomDomainActive]
)
return { onCopyUrl }
diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/ApiAccessToggle.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/ApiAccessToggle.tsx
index 5083475f8328d..3866556071227 100644
--- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/ApiAccessToggle.tsx
+++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/ApiAccessToggle.tsx
@@ -1,3 +1,8 @@
+import { useLoadBalancersQuery } from 'data/read-replicas/load-balancers-query'
+import { useReadReplicasQuery } from 'data/read-replicas/replicas-query'
+import { useIsSchemaExposed } from 'hooks/misc/useIsSchemaExposed'
+import { useQuerySchemaState } from 'hooks/misc/useSchemaQueryState'
+import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { Settings } from 'lucide-react'
import Link from 'next/link'
import {
@@ -10,7 +15,19 @@ import {
type SetStateAction,
} from 'react'
import { usePreviousDistinct } from 'react-use'
+import { Button, Popover_Shadcn_, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_, Switch } from 'ui'
+import { Admonition } from 'ui-patterns'
+import { Input } from 'ui-patterns/DataInputs/Input'
+import { InfoTooltip } from 'ui-patterns/info-tooltip'
+import {
+ MultiSelector,
+ MultiSelectorContent,
+ MultiSelectorItem,
+ MultiSelectorList,
+ MultiSelectorTrigger,
+} from 'ui-patterns/multi-select'
+import { useProjectApiUrl } from '@/data/config/project-endpoint-query'
import { useTableApiAccessQuery } from '@/data/privileges/table-api-access-query'
import { useStaticEffectEvent } from '@/hooks/useStaticEffectEvent'
import {
@@ -25,23 +42,6 @@ import {
} from '@/lib/data-api-types'
import type { DeepReadonly, Prettify } from '@/lib/type-helpers'
import { useDatabaseSelectorStateSnapshot } from '@/state/database-selector'
-import { useCustomDomainsQuery } from 'data/custom-domains/custom-domains-query'
-import { useLoadBalancersQuery } from 'data/read-replicas/load-balancers-query'
-import { useReadReplicasQuery } from 'data/read-replicas/replicas-query'
-import { useIsSchemaExposed } from 'hooks/misc/useIsSchemaExposed'
-import { useQuerySchemaState } from 'hooks/misc/useSchemaQueryState'
-import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
-import { Button, Popover_Shadcn_, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_, Switch } from 'ui'
-import { Admonition } from 'ui-patterns'
-import { Input } from 'ui-patterns/DataInputs/Input'
-import { InfoTooltip } from 'ui-patterns/info-tooltip'
-import {
- MultiSelector,
- MultiSelectorContent,
- MultiSelectorItem,
- MultiSelectorList,
- MultiSelectorTrigger,
-} from 'ui-patterns/multi-select'
const ROLE_LABELS: Record
= {
anon: 'Anonymous (anon)',
@@ -390,6 +390,7 @@ export const ApiAccessToggle = ({