OverviewUsage.constants.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import {
  2. RawAuthMetricsResponseSchema,
  3. type RawAuthMetricsResponse,
  4. type RawAuthMetricsRow,
  5. } from './OverviewUsage.schema'
  6. import { get } from '@/data/fetchers'
  7. export type AuthMetricsResponse = RawAuthMetricsResponse
  8. export type MetricName =
  9. | 'activeUsers'
  10. | 'signUpCount'
  11. | 'apiTotalRequests'
  12. | 'apiErrorRequests'
  13. | 'authTotalRequests'
  14. | 'authTotalErrors'
  15. | 'passwordResetRequests'
  16. type NumericMetricKey = keyof Omit<RawAuthMetricsRow, 'period'>
  17. const metricKeyMap: Record<MetricName, NumericMetricKey> = {
  18. activeUsers: 'active_users',
  19. signUpCount: 'sign_up_count',
  20. apiTotalRequests: 'api_total_requests',
  21. apiErrorRequests: 'api_error_requests',
  22. authTotalRequests: 'auth_total_requests',
  23. authTotalErrors: 'auth_total_errors',
  24. passwordResetRequests: 'password_reset_requests',
  25. }
  26. export const fetchAllAuthMetrics = async (projectRef: string): Promise<RawAuthMetricsResponse> => {
  27. const { data, error } = await get(
  28. `/platform/projects/{ref}/analytics/endpoints/auth.metrics` as any,
  29. {
  30. params: {
  31. path: { ref: projectRef },
  32. query: {
  33. interval: '1day',
  34. },
  35. },
  36. }
  37. )
  38. if (error) throw error
  39. const parsed = RawAuthMetricsResponseSchema.safeParse(data)
  40. if (!parsed.success) {
  41. const first = parsed.error.issues[0]
  42. throw new Error(`Invalid auth metrics response: ${first?.message ?? 'Invalid shape'}`)
  43. }
  44. return parsed.data
  45. }
  46. export const getMetricValues = (
  47. metrics: AuthMetricsResponse | undefined,
  48. metricName: MetricName
  49. ) => {
  50. const key = metricKeyMap[metricName]
  51. const currentRow = metrics?.result.find((r) => r.period === 'current')
  52. const previousRow = metrics?.result.find((r) => r.period === 'previous')
  53. return {
  54. current: currentRow ? currentRow[key] : 0,
  55. previous: previousRow ? previousRow[key] : 0,
  56. }
  57. }
  58. export const getApiSuccessRates = (metrics: AuthMetricsResponse | undefined) => {
  59. const { current: apiTotalCurrent, previous: apiTotalPrevious } = getMetricValues(
  60. metrics,
  61. 'apiTotalRequests'
  62. )
  63. const { current: apiErrorCurrent, previous: apiErrorPrevious } = getMetricValues(
  64. metrics,
  65. 'apiErrorRequests'
  66. )
  67. const current =
  68. apiTotalCurrent > 0 ? Math.max(0, 100 - (apiErrorCurrent / apiTotalCurrent) * 100) : 0
  69. const previous =
  70. apiTotalPrevious > 0 ? Math.max(0, 100 - (apiErrorPrevious / apiTotalPrevious) * 100) : 0
  71. return { current, previous }
  72. }
  73. export const getAuthSuccessRates = (metrics: AuthMetricsResponse | undefined) => {
  74. const { current: authTotalRequestsCurrent, previous: authTotalRequestsPrevious } =
  75. getMetricValues(metrics, 'authTotalRequests')
  76. const { current: authTotalErrorsCurrent, previous: authTotalErrorsPrevious } = getMetricValues(
  77. metrics,
  78. 'authTotalErrors'
  79. )
  80. const current =
  81. authTotalRequestsCurrent > 0
  82. ? Math.max(0, 100 - (authTotalErrorsCurrent / authTotalRequestsCurrent) * 100)
  83. : 0
  84. const previous =
  85. authTotalRequestsPrevious > 0
  86. ? Math.max(0, 100 - (authTotalErrorsPrevious / authTotalRequestsPrevious) * 100)
  87. : 0
  88. return { current, previous }
  89. }
  90. export const calculatePercentageChange = (current: number, previous: number): number => {
  91. if (previous === 0) return current > 0 ? 100 : 0
  92. return ((current - previous) / previous) * 100
  93. }
  94. export const getChangeColor = (percentageChange: number): string => {
  95. return percentageChange >= 0 ? 'text-brand' : 'text-destructive'
  96. }