mirror of
https://github.com/supabase/supabase.git
synced 2026-10-10 20:05:06 +03:00
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Tidying up the bottom panel in unified logs. Taking care of some visual quirks etc. Also preparing this area to house some other future concepts via tabs. | Before | After | |--------|--------| | <img width="828" height="384" alt="Screenshot 2026-04-30 at 11 24 09" src="https://github.com/user-attachments/assets/804bdf1c-7cdb-4dd8-bf1e-31c434ef1436" /> | <img width="830" height="407" alt="Screenshot 2026-04-30 at 11 22 53" src="https://github.com/user-attachments/assets/28555efe-f893-4bae-bcb0-284e6db733e6" /> | <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Redesigned service flow panel with Overview and Raw JSON tabs * Added Previous/Next navigation controls with Arrow Up/Down keyboard support * New detail components and section headers with icons for clearer organization * Improved Postgres detail view and message/session display * **Bug Fixes / Changes** * Removed legacy header UI and related controls * **UI / Style** * Enhanced copy-to-clipboard feedback animation * Updated "Load more" button styling * Adjusted panel sizing for improved resizing behavior <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Ali Waseem <waseema393@gmail.com>
750 lines
22 KiB
TypeScript
750 lines
22 KiB
TypeScript
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,
|
|
},
|
|
]
|