mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
feat(studio): show health advisors on the project home (#49661)
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Feature ## Summary - Add a `useProjectHealthLintsQuery` that runs the live health checks (database down, unreachable, connection limit, service error rate, infrastructure alerts) - Surface those results on the project home advisor row alongside security and performance errors - Register health lint metadata (titles, docs links, entity icon) so homepage cards can render them Bottom of the stack. The advisor sidebar still uses tabs; health items show under All until #49662. ## To test 1. Open any project home in Studio. 2. Find the Advisor row (the cards under “Advisor found N issues”). 3. If the project has a real health problem, you should see a **HEALTH** card (for example “Database process is down” or “Database connection limit reached”), not only SECURITY / PERFORMANCE. 4. If the project is healthy, you should **not** see a HEALTH card. Existing security and performance cards should still appear as before. 5. Click a HEALTH card (or any advisor card). Advisor Center should open on that item. 6. In Advisor Center on this PR, health items only show under the **All** tab — Health is not its own tab yet. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added a Health category to Advisor, with a dedicated tab and activity icon. - Added health checks for database availability, connection limits, service errors, and infrastructure alerts. - Health issues now appear alongside security and performance recommendations with relevant troubleshooting links. - **Bug Fixes** - Health-related advisor findings are now correctly categorized and displayed. - **Tests** - Added coverage for health checks, categorization, filtering, and project health query behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
7cf5737d36
commit
ad33b16f8c
13 files changed
+312
-28
No files matched your search
@@ -16,7 +16,7 @@ export type LintInfo = {
|
||||
link: (args: { projectRef: string; metadata: Lint['metadata'] }) => string
|
||||
linkText: string
|
||||
docsLink: string
|
||||
category: 'security' | 'performance'
|
||||
category: 'security' | 'performance' | 'health'
|
||||
}
|
||||
|
||||
export const LINT_TABS: ReadonlyArray<{
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import {
|
||||
Activity,
|
||||
AlertTriangle,
|
||||
Box,
|
||||
Clock,
|
||||
Database,
|
||||
Eye,
|
||||
Gauge,
|
||||
Lock,
|
||||
LockIcon,
|
||||
Ruler,
|
||||
Scaling,
|
||||
Server,
|
||||
Table2,
|
||||
TextSearch,
|
||||
Unlock,
|
||||
@@ -393,6 +398,80 @@ export const lintInfoMap: LintInfo[] = [
|
||||
docsLink: `${DOCS_URL}/guides/database/database-linter?lint=0029_authenticated_security_definer_function_executable`,
|
||||
category: 'security',
|
||||
},
|
||||
// Health lints report on the running project rather than on schema, so they link to the
|
||||
// page that shows what is happening rather than to an object to edit.
|
||||
{
|
||||
name: 'instance_db_down',
|
||||
title: 'Database process is down',
|
||||
icon: <Database className="text-foreground-muted" size={15} strokeWidth={1.5} />,
|
||||
link: ({ projectRef }) => `/project/${projectRef}/logs/postgres-logs`,
|
||||
linkText: 'View database logs',
|
||||
docsLink: `${DOCS_URL}/guides/platform/troubleshooting`,
|
||||
category: 'health',
|
||||
},
|
||||
{
|
||||
name: 'db_not_reachable',
|
||||
title: 'Database not usable',
|
||||
icon: <Server className="text-foreground-muted" size={15} strokeWidth={1.5} />,
|
||||
link: ({ projectRef }) => `/project/${projectRef}/database/settings`,
|
||||
linkText: 'View connection settings',
|
||||
docsLink: `${DOCS_URL}/guides/database/connecting-to-postgres`,
|
||||
category: 'health',
|
||||
},
|
||||
{
|
||||
name: 'db_connection_limit_reached',
|
||||
title: 'Database connection limit reached',
|
||||
icon: <Gauge className="text-foreground-muted" size={15} strokeWidth={1.5} />,
|
||||
link: ({ projectRef }) => `/project/${projectRef}/observability/connections`,
|
||||
linkText: 'View connections',
|
||||
docsLink: `${DOCS_URL}/guides/database/connection-management`,
|
||||
category: 'health',
|
||||
},
|
||||
{
|
||||
name: 'log_data_api_error_rate_high',
|
||||
title: 'Data API error rate is persistently high',
|
||||
icon: <Activity className="text-foreground-muted" size={15} strokeWidth={1.5} />,
|
||||
link: ({ projectRef }) => `/project/${projectRef}/logs/edge-logs`,
|
||||
linkText: 'View logs',
|
||||
docsLink: `${DOCS_URL}/guides/platform/troubleshooting`,
|
||||
category: 'health',
|
||||
},
|
||||
{
|
||||
name: 'log_auth_error_rate_high',
|
||||
title: 'Auth error rate is persistently high',
|
||||
icon: <Activity className="text-foreground-muted" size={15} strokeWidth={1.5} />,
|
||||
link: ({ projectRef }) => `/project/${projectRef}/logs/edge-logs`,
|
||||
linkText: 'View logs',
|
||||
docsLink: `${DOCS_URL}/guides/platform/troubleshooting`,
|
||||
category: 'health',
|
||||
},
|
||||
{
|
||||
name: 'log_storage_error_rate_high',
|
||||
title: 'Storage error rate is persistently high',
|
||||
icon: <Activity className="text-foreground-muted" size={15} strokeWidth={1.5} />,
|
||||
link: ({ projectRef }) => `/project/${projectRef}/logs/edge-logs`,
|
||||
linkText: 'View logs',
|
||||
docsLink: `${DOCS_URL}/guides/platform/troubleshooting`,
|
||||
category: 'health',
|
||||
},
|
||||
{
|
||||
name: 'log_edge_function_error_rate_high',
|
||||
title: 'Edge Function error rate is persistently high',
|
||||
icon: <Activity className="text-foreground-muted" size={15} strokeWidth={1.5} />,
|
||||
link: ({ projectRef }) => `/project/${projectRef}/logs/edge-logs`,
|
||||
linkText: 'View logs',
|
||||
docsLink: `${DOCS_URL}/guides/platform/troubleshooting`,
|
||||
category: 'health',
|
||||
},
|
||||
{
|
||||
name: 'instance_alert_firing',
|
||||
title: 'Infrastructure alerts firing',
|
||||
icon: <AlertTriangle className="text-foreground-muted" size={15} strokeWidth={1.5} />,
|
||||
link: ({ projectRef }) => `/project/${projectRef}/settings/infrastructure`,
|
||||
linkText: 'View infrastructure',
|
||||
docsLink: `${DOCS_URL}/guides/platform/troubleshooting`,
|
||||
category: 'health',
|
||||
},
|
||||
]
|
||||
|
||||
export const LintCTA = ({
|
||||
@@ -444,6 +523,8 @@ export const EntityTypeIcon = ({ type }: { type: string | undefined }) => {
|
||||
return <Eye className="text-foreground-muted" size={15} strokeWidth={1.5} />
|
||||
case 'auth':
|
||||
return <Lock className="text-foreground-muted" size={15} strokeWidth={1.5} />
|
||||
case 'health':
|
||||
return <Activity className="text-foreground-muted" size={15} strokeWidth={1.5} />
|
||||
default:
|
||||
return <Box className="text-foreground-muted" size={15} strokeWidth={1.5} />
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useParams } from 'common'
|
||||
import { BarChart, Shield } from 'lucide-react'
|
||||
import { Shield } from 'lucide-react'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { AiIconAnimation, Badge, Button, Card, CardContent, CardHeader, CardTitle, cn } from 'ui'
|
||||
import { Row } from 'ui-patterns/Row'
|
||||
@@ -17,9 +17,11 @@ import {
|
||||
severityBadgeVariants,
|
||||
severityColorClasses,
|
||||
sortAdvisorItems,
|
||||
tabIconMap,
|
||||
} from '@/components/ui/AdvisorPanel/AdvisorPanel.utils'
|
||||
import { useAdvisorSignals } from '@/components/ui/AdvisorPanel/useAdvisorSignals'
|
||||
import { AiAssistantDropdown } from '@/components/ui/AiAssistantDropdown'
|
||||
import { useProjectHealthLintsQuery } from '@/data/lint/health-lints-query'
|
||||
import { useProjectLintsQuery } from '@/data/lint/lint-query'
|
||||
import { useTrack } from '@/lib/telemetry/track'
|
||||
import { useAdvisorStateSnapshot } from '@/state/advisor-state'
|
||||
@@ -38,15 +40,21 @@ export const AdvisorSection = ({ showEmptyState = false }: { showEmptyState?: bo
|
||||
{ enabled: !showEmptyState }
|
||||
)
|
||||
|
||||
const { data: healthLints } = useProjectHealthLintsQuery(
|
||||
{ projectRef },
|
||||
{ enabled: !showEmptyState }
|
||||
)
|
||||
|
||||
const { data: signalItems } = useAdvisorSignals({ projectRef, enabled: !showEmptyState })
|
||||
|
||||
const advisorItems = useMemo<AdvisorItem[]>(() => {
|
||||
const criticalLintItems = createAdvisorLintItems(lints).filter(
|
||||
(item) => item.source === 'lint' && item.original.level === LINTER_LEVELS.ERROR
|
||||
)
|
||||
const criticalLintItems = createAdvisorLintItems([
|
||||
...(lints ?? []),
|
||||
...(healthLints ?? []),
|
||||
]).filter((item) => item.source === 'lint' && item.original.level === LINTER_LEVELS.ERROR)
|
||||
|
||||
return sortAdvisorItems([...criticalLintItems, ...signalItems])
|
||||
}, [lints, signalItems])
|
||||
}, [lints, healthLints, signalItems])
|
||||
|
||||
const visibleAdvisorItems = useMemo(
|
||||
() => advisorItems.slice(0, MAX_HOMEPAGE_ADVISOR_ITEMS),
|
||||
@@ -136,7 +144,8 @@ export const AdvisorSection = ({ showEmptyState = false }: { showEmptyState?: bo
|
||||
<Row maxColumns={4} minWidth={280}>
|
||||
{visibleAdvisorItems.map((item) => {
|
||||
const isLint = item.source === 'lint'
|
||||
const categoryLabel = item.tab === 'performance' ? 'PERFORMANCE' : 'SECURITY'
|
||||
const categoryLabel = item.tab.toUpperCase()
|
||||
const CategoryIcon = tabIconMap[item.tab]
|
||||
const title = getAdvisorItemDisplayTitle(item)
|
||||
const description =
|
||||
item.source === 'signal' ? item.summary : isLint ? item.original.detail : ''
|
||||
@@ -160,19 +169,11 @@ export const AdvisorSection = ({ showEmptyState = false }: { showEmptyState?: bo
|
||||
>
|
||||
<CardHeader className="border-b-0 shrink-0 flex flex-row gap-2 space-y-0 justify-between items-center">
|
||||
<div className="flex flex-row items-center gap-3">
|
||||
{item.tab === 'security' ? (
|
||||
<Shield
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
className={severityColorClasses[item.severity]}
|
||||
/>
|
||||
) : (
|
||||
<BarChart
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
className={severityColorClasses[item.severity]}
|
||||
/>
|
||||
)}
|
||||
<CategoryIcon
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
className={severityColorClasses[item.severity]}
|
||||
/>
|
||||
<CardTitle className="text-foreground-light">{categoryLabel}</CardTitle>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
|
||||
@@ -14,7 +14,7 @@ type AdvisorBaseItem = {
|
||||
title: string
|
||||
severity: AdvisorSeverity
|
||||
createdAt?: number
|
||||
tab: 'security' | 'performance' | 'messages'
|
||||
tab: 'security' | 'performance' | 'health' | 'messages'
|
||||
source: AdvisorItemSource
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,19 @@ describe('AdvisorPanel.utils', () => {
|
||||
expect(getAdvisorItemSecondaryText(bannedIpSignal)).toBe('Database · 203.0.113.10')
|
||||
})
|
||||
|
||||
it('files a health lint under the health tab', () => {
|
||||
const [item] = createAdvisorLintItems([
|
||||
createLint({
|
||||
cache_key: 'instance_db_down',
|
||||
name: 'instance_db_down',
|
||||
categories: ['HEALTH'],
|
||||
metadata: { type: 'health', entity: 'Database' },
|
||||
}),
|
||||
])
|
||||
|
||||
expect(item?.tab).toBe('health')
|
||||
})
|
||||
|
||||
describe('notification secondary text', () => {
|
||||
const [notificationWithProject] = createAdvisorNotificationItems([
|
||||
createNotification({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import dayjs from 'dayjs'
|
||||
import { Gauge, Inbox, Shield } from 'lucide-react'
|
||||
import { Activity, Gauge, Inbox, Shield } from 'lucide-react'
|
||||
import type { ElementType } from 'react'
|
||||
|
||||
import type { AdvisorItem, AdvisorLintItem, AdvisorNotificationItem } from './AdvisorPanel.types'
|
||||
@@ -50,7 +50,9 @@ export const createAdvisorLintItems = (lintData?: Lint[]): AdvisorLintItem[] =>
|
||||
? ('security' as const)
|
||||
: categories.includes('PERFORMANCE')
|
||||
? ('performance' as const)
|
||||
: undefined
|
||||
: categories.includes('HEALTH')
|
||||
? ('health' as const)
|
||||
: undefined
|
||||
|
||||
if (!tab) return null
|
||||
|
||||
@@ -153,6 +155,7 @@ export const getAdvisorItemSecondaryText = (
|
||||
export const tabIconMap: Record<Exclude<AdvisorTab, 'all'>, ElementType> = {
|
||||
security: Shield,
|
||||
performance: Gauge,
|
||||
health: Activity,
|
||||
messages: Inbox,
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { HttpResponse } from 'msw'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { getProjectHealthLints, HEALTH_LINT_NAMES } from './health-lints-query'
|
||||
import type { components } from '@/data/api'
|
||||
import { addAPIMock } from '@/tests/lib/msw'
|
||||
|
||||
type AdvisorsResponse = components['schemas']['V2ProjectAdvisorsResponse_Output']
|
||||
type AdvisorsRequestBody = components['schemas']['V2RunProjectAdvisorsBody']
|
||||
type AdvisorLint = AdvisorsResponse['data']['attributes']['lints'][number]
|
||||
|
||||
const createLint = (overrides: Partial<AdvisorLint> = {}): AdvisorLint =>
|
||||
({
|
||||
cache_key: 'instance_db_down',
|
||||
name: 'instance_db_down',
|
||||
title: 'Database process is down',
|
||||
level: 'ERROR',
|
||||
facing: 'EXTERNAL',
|
||||
categories: ['HEALTH'],
|
||||
description: 'The instance is running but Postgres is not accepting connections locally.',
|
||||
detail: 'The on-host check of Postgres is failing.',
|
||||
remediation: 'https://supabase.com/docs/guides/platform/troubleshooting',
|
||||
metadata: { type: 'health', entity: 'Database' },
|
||||
...overrides,
|
||||
}) as AdvisorLint
|
||||
|
||||
const mockAdvisorsRun = (lints: AdvisorLint[], onRequest?: (body: AdvisorsRequestBody) => void) => {
|
||||
addAPIMock({
|
||||
method: 'post',
|
||||
path: '/v2/projects/:ref/advisors/run',
|
||||
response: async ({ request }) => {
|
||||
onRequest?.((await request.json()) as AdvisorsRequestBody)
|
||||
return HttpResponse.json<AdvisorsResponse>({
|
||||
data: { type: 'project_advisors', attributes: { lints } },
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
describe('getProjectHealthLints', () => {
|
||||
it('asks the endpoint for the health lints Studio surfaces', async () => {
|
||||
let requestBody: AdvisorsRequestBody | undefined
|
||||
mockAdvisorsRun([], (body) => {
|
||||
requestBody = body
|
||||
})
|
||||
|
||||
await getProjectHealthLints({ projectRef: 'default' })
|
||||
|
||||
expect(requestBody?.data.attributes.lints.map((lint) => lint.name)).toEqual([
|
||||
...HEALTH_LINT_NAMES,
|
||||
])
|
||||
})
|
||||
|
||||
it('returns the health issues the project actually has', async () => {
|
||||
mockAdvisorsRun([createLint()])
|
||||
|
||||
const result = await getProjectHealthLints({ projectRef: 'default' })
|
||||
|
||||
expect(result.map((lint) => lint.name)).toEqual(['instance_db_down'])
|
||||
})
|
||||
|
||||
it('drops results that report on the check rather than on the project', async () => {
|
||||
mockAdvisorsRun([
|
||||
createLint(),
|
||||
createLint({
|
||||
cache_key: 'advisor_check_unavailable:metrics_unavailable',
|
||||
name: 'advisor_check_unavailable',
|
||||
level: 'INFO',
|
||||
}),
|
||||
createLint({ cache_key: 'project_not_active', name: 'project_not_active' }),
|
||||
])
|
||||
|
||||
const result = await getProjectHealthLints({ projectRef: 'default' })
|
||||
|
||||
expect(result.map((lint) => lint.name)).toEqual(['instance_db_down'])
|
||||
})
|
||||
|
||||
it('throws when no project ref is given', async () => {
|
||||
await expect(getProjectHealthLints({})).rejects.toThrow('Project ref is required')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,91 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import type { components } from 'api-types'
|
||||
|
||||
import { lintKeys } from './keys'
|
||||
import type { Lint } from './lint-query'
|
||||
import { handleError, post } from '@/data/fetchers'
|
||||
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
||||
import { IS_PLATFORM, 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 isActive = project?.status === PROJECT_STATUS.ACTIVE_HEALTHY
|
||||
|
||||
return useQuery<ProjectHealthLintsData, ProjectHealthLintsError, TData>({
|
||||
queryKey: lintKeys.healthLints(projectRef),
|
||||
queryFn: ({ signal }) => getProjectHealthLints({ projectRef }, signal),
|
||||
enabled: enabled && IS_PLATFORM && 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,
|
||||
})
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
export const lintKeys = {
|
||||
lint: (projectRef: string | undefined) => ['projects', projectRef, 'lint'] as const,
|
||||
healthLints: (projectRef: string | undefined) =>
|
||||
['projects', projectRef, 'lint', 'health'] as const,
|
||||
lintRules: (projectRef: string | undefined) => ['projects', projectRef, 'lint-rules'] as const,
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { proxy, snapshot, useSnapshot } from 'valtio'
|
||||
|
||||
export type AdvisorTab = 'all' | 'security' | 'performance' | 'messages'
|
||||
export type AdvisorTab = 'all' | 'security' | 'performance' | 'health' | 'messages'
|
||||
export type AdvisorSeverity = 'critical' | 'warning' | 'info'
|
||||
export type AdvisorItemSource = 'lint' | 'notification' | 'signal'
|
||||
|
||||
|
||||
Vendored
+4
-1
@@ -5469,7 +5469,10 @@ export interface components {
|
||||
| 'instance_telemetry_lost'
|
||||
| 'instance_db_down'
|
||||
| 'instance_alert_firing'
|
||||
| 'log_service_error_rate_high'
|
||||
| 'log_data_api_error_rate_high'
|
||||
| 'log_auth_error_rate_high'
|
||||
| 'log_storage_error_rate_high'
|
||||
| 'log_edge_function_error_rate_high'
|
||||
| 'project_not_active'
|
||||
| 'advisor_check_unavailable'
|
||||
/** Format: date-time */
|
||||
|
||||
Vendored
+8
-2
@@ -1865,7 +1865,10 @@ export interface components {
|
||||
| 'instance_telemetry_lost'
|
||||
| 'instance_db_down'
|
||||
| 'instance_alert_firing'
|
||||
| 'log_service_error_rate_high'
|
||||
| 'log_data_api_error_rate_high'
|
||||
| 'log_auth_error_rate_high'
|
||||
| 'log_storage_error_rate_high'
|
||||
| 'log_edge_function_error_rate_high'
|
||||
| 'project_not_active'
|
||||
| 'advisor_check_unavailable'
|
||||
/** Format: date-time */
|
||||
@@ -2082,7 +2085,10 @@ export interface components {
|
||||
| 'instance_telemetry_lost'
|
||||
| 'instance_db_down'
|
||||
| 'instance_alert_firing'
|
||||
| 'log_service_error_rate_high'
|
||||
| 'log_data_api_error_rate_high'
|
||||
| 'log_auth_error_rate_high'
|
||||
| 'log_storage_error_rate_high'
|
||||
| 'log_edge_function_error_rate_high'
|
||||
}[]
|
||||
}
|
||||
/**
|
||||
|
||||
+4
-1
@@ -7993,7 +7993,10 @@ export interface components {
|
||||
| 'instance_telemetry_lost'
|
||||
| 'instance_db_down'
|
||||
| 'instance_alert_firing'
|
||||
| 'log_service_error_rate_high'
|
||||
| 'log_data_api_error_rate_high'
|
||||
| 'log_auth_error_rate_high'
|
||||
| 'log_storage_error_rate_high'
|
||||
| 'log_edge_function_error_rate_high'
|
||||
| 'project_not_active'
|
||||
| 'advisor_check_unavailable'
|
||||
/** Format: date-time */
|
||||
|
||||
Reference in new issue
Block a user