logs.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import assert from 'node:assert'
  2. import { LogsService } from '@supabase/mcp-server-supabase/platform'
  3. import { stripIndent } from 'common-tags'
  4. import { WrappedResult } from './types'
  5. import { assertSelfHosted } from './util'
  6. import { PROJECT_ANALYTICS_URL } from '@/lib/constants/api'
  7. export type RetrieveAnalyticsDataOptions = {
  8. name: string
  9. projectRef: string
  10. params: Record<string, string | undefined>
  11. }
  12. export type AnalyticsResult = {
  13. result?: any[]
  14. error?: {
  15. message: string
  16. }
  17. [key: string]: any
  18. }
  19. /**
  20. * Retrieves analytics data from Logflare.
  21. *
  22. * _Only call this from server-side self-hosted code._
  23. */
  24. export async function retrieveAnalyticsData({
  25. name,
  26. projectRef,
  27. params,
  28. }: RetrieveAnalyticsDataOptions): Promise<WrappedResult<AnalyticsResult>> {
  29. assertSelfHosted()
  30. assert(PROJECT_ANALYTICS_URL, 'PROJECT_ANALYTICS_URL is required')
  31. assert(process.env.LOGFLARE_PRIVATE_ACCESS_TOKEN, 'LOGFLARE_PRIVATE_ACCESS_TOKEN is required')
  32. const url = new URL(`${PROJECT_ANALYTICS_URL}endpoints/query/${name}`)
  33. url.searchParams.set('project', projectRef)
  34. // Add all other params
  35. Object.entries(params).forEach(([key, value]) => {
  36. if (value !== undefined) {
  37. url.searchParams.set(key, value)
  38. }
  39. })
  40. try {
  41. const response = await fetch(url, {
  42. method: 'GET',
  43. headers: {
  44. 'x-api-key': process.env.LOGFLARE_PRIVATE_ACCESS_TOKEN,
  45. 'Content-Type': 'application/json',
  46. Accept: 'application/json',
  47. },
  48. })
  49. const result = await response.json()
  50. if (!response.ok) {
  51. const error = new Error(
  52. result?.error?.message ?? `Failed to retrieve analytics data: ${response.statusText}`
  53. )
  54. return { data: undefined, error }
  55. }
  56. return { data: result, error: undefined }
  57. } catch (error) {
  58. if (error instanceof Error) {
  59. return { data: undefined, error }
  60. }
  61. throw error
  62. }
  63. }
  64. export function getLogQuery(service: LogsService, limit: number = 100): string {
  65. assertSelfHosted()
  66. switch (service) {
  67. case 'api': {
  68. return stripIndent`
  69. select id, edge_logs.timestamp, event_message, request.method, request.path, request.search, response.status_code
  70. from edge_logs
  71. cross join unnest(metadata) as m
  72. cross join unnest(m.request) as request
  73. cross join unnest(m.response) as response
  74. order by timestamp desc
  75. limit ${limit};
  76. `
  77. }
  78. case 'branch-action': {
  79. throw new Error('Branching is only supported in the hosted Briven platform')
  80. }
  81. case 'postgres': {
  82. return stripIndent`
  83. select postgres_logs.timestamp, id, event_message, parsed.error_severity, parsed.detail, parsed.hint
  84. from postgres_logs
  85. cross join unnest(metadata) as m
  86. cross join unnest(m.parsed) as parsed
  87. order by timestamp desc
  88. limit ${limit};
  89. `
  90. }
  91. case 'edge-function': {
  92. return stripIndent`
  93. select id, function_edge_logs.timestamp, event_message
  94. from function_edge_logs
  95. order by timestamp desc
  96. limit ${limit}
  97. `
  98. }
  99. case 'auth': {
  100. return stripIndent`
  101. select id, auth_logs.timestamp, event_message, metadata.level, metadata.status, metadata.path, metadata.msg as msg, metadata.error from auth_logs
  102. cross join unnest(metadata) as metadata
  103. order by timestamp desc
  104. limit ${limit};
  105. `
  106. }
  107. case 'storage': {
  108. return stripIndent`
  109. select id, storage_logs.timestamp, event_message from storage_logs
  110. order by timestamp desc
  111. limit ${limit};
  112. `
  113. }
  114. case 'realtime': {
  115. return stripIndent`
  116. select id, realtime_logs.timestamp, event_message from realtime_logs
  117. order by timestamp desc
  118. limit ${limit};
  119. `
  120. }
  121. default: {
  122. throw new Error(`Unsupported log service: ${service}`)
  123. }
  124. }
  125. }