ServiceFlow.sql.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. /**
  2. * Service Flow SQL Queries
  3. *
  4. * This file contains SQL queries for fetching enriched service flow data
  5. * showing how requests flow through different layers of the infrastructure.
  6. */
  7. // Service configuration for different log types
  8. const SERVICE_CONFIGS = {
  9. postgrest: {
  10. pathFilter: '%/rest/%',
  11. },
  12. auth: {
  13. pathFilter: '%/auth/%',
  14. },
  15. 'edge-function': {
  16. pathFilter: '%/functions/%',
  17. },
  18. storage: {
  19. pathFilter: '%/storage/%',
  20. },
  21. } as const
  22. type EdgeServiceType = keyof typeof SERVICE_CONFIGS
  23. /**
  24. * Base Edge Logs Service Flow Query
  25. * Consolidated query for all edge-based services (postgrest, auth, edge-function, storage)
  26. * to eliminate 500+ lines of SQL duplication
  27. */
  28. const getBaseEdgeServiceFlowQuery = (logId: string, serviceType: EdgeServiceType): string => {
  29. return `
  30. select
  31. id,
  32. el.timestamp as timestamp,
  33. '${serviceType}' as log_type,
  34. CAST(edge_logs_response.status_code AS STRING) as status,
  35. CASE
  36. WHEN edge_logs_response.status_code BETWEEN 200 AND 299 THEN 'success'
  37. WHEN edge_logs_response.status_code BETWEEN 400 AND 499 THEN 'warning'
  38. WHEN edge_logs_response.status_code >= 500 THEN 'error'
  39. ELSE 'success'
  40. END as level,
  41. -- Request data
  42. edge_logs_request.path as request_path,
  43. edge_logs_request.host as request_host,
  44. edge_logs_request.method as request_method,
  45. edge_logs_request.url as request_url,
  46. edge_logs_request.search as request_search,
  47. -- Response data
  48. edge_logs_response.origin_time as response_origin_time,
  49. edge_logs_response_headers.content_type as response_content_type,
  50. edge_logs_response_headers.cf_cache_status as response_cache_status,
  51. -- Client location data
  52. edge_logs_cf.continent as client_continent,
  53. edge_logs_cf.country as client_country,
  54. edge_logs_cf.city as client_city,
  55. edge_logs_cf.region as client_region,
  56. edge_logs_cf.regionCode as client_region_code,
  57. edge_logs_cf.latitude as client_latitude,
  58. edge_logs_cf.longitude as client_longitude,
  59. edge_logs_cf.timezone as client_timezone,
  60. -- Network data
  61. edge_logs_cf.httpProtocol as network_protocol,
  62. edge_logs_cf.colo as network_datacenter,
  63. -- Request headers
  64. edge_logs_request_headers.user_agent as headers_user_agent,
  65. edge_logs_request_headers.x_client_info as headers_x_client_info,
  66. edge_logs_request_headers.x_forwarded_proto as headers_x_forwarded_proto,
  67. edge_logs_request_headers.x_real_ip as headers_x_real_ip,
  68. edge_logs_request_headers.referer as headers_referer,
  69. -- Auth data
  70. authorization_payload.role as api_role,
  71. COALESCE(sb.auth_user, null) as auth_user,
  72. -- JWT Key Authentication (old keys)
  73. CASE
  74. WHEN apikey_payload.algorithm = 'HS256' AND
  75. apikey_payload.issuer = 'briven' AND
  76. apikey_payload.role IN ('anon', 'service_role')
  77. THEN apikey_payload.role
  78. WHEN apikey_payload IS NOT NULL THEN '<unrecognized>'
  79. ELSE NULL
  80. END as jwt_key_role,
  81. apikey_payload.signature_prefix as jwt_key_prefix,
  82. -- API Key Authentication (new keys from sb.apikey[0].apikey[0])
  83. CASE
  84. WHEN sb_apikey_inner.prefix LIKE '%publishable%' THEN 'anon'
  85. WHEN sb_apikey_inner.prefix LIKE '%secret%' THEN 'service_role'
  86. WHEN sb_apikey_inner.prefix IS NOT NULL THEN 'unknown'
  87. ELSE NULL
  88. END as api_key_role,
  89. sb_apikey_inner.prefix as api_key_prefix,
  90. sb_apikey_inner.error as api_key_error,
  91. sb_apikey_inner.hash as api_key_hash,
  92. -- User Authorization
  93. authorization_payload.role as authorization_role,
  94. null as user_id,
  95. null as user_email,
  96. -- Cloudflare Network Info
  97. edge_logs_response_headers.cf_ray as cf_ray,
  98. edge_logs_request_headers.cf_ipcountry as cf_country,
  99. edge_logs_cf.colo as cf_datacenter,
  100. edge_logs_request_headers.cf_connecting_ip as client_ip,
  101. -- JWT data
  102. apikey_payload.role as jwt_apikey_role,
  103. apikey_payload.algorithm as jwt_apikey_algorithm,
  104. apikey_payload.expires_at as jwt_apikey_expires_at,
  105. apikey_payload.issuer as jwt_apikey_issuer,
  106. apikey_payload.signature_prefix as jwt_apikey_signature_prefix,
  107. null as jwt_apikey_key_id,
  108. null as jwt_apikey_session_id,
  109. apikey_payload.subject as jwt_apikey_subject,
  110. authorization_payload.role as jwt_auth_role,
  111. authorization_payload.algorithm as jwt_auth_algorithm,
  112. authorization_payload.expires_at as jwt_auth_expires_at,
  113. authorization_payload.issuer as jwt_auth_issuer,
  114. authorization_payload.signature_prefix as jwt_auth_signature_prefix,
  115. authorization_payload.key_id as jwt_auth_key_id,
  116. authorization_payload.session_id as jwt_auth_session_id,
  117. authorization_payload.subject as jwt_auth_subject,
  118. -- Storage specific data (included for all but only populated for storage)
  119. edge_logs_response_headers.sb_gateway_mode as storage_edge_gateway_mode,
  120. edge_logs_response_headers.sb_gateway_version as storage_edge_gateway_version,
  121. edge_logs_response_headers.cf_ray as correlation_cf_ray,
  122. -- Raw data
  123. el as raw_log_data
  124. from edge_logs as el
  125. cross join unnest(metadata) as edge_logs_metadata
  126. cross join unnest(edge_logs_metadata.request) as edge_logs_request
  127. cross join unnest(edge_logs_metadata.response) as edge_logs_response
  128. left join unnest(edge_logs_response.headers) as edge_logs_response_headers
  129. left join unnest(edge_logs_request.headers) as edge_logs_request_headers
  130. left join unnest(edge_logs_request.cf) as edge_logs_cf
  131. left join unnest(edge_logs_request.sb) as sb
  132. left join unnest(sb.jwt) as jwt
  133. left join unnest(COALESCE(jwt.apikey, [])) as apikey
  134. left join unnest(COALESCE(apikey.payload, [])) as apikey_payload
  135. left join unnest(COALESCE(jwt.authorization, [])) as auth
  136. left join unnest(COALESCE(auth.payload, [])) as authorization_payload
  137. left join unnest(COALESCE(sb.apikey, [])) as sb_apikey_outer
  138. left join unnest(COALESCE(sb_apikey_outer.apikey, [])) as sb_apikey_inner
  139. WHERE
  140. el.id = '${logId}'
  141. `
  142. }
  143. /**
  144. * PostgREST Service Flow Query for /rest/ requests
  145. * Fetches enriched edge log data for PostgREST requests with additional service-specific metadata
  146. */
  147. export const getPostgrestServiceFlowQuery = (logId: string): string => {
  148. return getBaseEdgeServiceFlowQuery(logId, 'postgrest')
  149. }
  150. /**
  151. * Auth Service Flow Query for /auth/ requests
  152. * Fetches enriched edge log data for GoTrue auth requests with service-specific metadata
  153. */
  154. export const getAuthServiceFlowQuery = (logId: string): string => {
  155. return getBaseEdgeServiceFlowQuery(logId, 'auth')
  156. }
  157. /**
  158. * Edge Function Service Flow Query for /functions/ requests
  159. * Fetches enriched edge log data for Edge Function requests with service-specific metadata
  160. * NOTE: Uses function_edge_logs table instead of edge_logs, so kept separate like postgres
  161. */
  162. export const getEdgeFunctionServiceFlowQuery = (logId: string): string => {
  163. return `
  164. select
  165. fel.id as id,
  166. fel.timestamp as timestamp,
  167. 'edge-function' as log_type,
  168. CAST(fel_response.status_code AS STRING) as status,
  169. CASE
  170. WHEN fel_response.status_code BETWEEN 200 AND 299 THEN 'success'
  171. WHEN fel_response.status_code BETWEEN 400 AND 499 THEN 'warning'
  172. WHEN fel_response.status_code >= 500 THEN 'error'
  173. ELSE 'success'
  174. END as level,
  175. -- Request data
  176. fel_request.pathname as request_path,
  177. fel_request.host as request_host,
  178. fel_request.method as request_method,
  179. fel_request.url as request_url,
  180. fel_request.search as request_search,
  181. fel_request.protocol as request_protocol,
  182. fel_request.port as request_port,
  183. -- Response data (no origin_time in function_edge_logs)
  184. fel_response_headers.content_type as response_content_type,
  185. fel_response_headers.content_length as response_content_length,
  186. fel_response_headers.date as response_date,
  187. fel_response_headers.server as response_server,
  188. fel_response_headers.x_sb_edge_region as response_edge_region,
  189. fel_response_headers.x_sb_compute_multiplier as response_compute_multiplier,
  190. fel_response_headers.x_sb_resource_multiplier as response_resource_multiplier,
  191. fel_response_headers.x_served_by as response_served_by,
  192. -- Function specific data
  193. fel_metadata.execution_id as execution_id,
  194. fel_metadata.function_id as function_id,
  195. fel_metadata.deployment_id as deployment_id,
  196. fel_metadata.execution_time_ms as execution_time_ms,
  197. fel_metadata.project_ref as project_ref,
  198. fel_metadata.version as function_version,
  199. -- Request headers
  200. fel_request_headers.user_agent as headers_user_agent,
  201. fel_request_headers.x_client_info as headers_x_client_info,
  202. fel_request_headers.accept as headers_accept,
  203. fel_request_headers.accept_encoding as headers_accept_encoding,
  204. fel_request_headers.connection as headers_connection,
  205. fel_request_headers.cookie as headers_cookie,
  206. fel_request_headers.host as headers_host,
  207. -- Auth data
  208. authorization_payload.role as api_role,
  209. COALESCE(sb.auth_user, null) as auth_user,
  210. -- JWT Key Authentication (old keys)
  211. CASE
  212. WHEN apikey_payload.algorithm = 'HS256' AND
  213. apikey_payload.issuer = 'briven' AND
  214. apikey_payload.role IN ('anon', 'service_role')
  215. THEN apikey_payload.role
  216. WHEN apikey_payload IS NOT NULL THEN '<unrecognized>'
  217. ELSE NULL
  218. END as jwt_key_role,
  219. apikey_payload.signature_prefix as jwt_key_prefix,
  220. -- API Key Authentication (new keys from sb.apikey[0].apikey[0])
  221. CASE
  222. WHEN sb_apikey_inner.prefix LIKE '%publishable%' THEN 'anon'
  223. WHEN sb_apikey_inner.prefix LIKE '%secret%' THEN 'service_role'
  224. WHEN sb_apikey_inner.prefix IS NOT NULL THEN 'unknown'
  225. ELSE NULL
  226. END as api_key_role,
  227. sb_apikey_inner.prefix as api_key_prefix,
  228. sb_apikey_inner.error as api_key_error,
  229. sb_apikey_inner.hash as api_key_hash,
  230. -- User Authorization
  231. authorization_payload.role as authorization_role,
  232. null as user_id,
  233. null as user_email,
  234. -- JWT data
  235. apikey_payload.role as jwt_apikey_role,
  236. apikey_payload.algorithm as jwt_apikey_algorithm,
  237. apikey_payload.expires_at as jwt_apikey_expires_at,
  238. apikey_payload.issuer as jwt_apikey_issuer,
  239. apikey_payload.signature_prefix as jwt_apikey_signature_prefix,
  240. null as jwt_apikey_key_id,
  241. null as jwt_apikey_session_id,
  242. apikey_payload.subject as jwt_apikey_subject,
  243. authorization_payload.role as jwt_auth_role,
  244. authorization_payload.algorithm as jwt_auth_algorithm,
  245. authorization_payload.expires_at as jwt_auth_expires_at,
  246. authorization_payload.issuer as jwt_auth_issuer,
  247. authorization_payload.signature_prefix as jwt_auth_signature_prefix,
  248. authorization_payload.key_id as jwt_auth_key_id,
  249. authorization_payload.session_id as jwt_auth_session_id,
  250. authorization_payload.subject as jwt_auth_subject,
  251. -- Function logs aggregation
  252. function_logs_agg.function_log_count as function_log_count,
  253. function_logs_agg.logs as function_logs,
  254. function_logs_agg.last_event_message as last_event_message,
  255. -- Raw data
  256. fel as raw_log_data
  257. from function_edge_logs as fel
  258. cross join unnest(metadata) as fel_metadata
  259. cross join unnest(fel_metadata.response) as fel_response
  260. cross join unnest(fel_metadata.request) as fel_request
  261. left join unnest(fel_response.headers) as fel_response_headers
  262. left join unnest(fel_request.headers) as fel_request_headers
  263. left join unnest(fel_request.sb) as sb
  264. left join unnest(sb.jwt) as jwt
  265. left join unnest(COALESCE(jwt.apikey, [])) as apikey
  266. left join unnest(COALESCE(apikey.payload, [])) as apikey_payload
  267. left join unnest(COALESCE(jwt.authorization, [])) as auth
  268. left join unnest(COALESCE(auth.payload, [])) as authorization_payload
  269. left join unnest(COALESCE(sb.apikey, [])) as sb_apikey_outer
  270. left join unnest(COALESCE(sb_apikey_outer.apikey, [])) as sb_apikey_inner
  271. left join (
  272. SELECT
  273. fl_metadata.execution_id,
  274. COUNT(fl.id) as function_log_count,
  275. ANY_VALUE(fl.event_message) as last_event_message,
  276. ARRAY_AGG(STRUCT(fl.id, fl.timestamp, fl.event_message, fl_metadata.level, fl_metadata.event_type)) as logs
  277. FROM function_logs as fl
  278. CROSS JOIN UNNEST(fl.metadata) as fl_metadata
  279. WHERE fl_metadata.execution_id IS NOT NULL
  280. GROUP BY fl_metadata.execution_id
  281. ) as function_logs_agg on fel_metadata.execution_id = function_logs_agg.execution_id
  282. WHERE
  283. fel.id = '${logId}'
  284. `
  285. }
  286. /**
  287. * Storage Service Flow Query for /storage/ requests
  288. * Fetches enriched edge log data for Storage requests with service-specific metadata
  289. */
  290. export const getStorageServiceFlowQuery = (logId: string): string => {
  291. return getBaseEdgeServiceFlowQuery(logId, 'storage')
  292. }
  293. /**
  294. * Postgres Service Flow Query for database operations
  295. * Fetches enriched postgres log data with database-specific metadata
  296. *
  297. * This handles direct database operations, connections, and queries
  298. * NOTE: Uses postgres_logs table instead of edge_logs, so kept separate
  299. */
  300. export const getPostgresServiceFlowQuery = (logId: string): string => {
  301. return `
  302. select
  303. pgl.id as id,
  304. pgl.timestamp as timestamp,
  305. 'postgres' as log_type,
  306. pgl_parsed.sql_state_code as status,
  307. CASE
  308. WHEN pgl_parsed.error_severity = 'LOG' THEN 'success'
  309. WHEN pgl_parsed.error_severity = 'WARNING' THEN 'warning'
  310. WHEN pgl_parsed.error_severity = 'FATAL' THEN 'error'
  311. WHEN pgl_parsed.error_severity = 'ERROR' THEN 'error'
  312. ELSE 'success'
  313. END as level,
  314. -- Database connection details
  315. pgl_parsed.database_name as database_name,
  316. pgl_parsed.user_name as database_user,
  317. pgl_parsed.connection_from as connection_from,
  318. pgl_metadata.host as database_host,
  319. -- Query/Operation details
  320. pgl_parsed.command_tag as command_tag,
  321. pgl_parsed.backend_type as backend_type,
  322. pgl_parsed.query_id as query_id,
  323. -- Session details
  324. pgl_parsed.session_id as session_id,
  325. pgl_parsed.process_id as process_id,
  326. pgl_parsed.virtual_transaction_id as virtual_transaction_id,
  327. pgl_parsed.transaction_id as transaction_id,
  328. pgl_parsed.session_start_time as session_start_time,
  329. pgl_parsed.session_line_num as session_line_num,
  330. -- Error/Status details
  331. pgl_parsed.error_severity as error_severity,
  332. pgl_parsed.sql_state_code as sql_state_code,
  333. pgl.event_message as event_message,
  334. -- Timing
  335. pgl_parsed.timestamp as operation_timestamp,
  336. -- Raw data
  337. pgl as raw_log_data
  338. from postgres_logs as pgl
  339. cross join unnest(pgl.metadata) as pgl_metadata
  340. cross join unnest(pgl_metadata.parsed) as pgl_parsed
  341. WHERE
  342. pgl.id = '${logId}'
  343. `
  344. }