mirror of
https://github.com/supabase/supabase.git
synced 2026-10-11 04:15:04 +03:00
Adds a new method of installation detection for partners like Doppler. Doppler creates edge function secrets with specific names (`DOPPLER_CONFIG`, `DOPPLER_ENVIRONMENT`, and `DOPPLER_PROJECT`). This method allows the dashboard to check for the presence of such a secret to show the installation status. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Support for edge-function-secret-based OAuth identification and verification. * Installation checks adapt to the selected installation method and only fetch required credentials when needed. * Integration detection can validate installations via API key prefixes or matching edge-function secret names. * **Chores** * Shared helper utilities and types exported to streamline installation checks and mappings. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45826) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
163 lines
5.3 KiB
TypeScript
163 lines
5.3 KiB
TypeScript
import { useMemo } from 'react'
|
|
|
|
import {
|
|
hasMatchingWrapper,
|
|
hasRequiredExtensions,
|
|
isOAuthInstalled,
|
|
isStripeSyncEngineInstalled,
|
|
} from './Landing.utils'
|
|
import { useAvailableIntegrations } from './useAvailableIntegrations'
|
|
import { useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
|
|
import { useDatabaseExtensionsQuery } from '@/data/database-extensions/database-extensions-query'
|
|
import { useSchemasQuery } from '@/data/database/schemas-query'
|
|
import { useFDWsQuery } from '@/data/fdw/fdws-query'
|
|
import { useSecretsQuery } from '@/data/secrets/secrets-query'
|
|
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
|
import { EMPTY_ARR } from '@/lib/void'
|
|
|
|
export const useInstalledIntegrations = () => {
|
|
const { data: project } = useSelectedProjectQuery()
|
|
|
|
const {
|
|
data: allIntegrations = EMPTY_ARR,
|
|
error: availableIntegrationsError,
|
|
isPending: isAvailableIntegrationsLoading,
|
|
isSuccess: isSuccessAvailableIntegrations,
|
|
isError: isErrorAvailableIntegrations,
|
|
} = useAvailableIntegrations()
|
|
|
|
const hasSecretKeyPrefixIntegration = useMemo(() => {
|
|
return allIntegrations.some(
|
|
(integration) =>
|
|
integration.type === 'oauth' &&
|
|
integration.installIdentificationMethod === 'secret_key_prefix' &&
|
|
!!integration.secretKeyPrefix
|
|
)
|
|
}, [allIntegrations])
|
|
|
|
const hasEdgeFunctionSecretNameIntegration = useMemo(() => {
|
|
return allIntegrations.some(
|
|
(integration) =>
|
|
integration.type === 'oauth' &&
|
|
integration.installIdentificationMethod === 'edge_function_secret_name' &&
|
|
!!integration.edgeFunctionSecretName
|
|
)
|
|
}, [allIntegrations])
|
|
|
|
const {
|
|
data: apiKeys = EMPTY_ARR,
|
|
error: apiKeysError,
|
|
isError: isErrorApiKeys,
|
|
isLoading: isApiKeysLoading,
|
|
isSuccess: isSuccessApiKeys,
|
|
} = useAPIKeysQuery(
|
|
{ projectRef: project?.ref, reveal: false },
|
|
{ enabled: hasSecretKeyPrefixIntegration }
|
|
)
|
|
|
|
const {
|
|
data: edgeFunctionSecrets = EMPTY_ARR,
|
|
error: edgeFunctionSecretsError,
|
|
isError: isErrorEdgeFunctionSecrets,
|
|
isLoading: isEdgeFunctionSecretsLoading,
|
|
isSuccess: isSuccessEdgeFunctionSecrets,
|
|
} = useSecretsQuery(
|
|
{ projectRef: project?.ref },
|
|
{ enabled: hasEdgeFunctionSecretNameIntegration }
|
|
)
|
|
|
|
const {
|
|
data: wrappers = EMPTY_ARR,
|
|
error: fdwError,
|
|
isError: isErrorFDWs,
|
|
isPending: isFDWLoading,
|
|
isSuccess: isSuccessFDWs,
|
|
} = useFDWsQuery({
|
|
projectRef: project?.ref,
|
|
connectionString: project?.connectionString,
|
|
})
|
|
const {
|
|
data: extensions = EMPTY_ARR,
|
|
error: extensionsError,
|
|
isError: isErrorExtensions,
|
|
isPending: isExtensionsLoading,
|
|
isSuccess: isSuccessExtensions,
|
|
} = useDatabaseExtensionsQuery({
|
|
projectRef: project?.ref,
|
|
connectionString: project?.connectionString,
|
|
})
|
|
|
|
const {
|
|
data: schemas = EMPTY_ARR,
|
|
error: schemasError,
|
|
isError: isErrorSchemas,
|
|
isPending: isSchemasLoading,
|
|
isSuccess: isSuccessSchemas,
|
|
} = useSchemasQuery({
|
|
projectRef: project?.ref,
|
|
connectionString: project?.connectionString,
|
|
})
|
|
|
|
const isHooksEnabled = schemas?.some((schema) => schema.name === 'supabase_functions')
|
|
|
|
const installedIntegrations = useMemo(() => {
|
|
return allIntegrations
|
|
.filter((integration) => {
|
|
if (integration.id === 'webhooks') return isHooksEnabled
|
|
if (integration.id === 'data_api') return true
|
|
if (integration.id === 'stripe_sync_engine') {
|
|
return isStripeSyncEngineInstalled(schemas)
|
|
}
|
|
if (integration.type === 'wrapper') {
|
|
return hasMatchingWrapper({ meta: integration.meta, wrappers })
|
|
}
|
|
if (integration.type === 'postgres_extension') {
|
|
return hasRequiredExtensions({ integration, extensions })
|
|
}
|
|
if (integration.type === 'oauth') {
|
|
return isOAuthInstalled({ integration, apiKeys, secrets: edgeFunctionSecrets })
|
|
}
|
|
return false
|
|
})
|
|
.sort((a, b) => a.name.localeCompare(b.name))
|
|
}, [allIntegrations, wrappers, extensions, schemas, isHooksEnabled, apiKeys, edgeFunctionSecrets])
|
|
|
|
const error =
|
|
fdwError ||
|
|
extensionsError ||
|
|
schemasError ||
|
|
availableIntegrationsError ||
|
|
(hasSecretKeyPrefixIntegration ? apiKeysError : null) ||
|
|
(hasEdgeFunctionSecretNameIntegration ? edgeFunctionSecretsError : null)
|
|
const isLoading =
|
|
isSchemasLoading ||
|
|
isFDWLoading ||
|
|
isExtensionsLoading ||
|
|
isAvailableIntegrationsLoading ||
|
|
(hasSecretKeyPrefixIntegration && isApiKeysLoading) ||
|
|
(hasEdgeFunctionSecretNameIntegration && isEdgeFunctionSecretsLoading)
|
|
const isError =
|
|
isErrorFDWs ||
|
|
isErrorExtensions ||
|
|
isErrorSchemas ||
|
|
isErrorAvailableIntegrations ||
|
|
(hasSecretKeyPrefixIntegration && isErrorApiKeys) ||
|
|
(hasEdgeFunctionSecretNameIntegration && isErrorEdgeFunctionSecrets)
|
|
const isSuccess =
|
|
isSuccessFDWs &&
|
|
isSuccessExtensions &&
|
|
isSuccessSchemas &&
|
|
isSuccessAvailableIntegrations &&
|
|
(!hasSecretKeyPrefixIntegration || isSuccessApiKeys) &&
|
|
(!hasEdgeFunctionSecretNameIntegration || isSuccessEdgeFunctionSecrets)
|
|
|
|
return {
|
|
// show all integrations at once instead of showing partial results
|
|
installedIntegrations: isLoading ? EMPTY_ARR : installedIntegrations,
|
|
error,
|
|
isError,
|
|
isLoading,
|
|
isSuccess,
|
|
}
|
|
}
|