Files
supabase/apps/studio/components/interfaces/Auth/Overview/OverviewUsage.constants.ts
kemal.earth 7927865669 feat(studio): auth overview usage section (#39207)
* feat: add button to all reports and basic layout

* feat: add last 24 hours indicator

* feat: add active users tile

* feat: working first row of usage stats

* feat: add basic report charts for sign in and sign up

* fix: change prev number to difference from before

* feat: pass time range to view all reports link

* fix: get static numbers to show

* fix: type error

* fix: exclude admin users from active users

* fix: revert change as number stopped working

* feat: testing latest sign ups table

* chore: remove table redoing

* feat: testing with renderers

* feat: revert back to reportchartv2

* feat: make sure sign up and sign in charts are bar by default

* fix: class name for default difference num

* chore: bit of clean up

* chore: tidy up constants file

* chore: remove unused auth renderers

* feat: single query instead of multiple tiny ones

* fix: query fetching

* feat: simplify how the query does averages

* nit: small css bits
2025-10-08 08:52:25 +01:00

106 lines
3.4 KiB
TypeScript

import dayjs from 'dayjs'
import { fetchLogs } from 'data/reports/report.utils'
// Date range helpers
export const getDateRanges = () => {
const endDate = dayjs().toISOString()
const startDate = dayjs().subtract(24, 'hour').toISOString()
const previousEndDate = dayjs().subtract(24, 'hour').toISOString()
const previousStartDate = dayjs().subtract(48, 'hour').toISOString()
return {
current: { startDate, endDate },
previous: { startDate: previousStartDate, endDate: previousEndDate },
}
}
export const AUTH_COMBINED_QUERY = () => `
with base as (
select
json_value(event_message, "$.auth_event.action") as action,
json_value(event_message, "$.auth_event.actor_id") as actor_id,
cast(json_value(event_message, "$.duration") as int64) as duration_ns
from auth_logs
)
select
'activeUsers' as metric,
cast(count(distinct case
when action in (
'login','user_signedup','token_refreshed','user_modified',
'user_recovery_requested','user_reauthenticate_requested'
) then actor_id
else null
end) as float64) as value
from base
union all
select 'passwordResetRequests' as metric,
cast(count(case when action = 'user_recovery_requested' then 1 else null end) as float64)
from base
union all
select 'signUpCount' as metric,
cast(count(case when action = 'user_signedup' then 1 else null end) as float64)
from base
union all
select 'signInLatency' as metric,
coalesce(round(avg(case when action = 'login' then duration_ns else null end) / 1000000, 2), 0)
from base
union all
select 'signUpLatency' as metric,
coalesce(round(avg(case when action = 'user_signedup' then duration_ns else null end) / 1000000, 2), 0)
from base
`
export const fetchAllAuthMetrics = async (projectRef: string, period: 'current' | 'previous') => {
const sql = AUTH_COMBINED_QUERY()
const { current, previous } = getDateRanges()
const dateRange = period === 'current' ? current : previous
return await fetchLogs(projectRef, sql, dateRange.startDate, dateRange.endDate)
}
export const processAllAuthMetrics = (currentData: any[], previousData: any[]) => {
const processData = (data: any[]) => {
if (!data || !Array.isArray(data)) {
return { activeUsers: 0, passwordResets: 0, signInLatency: 0, signUpLatency: 0 }
}
const result = data.reduce(
(acc, row) => {
const { metric, value } = row
if (metric === 'activeUsers') acc.activeUsers = value || 0
if (metric === 'passwordResetRequests') acc.passwordResets = value || 0
if (metric === 'signInLatency') acc.signInLatency = value || 0
if (metric === 'signUpLatency') acc.signUpLatency = value || 0
return acc
},
{ activeUsers: 0, passwordResets: 0, signInLatency: 0, signUpLatency: 0 }
)
return result
}
return {
current: processData(currentData),
previous: processData(previousData),
}
}
// Utility functions
export const calculatePercentageChange = (current: number, previous: number): number => {
if (previous === 0) return current > 0 ? 100 : 0
return ((current - previous) / previous) * 100
}
export const getChangeColor = (percentageChange: number): string => {
return percentageChange >= 0 ? 'text-brand' : 'text-destructive'
}
export const getChangeSign = (percentageChange: number): string => {
return percentageChange >= 0 ? '+' : ''
}