import { BlockFieldConfig } from '../types' import { getStorageMetadata } from '../utils/storageUtils' import { formatBytes } from '@/lib/helpers' // Helper functions that avoid duplication with existing storage utilities const getFileName = (path: string): string => { if (!path) return '' // Remove query parameters and hash const cleanPath = path.split('?')[0].split('#')[0] // Get the last part after the last slash const segments = cleanPath.split('/') return segments[segments.length - 1] || '' } const formatStorageDate = (dateString: string): string => { try { return new Date(dateString).toLocaleString() } catch { return dateString } } // ============================================================================= // NETWORK FIELDS // ============================================================================= // Field configurations - using filterable field IDs where possible export const originFields: BlockFieldConfig[] = [ { id: 'date', // Matches filterFields 'date' (timerange) - FILTERABLE label: 'Time', getValue: (data) => { if (!data?.timestamp && !data?.date) return null try { const timestamp = data?.timestamp || data?.date return new Date(timestamp).toLocaleString() } catch { return 'Invalid date' } }, }, ] // Primary Network Fields (Always Visible) - FILTERABLE export const networkPrimaryFields: BlockFieldConfig[] = [ { id: 'host', // Matches filterFields 'host' (input) - FILTERABLE label: 'Host', getValue: (data, enrichedData) => enrichedData?.request_host || enrichedData?.host || data?.host, }, { id: 'method', // Matches filterFields 'method' (checkbox) - FILTERABLE label: 'Method', getValue: (data, enrichedData) => enrichedData?.request_method || enrichedData?.method || data?.method, }, { id: 'pathname', // Matches filterFields 'pathname' (input) - FILTERABLE label: 'Path', getValue: (data, enrichedData) => enrichedData?.request_path || enrichedData?.pathname || data?.pathname, }, { id: 'user_agent', label: 'Client', getValue: (_data, enrichedData) => { const userAgent = enrichedData?.headers_user_agent if (!userAgent) return null // TODO: Parse user agent for nice display with icons return userAgent.length > 50 ? userAgent.substring(0, 50) + '...' : userAgent }, requiresEnrichedData: true, }, ] // Primary API Key Field (Always Visible) - Shows API key type only export const apiKeyPrimaryField: BlockFieldConfig = { id: 'api_key_role', label: 'API Key', getValue: (_data, enrichedData) => { const prefix = enrichedData?.api_key_prefix const jwtRole = enrichedData?.jwt_key_role const authRole = enrichedData?.authorization_role if (prefix) { // Extract key type from prefix if (prefix.includes('publishable')) return 'publishable' else if (prefix.includes('secret')) return 'secret' return 'unknown' } // Fallback to JWT apikey postgres role if no API key prefix if (jwtRole) { return jwtRole } // Fallback to authorization postgres role if no apikey JWT if (authRole) { return authRole } return null }, requiresEnrichedData: true, } // Additional API Key Fields (Collapsible) export const apiKeyAdditionalFields: BlockFieldConfig[] = [ { id: 'api_key_prefix_full', label: 'API Key Prefix', getValue: (_data, enrichedData) => { const prefix = enrichedData?.api_key_prefix return prefix || null }, requiresEnrichedData: true, }, { id: 'postgres_role', label: 'Postgres Role', getValue: (_data, enrichedData) => enrichedData?.jwt_key_role, requiresEnrichedData: true, }, { id: 'api_key_error', label: 'API Key Error', getValue: (_data, enrichedData) => enrichedData?.api_key_error, requiresEnrichedData: true, }, { id: 'api_key_hash', label: 'API Key Hash', getValue: (_data, enrichedData) => { const hash = enrichedData?.api_key_hash return hash ? `${hash.substring(0, 12)}...` : null }, requiresEnrichedData: true, }, { id: 'authorization_role', label: 'Postgres Role', getValue: (_data, enrichedData) => enrichedData?.authorization_role, requiresEnrichedData: true, }, ] // Primary User Field (Always Visible) export const userPrimaryField: BlockFieldConfig = { id: 'user_id', label: 'User', getValue: (_data, enrichedData) => { const userId = enrichedData?.user_id return userId ? `${userId.substring(0, 8)}...` : null }, requiresEnrichedData: true, } // Additional User Fields (Collapsible) export const userAdditionalFields: BlockFieldConfig[] = [ { id: 'auth_user', // Matches filterFields 'auth_user' (input) - FILTERABLE label: 'Auth User', getValue: (data, enrichedData) => enrichedData?.auth_user || data?.auth_user, }, { id: 'user_email', label: 'User Email', getValue: (_data, enrichedData) => enrichedData?.user_email, requiresEnrichedData: true, }, ] // Primary Location Field (Always Visible) export const locationPrimaryField: BlockFieldConfig = { id: 'client_country', label: 'Location', getValue: (_data, enrichedData) => { const country = enrichedData?.client_country || enrichedData?.cf_country const city = enrichedData?.client_city if (country && city) return `${city}, ${country}` if (country) return country if (city) return city return null }, requiresEnrichedData: true, } // Additional Location Fields (Collapsible) - includes IP addresses export const locationAdditionalFields: BlockFieldConfig[] = [ { id: 'client_continent', label: 'Continent', getValue: (_data, enrichedData) => enrichedData?.client_continent, requiresEnrichedData: true, }, { id: 'client_region', label: 'Region', getValue: (_data, enrichedData) => enrichedData?.client_region, requiresEnrichedData: true, }, { id: 'client_timezone', label: 'Timezone', getValue: (_data, enrichedData) => enrichedData?.client_timezone, requiresEnrichedData: true, }, { id: 'x_real_ip', label: 'Real IP', getValue: (_data, enrichedData) => enrichedData?.headers_x_real_ip, requiresEnrichedData: true, }, { id: 'client_ip', label: 'Client IP', getValue: (_data, enrichedData) => enrichedData?.client_ip, requiresEnrichedData: true, }, ] // Authorization Fields (Collapsible) - JWT authorization details export const authorizationFields: BlockFieldConfig[] = [ { id: 'jwt_auth_key_id', label: 'Key ID', getValue: (_data, enrichedData) => enrichedData?.jwt_auth_key_id || enrichedData?.jwt_apikey_key_id, requiresEnrichedData: true, }, { id: 'jwt_auth_session_id', label: 'Session ID', getValue: (_data, enrichedData) => enrichedData?.jwt_auth_session_id || enrichedData?.jwt_apikey_session_id, requiresEnrichedData: true, }, { id: 'jwt_auth_subject', label: 'Subject', getValue: (_data, enrichedData) => enrichedData?.jwt_auth_subject || enrichedData?.jwt_apikey_subject, requiresEnrichedData: true, }, { id: 'jwt_auth_issuer', label: 'Issuer', getValue: (_data, enrichedData) => enrichedData?.jwt_auth_issuer || enrichedData?.jwt_apikey_issuer, requiresEnrichedData: true, }, { id: 'jwt_auth_algorithm', label: 'Algorithm', getValue: (_data, enrichedData) => enrichedData?.jwt_auth_algorithm || enrichedData?.jwt_apikey_algorithm, requiresEnrichedData: true, }, { id: 'jwt_auth_expires_at', label: 'Expires At', getValue: (_data, enrichedData) => { const expiresAt = enrichedData?.jwt_auth_expires_at || enrichedData?.jwt_apikey_expires_at if (!expiresAt) return null try { return new Date(expiresAt * 1000).toLocaleString() } catch { return expiresAt } }, requiresEnrichedData: true, }, ] // Tech Details Fields (Collapsible) export const techDetailsFields: BlockFieldConfig[] = [ { id: 'network_protocol', label: 'Protocol', getValue: (_data, enrichedData) => enrichedData?.network_protocol, requiresEnrichedData: true, }, { id: 'cf_datacenter', label: 'Datacenter', getValue: (_data, enrichedData) => enrichedData?.network_datacenter || enrichedData?.cf_datacenter, requiresEnrichedData: true, }, { id: 'cache_status', label: 'Cache Status', getValue: (_data, enrichedData) => enrichedData?.response_cache_status, requiresEnrichedData: true, }, { id: 'cf_ray', label: 'CF-Ray', getValue: (_data, enrichedData) => enrichedData?.cf_ray, requiresEnrichedData: true, }, { id: 'x_client_info', label: 'SDK', getValue: (_data, enrichedData) => enrichedData?.headers_x_client_info, requiresEnrichedData: true, }, { id: 'x_forwarded_proto', label: 'Forwarded Proto', getValue: (_data, enrichedData) => enrichedData?.headers_x_forwarded_proto, requiresEnrichedData: true, }, ] // ============================================================================= // POSTGREST FIELDS // ============================================================================= // Primary PostgREST Fields (Always Visible) - FILTERABLE export const postgrestPrimaryFields: BlockFieldConfig[] = [ { id: 'status', // Matches filterFields 'status' (checkbox) - FILTERABLE label: 'Status', getValue: (data, enrichedData) => enrichedData?.status || data?.status, }, { id: 'postgres_role', label: 'Postgres Role', getValue: (_data, enrichedData) => enrichedData?.api_role, requiresEnrichedData: true, }, { id: 'response_time', label: 'Response Time', getValue: (data, enrichedData) => { const time = enrichedData?.response_origin_time || data?.response_time_ms return time ? `${time}ms` : null }, requiresEnrichedData: true, }, ] // PostgREST Response Details (Collapsible) export const postgrestResponseFields: BlockFieldConfig[] = [ { id: 'query_params', label: 'Query', getValue: (_data, enrichedData) => enrichedData?.request_search, requiresEnrichedData: true, }, { id: 'content_type', label: 'Content Type', getValue: (_data, enrichedData) => enrichedData?.response_content_type, requiresEnrichedData: true, }, { id: 'message', label: 'Message', getValue: (_data, enrichedData) => enrichedData?.message, requiresEnrichedData: true, }, ] // ============================================================================= // AUTH FIELDS // ============================================================================= // Primary GoTrue/Auth Fields (Always Visible) export const authPrimaryFields: BlockFieldConfig[] = [ { id: 'auth_path', label: 'Auth Path', getValue: (data, enrichedData) => { return enrichedData?.path || enrichedData?.request_path || data?.path }, requiresEnrichedData: true, }, { id: 'log_id', label: 'Log ID', getValue: (data, enrichedData) => { const logId = data?.id || enrichedData?.id return logId ? `${logId.substring(0, 8)}...` : null }, }, { id: 'referer', label: 'Referer', getValue: (_data, enrichedData) => { return enrichedData?.headers_referer || null }, requiresEnrichedData: true, }, ] // ============================================================================= // EDGE FUNCTION FIELDS // ============================================================================= // Primary Edge Function Fields (Always Visible) export const edgeFunctionPrimaryFields: BlockFieldConfig[] = [ { id: 'status', label: 'Status', getValue: (data, enrichedData) => enrichedData?.status || data?.status, }, { id: 'function_path', label: 'Function Path', getValue: (data, enrichedData) => { return ( enrichedData?.path || enrichedData?.request_path || data?.path || enrichedData?.request_url ) }, requiresEnrichedData: false, }, { id: 'execution_time', label: 'Execution Time', getValue: (data, enrichedData) => { const time = enrichedData?.execution_time_ms || data?.execution_time_ms return time ? `${time}ms` : null }, requiresEnrichedData: false, }, { id: 'execution_id', label: 'Execution ID', getValue: (data, enrichedData) => { const execId = enrichedData?.execution_id || data?.execution_id return execId ? `${execId.substring(0, 8)}...` : null }, requiresEnrichedData: false, }, ] // Edge Function Details (Collapsible) export const edgeFunctionDetailsFields: BlockFieldConfig[] = [ { id: 'function_id', label: 'Function ID', getValue: (data, enrichedData) => { const funcId = enrichedData?.function_id || data?.function_id return funcId ? `${funcId.substring(0, 8)}...` : null }, requiresEnrichedData: false, }, { id: 'execution_region', label: 'Execution Region', getValue: (data, enrichedData) => { return enrichedData?.execution_region || data?.execution_region || null }, requiresEnrichedData: false, }, { id: 'function_log_count', label: 'Function Logs', getValue: (data, enrichedData) => { const count = enrichedData?.function_log_count || data?.function_log_count || data?.log_count return count ? `${count} logs` : 'No logs' }, requiresEnrichedData: false, }, { id: 'method', label: 'Method', getValue: (data, enrichedData) => enrichedData?.method || data?.method, requiresEnrichedData: false, }, { id: 'user_agent', label: 'User Agent', getValue: (data, enrichedData) => { const ua = enrichedData?.headers_user_agent || data?.headers_user_agent return ua ? (ua.length > 30 ? `${ua.substring(0, 30)}...` : ua) : null }, requiresEnrichedData: false, }, ] // ============================================================================= // STORAGE FIELDS // ============================================================================= // Primary Storage Fields (Always Visible) export const storagePrimaryFields: BlockFieldConfig[] = [ { id: 'status', label: 'Status', getValue: (data, enrichedData) => enrichedData?.status || data?.status, }, { id: 'filename', label: 'File Name', getValue: (data, enrichedData) => { const path = enrichedData?.path || enrichedData?.request_path || data?.path return path ? getFileName(path) : null }, requiresEnrichedData: false, }, { id: 'content_type_size', label: 'Type & Size', getValue: (data, enrichedData) => { const status = enrichedData?.status || data?.status const isObjectDeleted = status === 404 || status === '404' const hasError = status && Number(status) >= 400 // For deleted objects, show status message if (isObjectDeleted) { return 'Object deleted' } else if (hasError) { return `Error ${status}` } // Try to get metadata like PreviewPane does const metadata = getStorageMetadata(data, enrichedData) const mimeType = metadata?.mimetype const size = metadata?.size // Fallback to headers if metadata not available const contentType = mimeType || enrichedData?.response_content_type || enrichedData?.storage_request_content_type const contentLength = size || (enrichedData?.storage_content_length ? parseInt(enrichedData.storage_content_length) : null) if (contentType && contentLength) { return `${contentType} - ${formatBytes(contentLength)}` } else if (contentType) { return contentType } else if (contentLength) { return formatBytes(contentLength) } return 'Unknown type' }, requiresEnrichedData: true, }, { id: 'response_time', label: 'Response Time', getValue: (data, enrichedData) => { const time = enrichedData?.response_origin_time || data?.response_time_ms return time ? `${time}ms` : null }, requiresEnrichedData: true, }, ] // Storage Details (Collapsible) export const storageDetailsFields: BlockFieldConfig[] = [ { id: 'storage_path', label: 'Storage Path', getValue: (data, enrichedData) => { const path = enrichedData?.path || enrichedData?.request_path || data?.path // Extract just the object path from the full storage path const match = path?.match(/\/storage\/v1\/object\/([^\/]+)\/(.+)/) if (match) { const [, bucketName, objectPath] = match return `${bucketName}/${objectPath}` } return path }, requiresEnrichedData: false, }, { id: 'last_modified', label: 'Last Modified', getValue: (data, enrichedData) => { const status = enrichedData?.status || data?.status const isObjectDeleted = status === 404 || status === '404' const hasError = status && Number(status) >= 400 if (isObjectDeleted) { return 'Object deleted' } else if (hasError) { return 'Unavailable' } // Try to get dates from metadata like PreviewPane does const metadata = getStorageMetadata(data, enrichedData) const updatedAt = metadata?.updated_at || data?.updated_at || enrichedData?.updated_at const lastModified = enrichedData?.storage_last_modified || updatedAt return lastModified ? formatStorageDate(lastModified) : null }, requiresEnrichedData: true, }, { id: 'etag', label: 'ETag', getValue: (data, enrichedData) => { const status = enrichedData?.status || data?.status const isObjectDeleted = status === 404 || status === '404' const hasError = status && Number(status) >= 400 if (isObjectDeleted) { return 'Object deleted' } else if (hasError) { return 'Unavailable' } const etag = enrichedData?.storage_etag return etag ? `${etag.substring(0, 12)}...` : null }, requiresEnrichedData: true, }, { id: 'content_disposition', label: 'Content Disposition', getValue: (data, enrichedData) => { const status = enrichedData?.status || data?.status const isObjectDeleted = status === 404 || status === '404' const hasError = status && Number(status) >= 400 if (isObjectDeleted) { return 'Object deleted' } else if (hasError) { return 'Unavailable' } return enrichedData?.storage_content_disposition || null }, requiresEnrichedData: true, }, { id: 'method', label: 'Method', getValue: (data, enrichedData) => enrichedData?.method || data?.method, requiresEnrichedData: false, }, { id: 'user_agent', label: 'User Agent', getValue: (data, enrichedData) => { const ua = enrichedData?.headers_user_agent || data?.headers_user_agent return ua ? (ua.length > 30 ? `${ua.substring(0, 30)}...` : ua) : null }, requiresEnrichedData: false, }, ] // ============================================================================= // POSTGRES FIELDS // ============================================================================= // Primary Postgres Fields (Always Visible) export const postgresPrimaryFields: BlockFieldConfig[] = [ { id: 'event_message', label: 'Message', getValue: (data, enrichedData) => enrichedData?.event_message || data?.event_message, wrap: true, }, { id: 'status', label: 'Status', getValue: (data, enrichedData) => enrichedData?.status || data?.status, }, { id: 'command_tag', label: 'Command', getValue: (data, enrichedData) => enrichedData?.command_tag || data?.command_tag, requiresEnrichedData: true, }, { id: 'database_name', label: 'Database', getValue: (data, enrichedData) => enrichedData?.database_name || data?.database_name, requiresEnrichedData: true, }, { id: 'database_user', label: 'User', getValue: (data, enrichedData) => enrichedData?.database_user || data?.database_user, requiresEnrichedData: true, }, ] // Postgres Details (Collapsible) export const postgresDetailsFields: BlockFieldConfig[] = [ { id: 'backend_type', label: 'Backend Type', getValue: (data, enrichedData) => enrichedData?.backend_type || data?.backend_type, requiresEnrichedData: true, }, { id: 'connection_from', label: 'Connection From', getValue: (data, enrichedData) => enrichedData?.connection_from || data?.connection_from, requiresEnrichedData: true, }, { id: 'session_id', label: 'Session ID', getValue: (data, enrichedData) => enrichedData?.session_id || data?.session_id, requiresEnrichedData: true, }, { id: 'process_id', label: 'Process ID', getValue: (data, enrichedData) => enrichedData?.process_id || data?.process_id, requiresEnrichedData: true, }, { id: 'query_id', label: 'Query ID', getValue: (data, enrichedData) => { const queryId = enrichedData?.query_id || data?.query_id return queryId ? String(queryId) : null }, requiresEnrichedData: true, }, { id: 'transaction_id', label: 'Transaction ID', getValue: (data, enrichedData) => { const txId = enrichedData?.transaction_id || data?.transaction_id return txId ? String(txId) : null }, requiresEnrichedData: true, }, { id: 'virtual_transaction_id', label: 'Virtual TX ID', getValue: (data, enrichedData) => enrichedData?.virtual_transaction_id || data?.virtual_transaction_id, requiresEnrichedData: true, }, { id: 'session_start_time', label: 'Session Started', getValue: (data, enrichedData) => { const startTime = enrichedData?.session_start_time || data?.session_start_time return startTime ? new Date(startTime).toLocaleString() : null }, requiresEnrichedData: true, }, ]