DataApiEnableSwitch.utils.ts 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. import { getUnsafeEntitiesInApiSql } from '@supabase/pg-meta'
  2. import type { EnableCheckAction, EnableCheckState } from './DataApiEnableSwitch.types'
  3. import { executeSql } from '@/data/sql/execute-sql-query'
  4. export type ExposedEntity = {
  5. schema: string
  6. name: string
  7. type: 'table' | 'foreign table' | 'materialized view' | 'view'
  8. }
  9. /**
  10. * Queries for entities that would be exposed through the Data API with
  11. * potential security issues: tables without RLS, foreign tables, materialized
  12. * views, and views without SECURITY INVOKER.
  13. *
  14. * This checks against the _target_ schemas rather than the currently active
  15. * PostgREST config, so it works correctly when enabling the Data API.
  16. */
  17. export async function queryUnsafeEntitiesInApi({
  18. projectRef,
  19. connectionString,
  20. schemas,
  21. }: {
  22. projectRef: string
  23. connectionString?: string | null
  24. schemas: Array<string>
  25. }): Promise<Array<ExposedEntity>> {
  26. if (schemas.length === 0) return []
  27. const { result } = await executeSql<Array<ExposedEntity>>({
  28. projectRef,
  29. connectionString,
  30. sql: getUnsafeEntitiesInApiSql({ schemas }),
  31. queryKey: ['unsafe-entities-in-api'],
  32. })
  33. return result ?? []
  34. }
  35. export const getDefaultSchemas = (dbSchema: string | null | undefined) => {
  36. const schemas =
  37. dbSchema
  38. ?.split(',')
  39. .map((schema) => schema.trim())
  40. .filter((schema) => schema.length > 0) ?? []
  41. return schemas.length > 0 ? schemas : ['public']
  42. }
  43. export function enableCheckReducer(
  44. state: EnableCheckState,
  45. action: EnableCheckAction
  46. ): EnableCheckState {
  47. switch (state.status) {
  48. case 'idle':
  49. if (action.type === 'START_CHECK') return { status: 'checking' }
  50. return state
  51. case 'checking':
  52. if (action.type === 'ENTITIES_FOUND')
  53. return { status: 'confirming', unsafeEntities: action.unsafeEntities }
  54. if (action.type === 'DISMISS') return { status: 'idle' }
  55. return state
  56. case 'confirming':
  57. if (action.type === 'DISMISS') return { status: 'idle' }
  58. return state
  59. }
  60. }