mirror of
https://github.com/supabase/supabase.git
synced 2026-06-01 18:34:37 +08:00
## Summary
PR 10 of the analytics SQL safety series. Migrates the last surface of
analytics queries that flowed through plain
`get(.../analytics/endpoints/logs.all, { query: { sql } })` or the
`fetchLogs(projectRef, sql: string, ...)` helper over to
`executeAnalyticsSql` with branded `SafeLogSqlFragment` inputs.
After this PR, every analytics SQL call site builds its query through
the safe-analytics-sql helpers and hits the wire through the single
`executeAnalyticsSql` boundary. User-controlled values (filter
operators, numeric thresholds, function IDs, regions, provider names)
all flow through `analyticsLiteral` / branded operator maps; static
fragments are wrapped in `safeSql`. PR 11 (ESLint / vitest rule
forbidding direct analytics-endpoint POST/GET outside
`executeAnalyticsSql`) is the next and final step.
## Changes
- **`hooks/analytics/useProjectUsageStats.tsx`** — route the
already-branded `genChartQuery` output through `executeAnalyticsSql`
(parallels `useLogsPreview`).
- **`data/reports/report.utils.ts`** — tighten `fetchLogs(sql)` from
`string` to `SafeLogSqlFragment`; the wire boundary is now the same
single `executeAnalyticsSql` wrapper used by the rest of the analytics
path. Adds two pre-branded fragment maps reused by the report configs:
- `SAFE_GRANULARITY_SQL` — closed set returned by
`analyticsIntervalToGranularity`.
- `SAFE_COMPARISON_OPERATOR_SQL` — closed set on
`NumericFilter.operator`.
- **`components/interfaces/Auth/Overview/OverviewErrors.constants.ts`**
— wrap the two static `AUTH_TOP_*_SQL` fragments in `safeSql` (no
interpolation, but the type now flows).
- **`data/reports/v2/edge-functions.config.ts`** — `filterToWhereClause`
and every entry in `METRIC_SQL` now return `SafeLogSqlFragment`.
User-controlled values (`status_code.value`, `execution_time.value`,
function IDs, regions) pass through `analyticsLiteral`; operators look
up the branded map; the granularity uses the branded map. The
wire-format strings are unchanged, so the existing
`edge-functions.test.tsx` exact-string expectations still hold.
- **`data/reports/v2/auth.config.ts`** — same shape applied to all ten
`AUTH_REPORT_SQL` entries. The legacy `whereClause.replace(/^WHERE\s+/,
'')` pattern is replaced by two helpers that emit `AND`-prefixed
predicate fragments directly (`authFiltersToAndPredicates`,
`edgeLogsFiltersToAndPredicates`). Static provider SELECT / GROUP BY
fragments are pre-branded.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Enhanced security for analytics and reporting queries by updating
query construction methods across auth, edge functions, and project
usage reports.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46476?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
114 lines
3.0 KiB
TypeScript
114 lines
3.0 KiB
TypeScript
import { useQuery } from '@tanstack/react-query'
|
|
import { useMemo } from 'react'
|
|
|
|
import { useFillTimeseriesSorted } from './useFillTimeseriesSorted'
|
|
import useTimeseriesUnixToIso from './useTimeseriesUnixToIso'
|
|
import { LogsTableName } from '@/components/interfaces/Settings/Logs/Logs.constants'
|
|
import type {
|
|
EventChart,
|
|
EventChartData,
|
|
Filters,
|
|
LogsEndpointParams,
|
|
} from '@/components/interfaces/Settings/Logs/Logs.types'
|
|
import { genChartQuery } from '@/components/interfaces/Settings/Logs/Logs.utils'
|
|
import { executeAnalyticsSql } from '@/data/logs/execute-analytics-sql'
|
|
|
|
interface ProjectUsageStatsHookResult {
|
|
error: string | Object | null
|
|
isLoading: boolean
|
|
filters: Filters
|
|
params: LogsEndpointParams
|
|
eventChartData: EventChartData[]
|
|
refresh: () => void
|
|
}
|
|
|
|
function useProjectUsageStats({
|
|
projectRef,
|
|
table,
|
|
timestampStart,
|
|
timestampEnd,
|
|
filterOverride,
|
|
}: {
|
|
projectRef: string
|
|
table: LogsTableName
|
|
timestampStart: string
|
|
timestampEnd: string
|
|
filterOverride?: Filters
|
|
}): ProjectUsageStatsHookResult {
|
|
const filterOverrideString = JSON.stringify(filterOverride)
|
|
const mergedFilters = useMemo(
|
|
() => ({
|
|
...filterOverride,
|
|
}),
|
|
[filterOverrideString]
|
|
)
|
|
|
|
const params: LogsEndpointParams = useMemo(() => {
|
|
return { iso_timestamp_start: timestampStart, iso_timestamp_end: timestampEnd }
|
|
}, [timestampStart, timestampEnd])
|
|
|
|
const chartQuery = useMemo(
|
|
() => genChartQuery(table, params, mergedFilters),
|
|
[table, params, mergedFilters]
|
|
)
|
|
|
|
const chartQueryKey = useMemo(
|
|
() => [
|
|
'projects',
|
|
projectRef,
|
|
'logs-chart',
|
|
table,
|
|
{
|
|
projectRef,
|
|
sql: chartQuery,
|
|
iso_timestamp_start: timestampStart,
|
|
iso_timestamp_end: timestampEnd,
|
|
},
|
|
],
|
|
[projectRef, chartQuery, timestampStart, timestampEnd, table]
|
|
)
|
|
|
|
const { data: eventChartResponse, refetch: refreshEventChart } = useQuery({
|
|
queryKey: chartQueryKey,
|
|
queryFn: async ({ signal }) => {
|
|
const data = await executeAnalyticsSql({
|
|
projectRef,
|
|
endpoint: '/platform/projects/{ref}/analytics/endpoints/logs.all',
|
|
sql: chartQuery,
|
|
iso_timestamp_start: timestampStart,
|
|
iso_timestamp_end: timestampEnd,
|
|
method: 'get',
|
|
signal,
|
|
})
|
|
|
|
return data as unknown as EventChart
|
|
},
|
|
refetchOnWindowFocus: false,
|
|
enabled: typeof projectRef !== 'undefined',
|
|
})
|
|
|
|
const normalizedEventChartData = useTimeseriesUnixToIso(
|
|
eventChartResponse?.result ?? [],
|
|
'timestamp'
|
|
)
|
|
|
|
const { data: eventChartData, error: eventChartError } = useFillTimeseriesSorted({
|
|
data: normalizedEventChartData,
|
|
timestampKey: 'timestamp',
|
|
valueKey: 'count',
|
|
defaultValue: 0,
|
|
startDate: timestampStart,
|
|
endDate: timestampEnd ?? new Date().toISOString(),
|
|
})
|
|
|
|
return {
|
|
isLoading: !eventChartResponse,
|
|
error: eventChartError,
|
|
filters: mergedFilters,
|
|
params,
|
|
eventChartData,
|
|
refresh: refreshEventChart,
|
|
}
|
|
}
|
|
export default useProjectUsageStats
|