mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 01:45:10 +03:00
## Problem Health Advisor results appear on the project homepage whenever the main `healthAdvisor` flag is enabled, so the homepage cannot be rolled out separately. The existing gates also contain redundant boolean and platform checks. ## Fix Require both `healthAdvisor` and `healthAdvisorInHomepage` before fetching or displaying health advisories on the homepage. Simplify all Health Advisor gates to use the boolean ConfigCat flag directly, including the cleanup requested in the review of #50326. ## How to test - Enable only `healthAdvisor` and verify Health Advisor remains available outside the homepage while health results do not appear or load on the homepage. - Enable both flags and verify health results appear on the homepage. - Disable `healthAdvisor` and verify Health Advisor remains unavailable everywhere. - Existing targeted tests pass: 5 tests across Advisor menu and panel integration suites. - Prettier and whitespace checks pass. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Health Advisor is now available in non-platform environments when enabled. * Navigation, filtering, lint checks, and project pages consistently follow the Health Advisor feature setting. * Self-hosted environments can use the Health Advisor category. * **Bug Fixes** * Homepage Health Advisor visibility now follows both the Health Advisor and homepage-specific settings. * Updated empty states and health lint results to match the configured availability. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
94 lines
3.5 KiB
TypeScript
94 lines
3.5 KiB
TypeScript
import { useQuery } from '@tanstack/react-query'
|
|
import type { components } from 'api-types'
|
|
import { useFlag } from 'common'
|
|
|
|
import { lintKeys } from './keys'
|
|
import type { Lint } from './lint-query'
|
|
import { handleError, post } from '@/data/fetchers'
|
|
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
|
import { PROJECT_STATUS } from '@/lib/constants'
|
|
import { EMPTY_ARR } from '@/lib/void'
|
|
import type { ResponseError, UseCustomQueryOptions } from '@/types'
|
|
|
|
type AdvisorLintName =
|
|
components['schemas']['V2RunProjectAdvisorsBody']['data']['attributes']['lints'][number]['name']
|
|
|
|
/**
|
|
* Health lints are on-demand only on the API side — each one opens a live database
|
|
* connection or queries the metrics/logs store — so the endpoint runs only the lints it
|
|
* is asked for. We request the checks that mean the project is actually broken, and leave
|
|
* out the diagnostic ones (`db_connection_failing`, `instance_telemetry_lost`), which
|
|
* describe the state of our own probes more than the state of the project.
|
|
*/
|
|
export const HEALTH_LINT_NAMES = [
|
|
'instance_db_down',
|
|
'db_not_reachable',
|
|
'db_connection_limit_reached',
|
|
'log_data_api_error_rate_high',
|
|
'log_auth_error_rate_high',
|
|
'log_storage_error_rate_high',
|
|
'log_edge_function_error_rate_high',
|
|
'instance_alert_firing',
|
|
] as const satisfies readonly AdvisorLintName[]
|
|
|
|
/**
|
|
* Names the API only ever returns as a result: they report that a check could not run
|
|
* rather than an issue with the project, and several checks can return the same one, so
|
|
* they'd show up as duplicate non-issues in the advisor lists.
|
|
*/
|
|
const RESULT_ONLY_LINT_NAMES = new Set(['project_not_active', 'advisor_check_unavailable'])
|
|
|
|
type ProjectHealthLintsVariables = {
|
|
projectRef?: string
|
|
}
|
|
|
|
export async function getProjectHealthLints(
|
|
{ projectRef }: ProjectHealthLintsVariables,
|
|
signal?: AbortSignal
|
|
) {
|
|
if (!projectRef) throw new Error('Project ref is required')
|
|
|
|
const { data, error } = await post('/v2/projects/{ref}/advisors/run', {
|
|
params: { path: { ref: projectRef } },
|
|
body: {
|
|
data: {
|
|
type: 'project_advisors',
|
|
attributes: { lints: HEALTH_LINT_NAMES.map((name) => ({ name })) },
|
|
},
|
|
},
|
|
signal,
|
|
})
|
|
|
|
if (error) handleError(error)
|
|
|
|
const lints: Lint[] = data?.data.attributes.lints ?? EMPTY_ARR
|
|
return lints.filter((lint) => !RESULT_ONLY_LINT_NAMES.has(lint.name))
|
|
}
|
|
|
|
export type ProjectHealthLintsData = Awaited<ReturnType<typeof getProjectHealthLints>>
|
|
export type ProjectHealthLintsError = ResponseError
|
|
|
|
export const useProjectHealthLintsQuery = <TData = ProjectHealthLintsData>(
|
|
{ projectRef }: ProjectHealthLintsVariables,
|
|
{
|
|
enabled = true,
|
|
...options
|
|
}: UseCustomQueryOptions<ProjectHealthLintsData, ProjectHealthLintsError, TData> = {}
|
|
) => {
|
|
const { data: project } = useSelectedProjectQuery()
|
|
const isHealthAdvisorEnabled = useFlag('healthAdvisor')
|
|
const isActive = project?.status === PROJECT_STATUS.ACTIVE_HEALTHY
|
|
|
|
return useQuery<ProjectHealthLintsData, ProjectHealthLintsError, TData>({
|
|
queryKey: lintKeys.healthLints(projectRef),
|
|
queryFn: ({ signal }) => getProjectHealthLints({ projectRef }, signal),
|
|
enabled: enabled && isHealthAdvisorEnabled && typeof projectRef !== 'undefined' && isActive,
|
|
// Every run costs a live database connection plus a metrics and a logs query, so keep
|
|
// repeat mounts (homepage row, advisor panel) on one result and don't retry failures.
|
|
staleTime: 60_000,
|
|
retry: false,
|
|
refetchOnWindowFocus: false,
|
|
...options,
|
|
})
|
|
}
|