Files
supabase/apps/studio/components/layouts/ProjectSettingsLayout/ProjectSettings.Commands.tsx
Ivan Vasilov e10f7cc808 feat: Add a config drift page in Studio (#48906)
## New Features
- Initial work for showing configuration drift in Studio
- This only works for Github-connected projects and it'll show a banner
if the project state differs from the git-tracked `config.toml`
- Currently behind a feature-flag `ConfigDrift`, enabled on local and
staging.
- There might be drift shown without changing any setting, this is
work-in-progress.

<img width="1217" height="1195" alt="Screenshot 2026-08-19 at 23 12 10"
src="https://github.com/user-attachments/assets/fb0b18d8-1a93-4595-85cc-e8b8a3462847"
/>

## How to test
1. Connect a project to a Github repo
2. Resync the branch on `/dashboard/project/_/branches`. This will
trigger deployment of the `config.toml` on your project
3. Change some settings (I recommend
`dashboard/project/_/auth/providers`
4. A banner should appear on all project pages with a link 


## Tests
- Added coverage for configuration conversion, normalization, matching,
drift detection, and unmanaged settings.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-25 13:56:40 +02:00

209 lines
6.8 KiB
TypeScript

import { useFlag, useParams } from 'common'
import { useRouter } from 'next/router'
import type { CommandOptions, ICommand } from 'ui-patterns/CommandMenu'
import { useRegisterCommands, useSetCommandMenuOpen } from 'ui-patterns/CommandMenu'
import { IRouteCommand } from 'ui-patterns/CommandMenu/internal/types'
import { COMMAND_MENU_SECTIONS } from '@/components/interfaces/App/CommandMenu/CommandMenu.utils'
import { useIsPlatformWebhooksEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
import { IS_PLATFORM } from '@/lib/constants'
export function useProjectSettingsGotoCommands(options?: CommandOptions) {
const router = useRouter()
const setIsOpen = useSetCommandMenuOpen()
let { ref, slug } = useParams()
const platformWebhooksEnabled = useIsPlatformWebhooksEnabled()
const showConfigDrift = useFlag('ConfigDrift')
ref ||= '_'
const hasOrgSlug = typeof slug === 'string' && slug.length > 0 && slug !== '_'
const {
logsAll,
projectSettingsLogDrains,
projectSettingsCustomDomains,
authenticationSignInProviders,
} = useIsFeatureEnabled([
'logs:all',
'project_settings:log_drains',
'project_settings:custom_domains',
'authentication:sign_in_providers',
])
// Log drains depend on the analytics backend, gated by logs:all (see SettingsMenu.utils).
const showLogDrains = logsAll && projectSettingsLogDrains
useRegisterCommands(
COMMAND_MENU_SECTIONS.NAVIGATE,
[
{
id: 'nav-project-settings-add-ons',
name: 'Add-ons',
value: 'Add-ons addons add ons add on add-on',
route: `/project/${ref}/settings/addons`,
defaultHidden: true,
},
{
id: 'nav-project-settings-general',
name: 'General Settings',
route: `/project/${ref}/settings/general`,
defaultHidden: true,
},
...(IS_PLATFORM && showConfigDrift
? [
{
id: 'nav-project-settings-code-configuration',
name: 'Code configuration',
route: `/project/${ref}/settings/code-configuration`,
defaultHidden: true,
} as IRouteCommand,
]
: []),
{
id: 'nav-project-settings-database',
name: 'Database Settings',
route: `/project/${ref}/database/settings`,
defaultHidden: true,
},
{
id: 'nav-project-settings-auth',
name: 'Auth Settings',
route: authenticationSignInProviders
? `/project/${ref}/auth/providers`
: `/project/${ref}/database/policies`,
defaultHidden: true,
},
...(platformWebhooksEnabled
? [
{
id: 'nav-project-settings-webhooks',
name: 'Project Webhooks',
route: `/project/${ref}/settings/webhooks`,
defaultHidden: true,
} as IRouteCommand,
...(hasOrgSlug
? [
{
id: 'nav-organization-settings-webhooks',
name: 'Organization Webhooks',
route: `/org/${slug}/webhooks`,
defaultHidden: true,
} as IRouteCommand,
]
: []),
]
: []),
{
id: 'nav-project-settings-api',
name: 'API Settings',
route: `/project/${ref}/integrations/data_api/settings`,
defaultHidden: true,
},
{
id: 'nav-project-settings-storage',
name: 'Storage Settings',
route: `/project/${ref}/storage/settings`,
defaultHidden: true,
},
...(projectSettingsCustomDomains
? [
{
id: 'nav-project-settings-custom-domains',
name: 'Custom Domains',
route: `/project/${ref}/settings/general#custom-domains`,
defaultHidden: true,
} as IRouteCommand,
]
: []),
{
id: 'nav-project-settings-restart-project',
name: 'Project availability',
value: 'project availability restart project pause project resume project',
route: `/project/${ref}/settings/general#restart-project`,
defaultHidden: true,
},
{
id: 'nav-project-settings-pause-project',
name: 'Pause project',
route: `/project/${ref}/settings/general#pause-project`,
defaultHidden: true,
},
{
id: 'nav-project-settings-transfer-project',
name: 'Transfer project',
route: `/project/${ref}/settings/general#transfer-project`,
defaultHidden: true,
},
{
id: 'nav-project-settings-delete-project',
name: 'Delete project',
route: `/project/${ref}/settings/general#delete-project`,
defaultHidden: true,
},
{
id: 'nav-project-settings-database-password',
name: 'Database password',
route: `/project/${ref}/database/settings#database-password`,
defaultHidden: true,
},
{
id: 'nav-project-settings-reset-database-password',
name: 'Reset database password',
route: `/project/${ref}/database/settings#database-password`,
defaultHidden: true,
},
{
id: 'nav-project-settings-connection-string',
name: 'Connection string',
action: () => {
router.push(
{
pathname: router.pathname,
query: { ...router.query, showConnect: 'true' },
},
undefined,
{ shallow: true }
)
setIsOpen(false)
},
defaultHidden: true,
} as ICommand,
{
id: 'nav-project-settings-connection-pooling',
name: 'Connection pooling',
route: `/project/${ref}/database/settings#connection-pooler`,
defaultHidden: true,
},
{
id: 'nav-project-settings-ssl-configuration',
name: 'SSL configuration',
route: `/project/${ref}/database/settings#ssl-configuration`,
defaultHidden: true,
},
{
id: 'nav-project-settings-network-restrictions',
name: 'Network restrictions',
route: `/project/${ref}/database/settings#network-restrictions`,
defaultHidden: true,
},
{
id: 'nav-project-settings-banned-ips',
name: 'Banned IPs',
route: `/project/${ref}/database/settings#banned-ips`,
defaultHidden: true,
},
...(showLogDrains
? [
{
id: 'nav-project-settings-log-drains',
name: 'Log drains',
route: `/project/${ref}/settings/log-drains`,
defaultHidden: true,
} as IRouteCommand,
]
: []),
],
{ ...options, deps: [platformWebhooksEnabled, showLogDrains, showConfigDrift, ref, slug] }
)
}