mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 01:45:10 +03:00
## What kind of change does this PR introduce? Mechanical cleanup on top of the Button default-variant change (#50160). ## What is the current behavior? Many callsites still pass `variant="default"` even though that is now the component default. ## What is the new behavior? Removes redundant static `variant="default"` from legacy `Button` and `ButtonTooltip` callsites. Keeps explicit defaults where they document the API: - `button-default.tsx` and `button-sizes.tsx` demos - `DocsButton`, which pins neutral styling at the wrapper boundary ## To test Studio: - [Auth → Rate Limits](https://studio-staging-2s957kwc4-supabase.vercel.app/dashboard/project/_/auth/rate-limits): dirty the form so Cancel appears; Cancel stays neutral, Save stays green - [Project Settings → API Keys](https://studio-staging-2s957kwc4-supabase.vercel.app/dashboard/project/_/settings/api-keys): `DocsButton` in the header actions stays neutral Design system: - [Design system → Button](https://design-system-git-dnywh-dc924ac1-supabase.vercel.app/design-system/docs/components/button): `button-default` / `button-sizes` still show explicit default styling; Primary (green) is restricted to the Primary section (and `asChild`) WWW: - [www → Brand assets](https://zone-www-dot-com-git-dnywh-dc924ac1-supabase.vercel.app/brand-assets): Download logo kit / Download button kit stay neutral
601 lines
24 KiB
TypeScript
601 lines
24 KiB
TypeScript
import {
|
|
Activity,
|
|
AlertTriangle,
|
|
Box,
|
|
Clock,
|
|
Database,
|
|
Eye,
|
|
Gauge,
|
|
Lock,
|
|
LockIcon,
|
|
Ruler,
|
|
Scaling,
|
|
Server,
|
|
Table2,
|
|
TextSearch,
|
|
Unlock,
|
|
User,
|
|
} from 'lucide-react'
|
|
import Link from 'next/link'
|
|
import { Badge, Button } from 'ui'
|
|
|
|
import { asGraphqlExposureLint, GraphqlExposureLintCTA } from './GraphqlExposureLintCTA'
|
|
import { LINTER_LEVELS, LintInfo } from '@/components/interfaces/Linter/Linter.constants'
|
|
import { getServiceVersionsPath } from '@/components/interfaces/Settings/General/ServiceVersions/ServiceVersions.utils'
|
|
import { Lint, LINT_TYPES } from '@/data/lint/lint-query'
|
|
import { DOCS_URL } from '@/lib/constants'
|
|
|
|
export const lintInfoMap: LintInfo[] = [
|
|
{
|
|
name: 'unindexed_foreign_keys',
|
|
title: 'Unindexed foreign keys',
|
|
icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef, metadata }) =>
|
|
`/project/${projectRef}/database/indexes?schema=${encodeURIComponent(metadata?.schema ?? '')}`,
|
|
linkText: 'Create an index',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?queryGroups=lint&lint=0001_unindexed_foreign_keys`,
|
|
category: 'performance',
|
|
},
|
|
{
|
|
name: 'auth_users_exposed',
|
|
title: 'Exposed Auth Users',
|
|
icon: <Lock className="text-foreground-muted" size={15} strokeWidth={1.5} />,
|
|
link: ({ projectRef }) => `/project/${projectRef}/editor`,
|
|
linkText: 'View table',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?queryGroups=lint&lint=0002_auth_users_exposed`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'auth_rls_initplan',
|
|
title: 'Auth RLS Initialization Plan',
|
|
icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef }) => `/project/${projectRef}/database/policies`,
|
|
linkText: 'View policies',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?queryGroups=lint&lint=0003_auth_rls_initplan`,
|
|
category: 'performance',
|
|
},
|
|
{
|
|
name: 'no_primary_key',
|
|
title: 'No Primary Key',
|
|
icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef }) => `/project/${projectRef}/editor`,
|
|
linkText: 'View table',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?queryGroups=lint&lint=0004_no_primary_key`,
|
|
category: 'performance',
|
|
},
|
|
{
|
|
name: 'unused_index',
|
|
title: 'Unused Index',
|
|
icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef, metadata }) =>
|
|
`/project/${projectRef}/database/indexes?schema=${encodeURIComponent(metadata?.schema ?? '')}&table=${encodeURIComponent(metadata?.name ?? '')}`,
|
|
linkText: 'View index',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?queryGroups=lint&lint=0005_unused_index`,
|
|
category: 'performance',
|
|
},
|
|
{
|
|
name: 'multiple_permissive_policies',
|
|
title: 'Multiple Permissive Policies',
|
|
icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef, metadata }) =>
|
|
`/project/${projectRef}/database/policies?schema=${encodeURIComponent(metadata?.schema ?? '')}&search=${encodeURIComponent(metadata?.name ?? '')}`,
|
|
linkText: 'View policies',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?queryGroups=lint&lint=0006_multiple_permissive_policies`,
|
|
category: 'performance',
|
|
},
|
|
{
|
|
name: 'policy_exists_rls_disabled',
|
|
title: 'Policy Exists RLS Disabled',
|
|
icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef, metadata }) =>
|
|
`/project/${projectRef}/database/policies?schema=${encodeURIComponent(metadata?.schema ?? '')}&search=${encodeURIComponent(metadata?.name ?? '')}`,
|
|
linkText: 'View policies',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?queryGroups=lint&lint=0007_policy_exists_rls_disabled`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'rls_enabled_no_policy',
|
|
title: 'RLS Enabled No Policy',
|
|
icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef, metadata }) =>
|
|
`/project/${projectRef}/database/policies?schema=${encodeURIComponent(metadata?.schema ?? '')}&search=${encodeURIComponent(metadata?.name ?? '')}`,
|
|
linkText: 'View table',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?queryGroups=lint&lint=0008_rls_enabled_no_policy`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'duplicate_index',
|
|
title: 'Duplicate Index',
|
|
icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef, metadata }) =>
|
|
`/project/${projectRef}/database/indexes?schema=${encodeURIComponent(metadata?.schema ?? '')}&table=${encodeURIComponent(metadata?.name ?? '')}`,
|
|
linkText: 'View index',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?queryGroups=lint&lint=0009_duplicate_index`,
|
|
category: 'performance',
|
|
},
|
|
{
|
|
name: 'security_definer_view',
|
|
title: 'Security Definer View',
|
|
icon: <Eye className="text-foreground-muted" size={15} strokeWidth={1.5} />,
|
|
link: () =>
|
|
`${DOCS_URL}/guides/observability/advisors?queryGroups=lint&lint=0010_security_definer_view`,
|
|
linkText: 'View docs',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?queryGroups=lint&lint=0010_security_definer_view`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'function_search_path_mutable',
|
|
title: 'Function Search Path Mutable',
|
|
icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef, metadata }) =>
|
|
`/project/${projectRef}/database/functions?schema=${encodeURIComponent(metadata?.schema ?? '')}&search=${encodeURIComponent(metadata?.name ?? '')}`,
|
|
linkText: 'View functions',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?queryGroups=lint&lint=0011_function_search_path_mutable`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'auth_allow_anonymous_sign_ins',
|
|
title: 'Anonymous Sign-Ins Allowed',
|
|
icon: <User className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef }) => `/project/${projectRef}/auth/providers`,
|
|
linkText: 'View settings',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?queryGroups=lint&lint=0012_auth_allow_anonymous_sign_ins`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'rls_disabled_in_public',
|
|
title: 'RLS Disabled in Public',
|
|
icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef, metadata }) =>
|
|
`/project/${projectRef}/database/policies?schema=${encodeURIComponent(metadata?.schema ?? '')}&search=${encodeURIComponent(metadata?.name ?? '')}`,
|
|
linkText: 'View policies',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?queryGroups=lint&lint=0013_rls_disabled_in_public`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'extension_in_public',
|
|
title: 'Extension in Public',
|
|
icon: <Unlock className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef, metadata }) =>
|
|
`/project/${projectRef}/database/extensions?filter=${encodeURIComponent(metadata?.name ?? '')}`,
|
|
linkText: 'View extension',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?queryGroups=lint&lint=0014_extension_in_public`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'auth_otp_long_expiry',
|
|
title: 'Auth OTP Long Expiry',
|
|
icon: <Clock className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef }) => `/project/${projectRef}/auth/providers`,
|
|
linkText: 'View settings',
|
|
docsLink: `${DOCS_URL}/guides/platform/going-into-prod#security`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'auth_otp_short_length',
|
|
title: 'Auth OTP Short Length',
|
|
icon: <Ruler className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef }) => `/project/${projectRef}/auth/providers`,
|
|
linkText: 'View settings',
|
|
docsLink: `${DOCS_URL}/guides/platform/going-into-prod#security`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'auth_db_connections_absolute',
|
|
title: 'Auth Absolute Connection Management Strategy',
|
|
icon: <Scaling className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef }) => `/project/${projectRef}/auth/performance`,
|
|
linkText: 'View settings',
|
|
docsLink: `${DOCS_URL}/guides/platform/going-into-prod`,
|
|
category: 'performance',
|
|
},
|
|
{
|
|
name: 'rls_references_user_metadata',
|
|
title: 'RLS references user metadata',
|
|
icon: <User className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef }) => `/project/${projectRef}/database/policies`,
|
|
linkText: 'View policies',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?queryGroups=lint&lint=0015_rls_references_user_metadata`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'materialized_view_in_api',
|
|
title: 'Materialized View in API',
|
|
icon: <Eye className="text-foreground-muted" size={15} strokeWidth={1.5} />,
|
|
link: () => `${DOCS_URL}/guides/observability/advisors?lint=0016_materialized_view_in_api`,
|
|
linkText: 'View docs',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?lint=0016_materialized_view_in_api`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'foreign_table_in_api',
|
|
title: 'Foreign Table in API',
|
|
icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1.5} />,
|
|
link: () => `${DOCS_URL}/guides/observability/advisors?lint=0017_foreign_table_in_api`,
|
|
linkText: 'View docs',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?lint=0017_foreign_table_in_api`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'unsupported_reg_types',
|
|
title: 'Unsupported reg types',
|
|
icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1.5} />,
|
|
link: () =>
|
|
`${DOCS_URL}/guides/observability/advisors?lint=0018_unsupported_reg_types&queryGroups=lint`,
|
|
linkText: 'View docs',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?lint=0018_unsupported_reg_types&queryGroups=lint`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'ssl_not_enforced',
|
|
title: 'SSL not enforced',
|
|
icon: <Ruler className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef }) => `/project/${projectRef}/database/settings`,
|
|
linkText: 'View settings',
|
|
docsLink: `${DOCS_URL}/guides/platform/ssl-enforcement`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'network_restrictions_not_set',
|
|
title: 'No network restrictions',
|
|
icon: <Ruler className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef }) => `/project/${projectRef}/database/settings`,
|
|
linkText: 'View settings',
|
|
docsLink: `${DOCS_URL}/guides/platform/network-restrictions`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'password_requirements_min_length',
|
|
title: 'Minimum password length not set or inadequate',
|
|
icon: <Ruler className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef }) => `/project/${projectRef}/auth/providers?provider=Email`,
|
|
linkText: 'View settings',
|
|
docsLink: `${DOCS_URL}/guides/platform/going-into-prod#security`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'pitr_not_enabled',
|
|
title: 'PITR not enabled',
|
|
icon: <Ruler className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef }) => `/project/${projectRef}/database/backups/pitr`,
|
|
linkText: 'View settings',
|
|
docsLink: `${DOCS_URL}/guides/platform/backups#point-in-time-recovery`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'pitr_archiving_stale',
|
|
title: 'PITR archiving may be broken',
|
|
icon: <Ruler className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef }) => `/project/${projectRef}/database/backups/pitr`,
|
|
linkText: 'View settings',
|
|
// anchor explains what PITR is, not how to fix a stale archive; PITR-archiving-specific docs tracked in TODO: INDATA-1149
|
|
docsLink: `${DOCS_URL}/guides/platform/backups#point-in-time-recovery`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'auth_leaked_password_protection',
|
|
title: 'Leaked Password Protection Disabled',
|
|
icon: <LockIcon className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef }) => `/project/${projectRef}/auth/providers?provider=Email`,
|
|
linkText: 'View settings',
|
|
docsLink: `${DOCS_URL}/guides/auth/password-security#password-strength-and-leaked-password-protection`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'auth_insufficient_mfa_options',
|
|
title: 'Insufficient MFA Options',
|
|
icon: <LockIcon className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef }) => `/project/${projectRef}/auth/mfa`,
|
|
linkText: 'View settings',
|
|
docsLink: `${DOCS_URL}/guides/auth/auth-mfa`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'auth_password_policy_missing',
|
|
title: 'Password Policy Missing',
|
|
icon: <LockIcon className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef }) => `/project/${projectRef}/auth/providers?provider=Email`,
|
|
linkText: 'View settings',
|
|
docsLink: `${DOCS_URL}/guides/auth/password-security`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'leaked_service_key',
|
|
title: 'Leaked Service Key Detected',
|
|
icon: <LockIcon className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef }) => `/project/${projectRef}/settings/api-keys`,
|
|
linkText: 'View settings',
|
|
docsLink: `${DOCS_URL}/guides/api/api-keys#the-servicerole-key`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'no_backup_admin',
|
|
title: 'No Backup Admin Detected',
|
|
icon: <LockIcon className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef }) => `/project/${projectRef}/auth/mfa`,
|
|
linkText: 'View settings',
|
|
docsLink: `${DOCS_URL}/guides/auth/auth-mfa`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'vulnerable_postgres_version',
|
|
title: 'Postgres version has security patches available',
|
|
icon: <LockIcon className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef }) => getServiceVersionsPath(projectRef),
|
|
linkText: 'View settings',
|
|
docsLink: `${DOCS_URL}/guides/platform/upgrading`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'sensitive_columns_exposed',
|
|
title: 'Sensitive Columns Exposed',
|
|
icon: <Eye className="text-foreground-muted" size={15} strokeWidth={1.5} />,
|
|
link: ({ projectRef, metadata }) =>
|
|
`/project/${projectRef}/editor?schema=${encodeURIComponent(metadata?.schema ?? '')}&table=${encodeURIComponent(metadata?.name ?? '')}`,
|
|
linkText: 'View table',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?lint=0023_sensitive_columns_exposed`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'rls_policy_always_true',
|
|
title: 'RLS Policy Always True',
|
|
icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef, metadata }) =>
|
|
`/project/${projectRef}/database/policies?schema=${encodeURIComponent(metadata?.schema ?? '')}&search=${encodeURIComponent(metadata?.name ?? '')}`,
|
|
linkText: 'View policies',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?lint=0024_permissive_rls_policy`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'public_bucket_allows_listing',
|
|
title: 'Public Bucket Allows Listing',
|
|
icon: <Box className="text-foreground-muted" size={15} strokeWidth={1.5} />,
|
|
link: ({ projectRef, metadata }) => {
|
|
const bucketId = (metadata as Record<string, string | undefined> | undefined)?.bucket_id
|
|
return `/project/${projectRef}/storage/files/buckets/${encodeURIComponent(bucketId ?? metadata?.name ?? '')}`
|
|
},
|
|
linkText: 'View bucket',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?lint=0025_public_bucket_allows_listing`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'pg_graphql_anon_table_exposed',
|
|
title: 'Public Can See Object in GraphQL Schema',
|
|
icon: <Eye className="text-foreground-muted" size={15} strokeWidth={1.5} />,
|
|
link: ({ projectRef, metadata }) =>
|
|
`/project/${projectRef}/editor?schema=${encodeURIComponent(metadata?.schema ?? '')}&table=${encodeURIComponent(metadata?.name ?? '')}`,
|
|
linkText: 'View object',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?lint=0026_pg_graphql_anon_table_exposed`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'pg_graphql_authenticated_table_exposed',
|
|
title: 'Signed-In Users Can See Object in GraphQL Schema',
|
|
icon: <Eye className="text-foreground-muted" size={15} strokeWidth={1.5} />,
|
|
link: ({ projectRef, metadata }) =>
|
|
`/project/${projectRef}/editor?schema=${encodeURIComponent(metadata?.schema ?? '')}&table=${encodeURIComponent(metadata?.name ?? '')}`,
|
|
linkText: 'View object',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?lint=0027_pg_graphql_authenticated_table_exposed`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'anon_security_definer_function_executable',
|
|
title: 'Public Can Execute SECURITY DEFINER Function',
|
|
icon: <LockIcon className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef, metadata }) =>
|
|
`/project/${projectRef}/database/functions?schema=${encodeURIComponent(metadata?.schema ?? '')}&search=${encodeURIComponent(metadata?.name ?? '')}`,
|
|
linkText: 'View function',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?lint=0028_anon_security_definer_function_executable`,
|
|
category: 'security',
|
|
},
|
|
{
|
|
name: 'authenticated_security_definer_function_executable',
|
|
title: 'Signed-In Users Can Execute SECURITY DEFINER Function',
|
|
icon: <LockIcon className="text-foreground-muted" size={15} strokeWidth={1} />,
|
|
link: ({ projectRef, metadata }) =>
|
|
`/project/${projectRef}/database/functions?schema=${encodeURIComponent(metadata?.schema ?? '')}&search=${encodeURIComponent(metadata?.name ?? '')}`,
|
|
linkText: 'View function',
|
|
docsLink: `${DOCS_URL}/guides/observability/advisors?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 = ({
|
|
title,
|
|
projectRef,
|
|
metadata,
|
|
onAfterAction,
|
|
}: {
|
|
title: LINT_TYPES
|
|
projectRef: string
|
|
metadata: Lint['metadata']
|
|
onAfterAction?: () => void
|
|
}) => {
|
|
const lintInfo = lintInfoMap.find((item) => item.name === title)
|
|
|
|
if (!lintInfo) {
|
|
return null
|
|
}
|
|
|
|
const graphqlExposureLintName = asGraphqlExposureLint(title)
|
|
if (graphqlExposureLintName) {
|
|
return (
|
|
<GraphqlExposureLintCTA
|
|
lintName={graphqlExposureLintName}
|
|
projectRef={projectRef}
|
|
metadata={metadata}
|
|
onAfterAction={onAfterAction}
|
|
/>
|
|
)
|
|
}
|
|
|
|
const link = lintInfo.link({ projectRef, metadata })
|
|
const linkText = lintInfo.linkText
|
|
|
|
return (
|
|
<Button asChild>
|
|
<Link href={link} rel="noreferrer" className="no-underline">
|
|
{linkText}
|
|
</Link>
|
|
</Button>
|
|
)
|
|
}
|
|
|
|
export const EntityTypeIcon = ({ type }: { type: string | undefined }) => {
|
|
switch (type) {
|
|
case 'table':
|
|
return <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />
|
|
case 'view':
|
|
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} />
|
|
}
|
|
}
|
|
|
|
export const LintEntity = ({ metadata }: { metadata: Lint['metadata'] }) => {
|
|
return getLintEntityString(metadata)
|
|
}
|
|
|
|
export const LintCategoryBadge = ({ category }: { category: string }) => {
|
|
return (
|
|
<Badge variant={category === 'SECURITY' ? 'destructive' : 'warning'}>
|
|
{category.toLowerCase()}
|
|
</Badge>
|
|
)
|
|
}
|
|
|
|
export const NoIssuesFound = ({ level }: { level: string }) => {
|
|
const noun = level === LINTER_LEVELS.ERROR ? 'errors' : 'warnings'
|
|
return (
|
|
<div className="absolute top-28 px-6 flex flex-col items-center justify-center w-full gap-y-2">
|
|
<TextSearch className="text-foreground-muted" strokeWidth={1} />
|
|
<div className="text-center">
|
|
<p className="text-foreground">No {noun} detected</p>
|
|
<p className="text-foreground-light">
|
|
Congrats! There are no {noun} detected for this database
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export const createLintSummaryPrompt = (lint: Lint) => {
|
|
const title = lintInfoMap.find((item) => item.name === lint.name)?.title ?? lint.title
|
|
const entity = getLintEntityString(lint.metadata) || 'N/A'
|
|
const schema = lint.metadata?.schema ?? 'N/A'
|
|
const issue = lint.detail ? lint.detail.replace(/\\`/g, '`') : 'N/A'
|
|
const description = lint.description ? lint.description.replace(/\\`/g, '`') : 'N/A'
|
|
return `Summarize the issue and suggest fixes for the following lint item:
|
|
Title: ${title}
|
|
Entity: ${entity}
|
|
Schema: ${schema}
|
|
Issue Details: ${issue}
|
|
Description: ${description}`
|
|
}
|
|
|
|
const LINTER_LEVEL_VALUES = Object.values(LINTER_LEVELS)
|
|
|
|
/**
|
|
* The `preset` query param is user controlled, so match it against the known levels
|
|
* instead of trusting it to be one.
|
|
*/
|
|
export const parseLinterLevel = (value?: string) =>
|
|
LINTER_LEVEL_VALUES.find((level) => level === value)
|
|
|
|
export const getLintEntityString = (metadata: Lint['metadata']) => {
|
|
if (!metadata) {
|
|
return undefined
|
|
}
|
|
|
|
if (metadata.entity) {
|
|
return metadata.entity
|
|
}
|
|
|
|
if (metadata.schema && metadata.name) {
|
|
const extendedMetadata = metadata as typeof metadata & { arguments?: string }
|
|
const args =
|
|
typeof extendedMetadata.arguments === 'string' ? extendedMetadata.arguments : undefined
|
|
return `${metadata.schema}.${metadata.name}${args !== undefined ? `(${args})` : ''}`
|
|
}
|
|
|
|
return undefined
|
|
}
|