diff --git a/apps/docs/content/guides/self-hosting/docker.mdx b/apps/docs/content/guides/self-hosting/docker.mdx index eced3b834fb1d..365055a18b627 100644 --- a/apps/docs/content/guides/self-hosting/docker.mdx +++ b/apps/docs/content/guides/self-hosting/docker.mdx @@ -182,6 +182,10 @@ Edit the following settings in the `.env` file: - `PG_META_CRYPTO_KEY`: encryption key for securing connection strings used by Studio against postgres-meta. (Must be at least 32 characters; generate with `openssl rand -base64 24`) - `LOGFLARE_PUBLIC_ACCESS_TOKEN`: API token for log ingestion and querying. Used by Vector and Studio to send and query logs. (Must be at least 32 characters; generate with `openssl rand -base64 24`) - `LOGFLARE_PRIVATE_ACCESS_TOKEN`: API token for Logflare management operations. Used by Studio for administrative tasks. Never expose client-side. (Must be at least 32 characters; generate with `openssl rand -base64 24`) +- `S3_PROTOCOL_ACCESS_KEY_ID`: Access key ID (username-like) for authenticating API requests to S3-compatible storage. (Generate with `openssl rand -hex 16`) +- `S3_PROTOCOL_ACCESS_KEY_SECRET`: Secret key (password-like) used with S3_PROTOCOL_ACCESS_KEY_ID to sign and authorize S3 storage operations. (Generate with `openssl rand -hex 32`) +{/* supa-mdx-lint-disable-next-line Rule003Spelling */} +- `MINIO_ROOT_PASSWORD`: Root administrator password for the MinIO server. (Must be 8+ characters; generate with `openssl rand -hex 16`) Review and change URL environment variables: diff --git a/apps/studio/.github/eslint-rule-baselines.json b/apps/studio/.github/eslint-rule-baselines.json index d8ffa12f34063..94e3e7c1bb6ef 100644 --- a/apps/studio/.github/eslint-rule-baselines.json +++ b/apps/studio/.github/eslint-rule-baselines.json @@ -1,9 +1,9 @@ { "rules": { - "react-hooks/exhaustive-deps": 213, + "react-hooks/exhaustive-deps": 211, "import/no-anonymous-default-export": 57, "@tanstack/query/exhaustive-deps": 13, - "@typescript-eslint/no-explicit-any": 1231 + "@typescript-eslint/no-explicit-any": 1220 }, "ruleFiles": { "react-hooks/exhaustive-deps": { @@ -39,6 +39,7 @@ "components/interfaces/Billing/Payment/PaymentConfirmation.tsx": 1, "components/interfaces/Billing/Payment/PaymentMethods/NewPaymentMethodElement.tsx": 2, "components/interfaces/Connect/DatabaseConnectionString.tsx": 2, + "components/interfaces/ConnectSheet/content/steps/mcp/cursor/content.tsx": 1, "components/interfaces/Database/Backups/PITR/TimeInput.tsx": 4, "components/interfaces/Database/EnumeratedTypes/CreateEnumeratedTypeSidePanel.tsx": 1, "components/interfaces/Database/Indexes/Indexes.tsx": 2, @@ -79,7 +80,6 @@ "components/interfaces/SQLEditor/RenameQueryModal.tsx": 1, "components/interfaces/SQLEditor/SQLEditor.tsx": 3, "components/interfaces/SQLEditor/UtilityPanel/SavingIndicator.tsx": 1, - "components/interfaces/Settings/API/PostgrestConfig.tsx": 1, "components/interfaces/Settings/Addons/CustomDomainSidePanel.tsx": 1, "components/interfaces/Settings/Addons/IPv4SidePanel.tsx": 1, "components/interfaces/Settings/Addons/PITRSidePanel.tsx": 1, @@ -105,7 +105,6 @@ "components/interfaces/UnifiedLogs/ServiceFlow/components/ServiceFlowHeader.tsx": 2, "components/layouts/EdgeFunctionsLayout/EdgeFunctionDetailsLayout.tsx": 1, "components/layouts/ProjectLayout/ConnectingState.tsx": 1, - "components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackWidget.tsx": 2, "components/layouts/ProjectLayout/PausedState/PauseDisabledState.tsx": 1, "components/layouts/ProjectLayout/index.tsx": 1, "components/layouts/SQLEditorLayout/SQLEditorNavV2/SQLEditorNav.tsx": 4, @@ -246,7 +245,7 @@ "components/grid/components/editor/JsonEditor.tsx": 2, "components/grid/components/editor/SelectEditor.tsx": 1, "components/grid/components/editor/TextEditor.tsx": 2, - "components/grid/components/formatter/ReferenceRecordPeek.tsx": 4, + "components/grid/components/formatter/ReferenceRecordPeek.tsx": 3, "components/grid/components/grid/AddColumn.tsx": 2, "components/grid/components/grid/Grid.utils.tsx": 2, "components/grid/components/grid/SelectColumn.tsx": 3, @@ -258,8 +257,7 @@ "components/grid/utils/column.ts": 2, "components/grid/utils/common.ts": 1, "components/grid/utils/gridColumns.tsx": 10, - "components/interfaces/Account/AccessTokens/NewAccessTokenButton.tsx": 1, - "components/interfaces/Account/AccessTokens/NewAccessTokenDialog.tsx": 1, + "components/interfaces/Account/AccessTokens/Scoped/Form/ResourceAccess/ResourceAccess.types.ts": 2, "components/interfaces/Auth/AuditLogsForm.tsx": 1, "components/interfaces/Auth/AuthProvidersForm/AuthProvidersForm.types.ts": 1, "components/interfaces/Auth/AuthProvidersForm/FormField.tsx": 4, @@ -308,7 +306,6 @@ "components/interfaces/Database/EnumeratedTypes/EnumeratedTypeValueRow.tsx": 1, "components/interfaces/Database/Functions/CreateFunction/FunctionEditor.tsx": 1, "components/interfaces/Database/Functions/FunctionsList/FunctionList.tsx": 3, - "components/interfaces/Database/Hooks/HooksList/HooksList.tsx": 2, "components/interfaces/Database/Indexes/Indexes.tsx": 1, "components/interfaces/Database/Migrations/Migrations.tsx": 2, "components/interfaces/Database/Privileges/PrivilegesTable.tsx": 1, @@ -371,7 +368,6 @@ "components/interfaces/Organization/BillingSettings/Subscription/PlanUpdateSidePanel.tsx": 1, "components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx": 6, "components/interfaces/Organization/BillingSettings/Subscription/UpgradeModal.tsx": 3, - "components/interfaces/Organization/GeneralSettings/DeleteOrganizationButton.tsx": 3, "components/interfaces/Organization/IntegrationSettings/IntegrationSettings.tsx": 1, "components/interfaces/Organization/IntegrationSettings/SidePanelVercelProjectLinker.tsx": 1, "components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx": 2, @@ -389,7 +385,6 @@ "components/interfaces/ProjectAPIDocs/ProjectAPIDocs.constants.ts": 2, "components/interfaces/ProjectAPIDocs/ResourceContent.tsx": 1, "components/interfaces/ProjectCreation/PostgresVersionSelector.tsx": 2, - "components/interfaces/ProjectCreation/SchemaGenerator.tsx": 1, "components/interfaces/QueryPerformance/IndexAdvisor/EnableIndexAdvisorButton.tsx": 1, "components/interfaces/QueryPerformance/IndexAdvisor/IndexAdvisorDisabledState.tsx": 1, "components/interfaces/QueryPerformance/IndexAdvisor/index-advisor.utils.ts": 2, @@ -427,7 +422,6 @@ "components/interfaces/SQLEditor/UtilityPanel/UtilityPanel.tsx": 1, "components/interfaces/SQLEditor/hooks.ts": 1, "components/interfaces/SQLEditor/useAddDefinitions.ts": 1, - "components/interfaces/SchemaVisualizer/index.tsx": 1, "components/interfaces/Settings/Addons/Addons.tsx": 1, "components/interfaces/Settings/Addons/CustomDomainSidePanel.tsx": 1, "components/interfaces/Settings/Addons/IPv4SidePanel.tsx": 1, @@ -708,7 +702,7 @@ "lib/role-impersonation.ts": 1, "lib/telemetry/track.ts": 1, "pages/_app.tsx": 1, - "pages/account/tokens.tsx": 1, + "pages/account/tokens/scoped.tsx": 1, "pages/api/ai/feedback/rate.ts": 2, "pages/api/check-cname.ts": 1, "pages/api/edge-functions/test.ts": 1, @@ -724,7 +718,6 @@ "pages/integrations/vercel/[slug]/deploy-button/new-project.tsx": 1, "pages/integrations/vercel/[slug]/marketplace/choose-project.tsx": 1, "pages/new/index.tsx": 1, - "pages/project/[ref]/api/index.tsx": 3, "pages/project/[ref]/auth/templates/[templateId].tsx": 1, "pages/project/[ref]/functions/[functionSlug]/index.tsx": 3, "pages/project/[ref]/settings/log-drains.tsx": 1, diff --git a/apps/studio/components/grid/components/grid/Grid.tsx b/apps/studio/components/grid/components/grid/Grid.tsx index 0b589f5451fb0..2079e3692ea28 100644 --- a/apps/studio/components/grid/components/grid/Grid.tsx +++ b/apps/studio/components/grid/components/grid/Grid.tsx @@ -97,12 +97,7 @@ export const Grid = memo( const { mutate: sendEvent } = useSendEventMutation() - const { - isValidFile: isValidFileDraggedOver, - isDraggedOver, - onDragOver, - onFileDrop, - } = useCsvFileDrop({ + const { isDraggedOver, onDragOver, onFileDrop } = useCsvFileDrop({ enabled: isTableEmpty && !isForeignTable, onFileDropped: (file) => tableEditorSnap.onImportData(valtioRef(file)), onTelemetryEvent: (eventName) => { @@ -241,9 +236,7 @@ export const Grid = memo( {(rows ?? []).length === 0 && (
{isLoading && !isDisabled && ( @@ -272,18 +265,13 @@ export const Grid = memo(
) : (filters ?? []).length === 0 ? ( -
-

- {isDraggedOver ? ( - isValidFileDraggedOver ? ( - 'Drop your CSV file here' - ) : ( - Only CSV files are accepted - ) - ) : ( - 'This table is empty' - )} -

+
+

This table is empty

{tableEntityType === ENTITY_TYPE.FOREIGN_TABLE ? (

@@ -292,30 +280,28 @@ export const Grid = memo(

) : ( - !isDraggedOver && ( -
- -

- or drag and drop a CSV file here -

-
- ) +
+ +

+ or drag and drop a CSV file here +

+
)}
) : ( diff --git a/apps/studio/components/interfaces/Settings/Logs/LogTable.tsx b/apps/studio/components/interfaces/Settings/Logs/LogTable.tsx index 68fccc043e14a..64046af1178ce 100644 --- a/apps/studio/components/interfaces/Settings/Logs/LogTable.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/LogTable.tsx @@ -1,4 +1,10 @@ import { PermissionAction } from '@supabase/shared-types/out/constants' +import { IS_PLATFORM, useParams } from 'common' +import { ButtonTooltip } from 'components/ui/ButtonTooltip' +import { DownloadResultsButton } from 'components/ui/DownloadResultsButton' +import { useSelectedLog } from 'hooks/analytics/useSelectedLog' +import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions' +import { useProfile } from 'lib/profile' import { isEqual } from 'lodash' import { Copy, Eye, EyeOff, Play } from 'lucide-react' import { Key, ReactNode, useCallback, useEffect, useMemo, useState } from 'react' @@ -6,31 +12,25 @@ import { Item, Menu, useContextMenu } from 'react-contexify' import DataGrid, { Column, RenderRowProps, Row } from 'react-data-grid' import { createPortal } from 'react-dom' import { toast } from 'sonner' - -import { IS_PLATFORM, useParams } from 'common' -import { ButtonTooltip } from 'components/ui/ButtonTooltip' -import { DownloadResultsButton } from 'components/ui/DownloadResultsButton' -import { useSelectedLog } from 'hooks/analytics/useSelectedLog' -import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions' -import { useProfile } from 'lib/profile' import type { ResponseError } from 'types' import { Button, + cn, + copyToClipboard, ResizableHandle, ResizablePanel, ResizablePanelGroup, - cn, - copyToClipboard, } from 'ui' + import AuthColumnRenderer from './LogColumnRenderers/AuthColumnRenderer' import DatabaseApiColumnRender from './LogColumnRenderers/DatabaseApiColumnRender' import DatabasePostgresColumnRender from './LogColumnRenderers/DatabasePostgresColumnRender' import DefaultPreviewColumnRenderer from './LogColumnRenderers/DefaultPreviewColumnRenderer' import FunctionsEdgeColumnRender from './LogColumnRenderers/FunctionsEdgeColumnRender' import FunctionsLogsColumnRender from './LogColumnRenderers/FunctionsLogsColumnRender' -import LogSelection from './LogSelection' import type { LogData, LogQueryError, QueryType } from './Logs.types' import { isDefaultLogPreviewFormat } from './Logs.utils' +import LogSelection from './LogSelection' import { DefaultErrorRenderer } from './LogsErrorRenderers/DefaultErrorRenderer' import ResourcesExceededErrorRenderer from './LogsErrorRenderers/ResourcesExceededErrorRenderer' import { LogsTableEmptyState } from './LogsTableEmptyState' @@ -64,7 +64,7 @@ type LogMap = { [id: string]: LogData } * * When in custom data display mode, the side panel will not open when focusing on logs. */ -const LogTable = ({ +export const LogTable = ({ data = [], queryType, onHistogramToggle, @@ -120,6 +120,9 @@ const LogTable = ({ const hasId = columnNames.includes('id') const hasTimestamp = columnNames.includes('timestamp') + const panelContentMinSize = 40 + const panelContentMaxSize = 60 + const LOGS_EXPLORER_CONTEXT_MENU_ID = 'logs-explorer-context-menu' const DEFAULT_COLUMNS = columnNames.map((v: keyof LogData, idx) => { const column = `logs-column-${idx}` @@ -127,7 +130,7 @@ const LogTable = ({ key: column, name: v as string, resizable: true, - renderCell: ({ row }: any) => { + renderCell: ({ row }) => { return ( { @@ -140,7 +143,7 @@ const LogTable = ({ ) }, - renderHeaderCell: (props) => { + renderHeaderCell: () => { return
{v}
}, minWidth: 128, @@ -392,7 +395,13 @@ const LogTable = ({ {!queryType && } - + - - {selectionOpen && ( - - { - onSelectedLogChange?.(null) - setSelectionOpen(false) - }} - log={selectedLog} - error={selectedLogError} - queryType={queryType} - /> - + <> + + + { + onSelectedLogChange?.(null) + setSelectionOpen(false) + }} + log={selectedLog} + error={selectedLogError} + queryType={queryType} + /> + + )} ) } -export default LogTable diff --git a/apps/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx b/apps/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx index 5b986b63fe624..77e05a6eed1c5 100644 --- a/apps/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx @@ -27,7 +27,7 @@ import { import { DatePickerValue } from './Logs.DatePickers' import type { Filters, LogSearchCallback, LogTemplate, QueryType } from './Logs.types' import { maybeShowUpgradePromptIfNotEntitled } from './Logs.utils' -import LogTable from './LogTable' +import { LogTable } from './LogTable' import UpgradePrompt from './UpgradePrompt' /** diff --git a/apps/studio/components/layouts/LogsLayout/LogsSidebarMenuV2.tsx b/apps/studio/components/layouts/LogsLayout/LogsSidebarMenuV2.tsx index 2135799e473fc..412f796de34b0 100644 --- a/apps/studio/components/layouts/LogsLayout/LogsSidebarMenuV2.tsx +++ b/apps/studio/components/layouts/LogsLayout/LogsSidebarMenuV2.tsx @@ -1,8 +1,3 @@ -import { ChevronRight, CircleHelpIcon, Plus } from 'lucide-react' -import Link from 'next/link' -import { useRouter } from 'next/router' -import React, { useState } from 'react' - import { IS_PLATFORM, useFlag, useParams } from 'common' import { useFeaturePreviewModal, @@ -17,6 +12,10 @@ import { useContentQuery } from 'data/content/content-query' import { useReplicationSourcesQuery } from 'data/replication/sources-query' import { useCurrentOrgPlan } from 'hooks/misc/useCurrentOrgPlan' import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' +import { ChevronRight, CircleHelpIcon, Plus } from 'lucide-react' +import Link from 'next/link' +import { useRouter } from 'next/router' +import React, { useState } from 'react' import { Badge, Button, @@ -33,6 +32,7 @@ import { InnerSideMenuItem, } from 'ui-patterns/InnerSideMenu' import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader' + import { FeaturePreviewSidebarPanel } from '../../ui/FeaturePreviewSidebarPanel' const SupaIcon = ({ className }: { className?: string }) => { @@ -368,7 +368,7 @@ export function LogsSidebarMenuV2() { /> )} {savedQueries.map((query) => ( - // kemal: i know, i know, temp any fix. + ))} @@ -381,7 +381,7 @@ export function LogsSidebarMenuV2() { illustration={
{LOG_DRAIN_TYPES.map((type) => - React.cloneElement(type.icon, { height: 20, width: 20 }) + React.cloneElement(type.icon, { key: type.name, height: 20, width: 20 }) )}
} diff --git a/apps/studio/hooks/ui/useCsvFileDrop.ts b/apps/studio/hooks/ui/useCsvFileDrop.ts index 2f2da36a1889f..26119d2313553 100644 --- a/apps/studio/hooks/ui/useCsvFileDrop.ts +++ b/apps/studio/hooks/ui/useCsvFileDrop.ts @@ -1,7 +1,6 @@ -import { type DragEvent, useCallback, useState } from 'react' - import { type ImportDataFileDroppedEvent } from 'common/telemetry-constants' import { flagInvalidFileImport } from 'components/interfaces/TableGridEditor/SidePanelEditor/SpreadsheetImport/SpreadsheetImport.utils' +import { useCallback, useState, type DragEvent } from 'react' interface UseCsvFileDropOptions { enabled: boolean @@ -11,7 +10,6 @@ interface UseCsvFileDropOptions { interface UseCsvFileDropReturn { isDraggedOver: boolean - isValidFile: boolean onDragOver: (event: DragEvent) => void onFileDrop: (event: DragEvent) => void } @@ -22,7 +20,6 @@ export function useCsvFileDrop({ onTelemetryEvent, }: UseCsvFileDropOptions): UseCsvFileDropReturn { const [isDraggedOver, setIsDraggedOver] = useState(false) - const [isValidFile, setIsValidFile] = useState(false) const onDragOver = useCallback( (event: DragEvent) => { @@ -35,15 +32,13 @@ export function useCsvFileDrop({ if (event.type === 'dragover' && !isDraggedOver) { setIsDraggedOver(true) - setIsValidFile(item.type === 'text/csv') } else if (event.type === 'dragleave' || event.type === 'drop') { setIsDraggedOver(false) - setIsValidFile(false) } event.stopPropagation() event.preventDefault() }, - [enabled, isDraggedOver, isValidFile] + [enabled, isDraggedOver] ) const onFileDrop = useCallback( @@ -68,7 +63,6 @@ export function useCsvFileDrop({ ) return { - isValidFile: isValidFile && isDraggedOver, isDraggedOver, onDragOver, onFileDrop, diff --git a/apps/studio/instrumentation-client.ts b/apps/studio/instrumentation-client.ts index 9bd34e8177f0b..5dd543ad6d3fd 100644 --- a/apps/studio/instrumentation-client.ts +++ b/apps/studio/instrumentation-client.ts @@ -57,6 +57,10 @@ Sentry.init({ }), // Setting this option to true will print useful information to the console while you're setting up Sentry. debug: false, + + // Enable performance monitoring - Next.js routes and API calls are automatically instrumented + tracesSampleRate: 0.1, // Capture 10% of transactions for performance monitoring + // [Ali] Filter out browser extensions and user scripts (FE-2094) // Using denyUrls to block known third-party script patterns denyUrls: [/userscript/i], diff --git a/apps/studio/pages/project/[ref]/logs/explorer/index.tsx b/apps/studio/pages/project/[ref]/logs/explorer/index.tsx index 778e06362797e..93ca9663537bb 100644 --- a/apps/studio/pages/project/[ref]/logs/explorer/index.tsx +++ b/apps/studio/pages/project/[ref]/logs/explorer/index.tsx @@ -1,27 +1,24 @@ import { useMonaco } from '@monaco-editor/react' import { useLocalStorage } from '@uidotdev/usehooks' -import dayjs from 'dayjs' -import type { editor } from 'monaco-editor' -import { useRouter } from 'next/router' -import { useEffect, useMemo, useRef, useState } from 'react' -import { toast } from 'sonner' - import { IS_PLATFORM, LOCAL_STORAGE_KEYS, useParams } from 'common' - import { EXPLORER_DATEPICKER_HELPERS, + getDefaultHelper, LOGS_LARGE_DATE_RANGE_DAYS_THRESHOLD, TEMPLATES, - getDefaultHelper, } from 'components/interfaces/Settings/Logs/Logs.constants' -import { LogData, LogTemplate, LogsWarning } from 'components/interfaces/Settings/Logs/Logs.types' import { DatePickerValue } from 'components/interfaces/Settings/Logs/Logs.DatePickers' +import { LogData, LogsWarning, LogTemplate } from 'components/interfaces/Settings/Logs/Logs.types' import { maybeShowUpgradePromptIfNotEntitled, useEditorHints, } from 'components/interfaces/Settings/Logs/Logs.utils' +import { + buildLogQueryParams, + resolveLogDateRange, +} from 'components/interfaces/Settings/Logs/logsDateRange' import LogsQueryPanel from 'components/interfaces/Settings/Logs/LogsQueryPanel' -import LogTable from 'components/interfaces/Settings/Logs/LogTable' +import { LogTable } from 'components/interfaces/Settings/Logs/LogTable' import UpgradePrompt from 'components/interfaces/Settings/Logs/UpgradePrompt' import DefaultLayout from 'components/layouts/DefaultLayout' import LogsLayout from 'components/layouts/LogsLayout/LogsLayout' @@ -33,6 +30,7 @@ import { UpsertContentPayload, useContentUpsertMutation, } from 'data/content/content-upsert-mutation' +import dayjs from 'dayjs' import useLogsQuery from 'hooks/analytics/useLogsQuery' import { useLogsUrlState } from 'hooks/analytics/useLogsUrlState' import { useCustomContent } from 'hooks/custom-content/useCustomContent' @@ -42,11 +40,11 @@ import { useUpgradePrompt } from 'hooks/misc/useUpgradePrompt' import { uuidv4 } from 'lib/helpers' import { useProfile } from 'lib/profile' import { useTrack } from 'lib/telemetry/track' +import type { editor } from 'monaco-editor' +import { useRouter } from 'next/router' +import { useEffect, useMemo, useRef, useState } from 'react' +import { toast } from 'sonner' import type { LogSqlSnippets, NextPageWithLayout } from 'types' -import { - buildLogQueryParams, - resolveLogDateRange, -} from 'components/interfaces/Settings/Logs/logsDateRange' import { Button, Form, diff --git a/apps/studio/sentry.edge.config.ts b/apps/studio/sentry.edge.config.ts index c5db7e27ad95c..ad17e8b86a208 100644 --- a/apps/studio/sentry.edge.config.ts +++ b/apps/studio/sentry.edge.config.ts @@ -12,4 +12,7 @@ Sentry.init({ }), // Setting this option to true will print useful information to the console while you're setting up Sentry. debug: false, + + // Enable performance monitoring + tracesSampleRate: 0.1, // Capture 10% of transactions for performance monitoring }) diff --git a/apps/studio/sentry.server.config.ts b/apps/studio/sentry.server.config.ts index dba6457bc5f2b..13d62e7bc317e 100644 --- a/apps/studio/sentry.server.config.ts +++ b/apps/studio/sentry.server.config.ts @@ -11,6 +11,9 @@ Sentry.init({ }), // Setting this option to true will print useful information to the console while you're setting up Sentry. debug: false, + + // Enable performance monitoring + tracesSampleRate: 0.1, // Capture 10% of transactions for performance monitoring ignoreErrors: [ // Used exclusively in Monaco Editor. 'ResizeObserver', diff --git a/apps/studio/tests/features/logs/LogTable.test.tsx b/apps/studio/tests/features/logs/LogTable.test.tsx index 73a85fdf1288c..d25554b3ad801 100644 --- a/apps/studio/tests/features/logs/LogTable.test.tsx +++ b/apps/studio/tests/features/logs/LogTable.test.tsx @@ -1,12 +1,13 @@ import { screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import LogTable from 'components/interfaces/Settings/Logs/LogTable' +import { LogTable } from 'components/interfaces/Settings/Logs/LogTable' import dayjs from 'dayjs' import customParseFormat from 'dayjs/plugin/customParseFormat' import relativeTime from 'dayjs/plugin/relativeTime' import timezone from 'dayjs/plugin/timezone' import utc from 'dayjs/plugin/utc' import { beforeAll, expect, test, vi } from 'vitest' + import { render } from '../../helpers' dayjs.extend(customParseFormat) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 6fd37691c2d07..50db1b44ac93c 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -356,8 +356,8 @@ services: container_name: supabase-analytics image: supabase/logflare:1.30.3 restart: unless-stopped - ports: - - 4000:4000 + # ports: + # - 4000:4000 # Uncomment to use Big Query backend for analytics # volumes: # - type: bind @@ -459,7 +459,7 @@ services: vector: container_name: supabase-vector - image: timberio/vector:0.28.1-alpine + image: timberio/vector:0.53.0-alpine restart: unless-stopped volumes: - ./volumes/logs/vector.yml:/etc/vector/vector.yml:ro,z diff --git a/docker/utils/generate-keys.sh b/docker/utils/generate-keys.sh index e67c6957bc62b..d9a16395bf694 100644 --- a/docker/utils/generate-keys.sh +++ b/docker/utils/generate-keys.sh @@ -60,6 +60,8 @@ logflare_private_access_token=$(gen_base64 24) s3_protocol_access_key_id=$(gen_hex 16) s3_protocol_access_key_secret=$(gen_hex 32) +minio_root_password=$(gen_hex 16) + echo "" echo "JWT_SECRET=${jwt_secret}" echo "" @@ -75,6 +77,7 @@ echo "LOGFLARE_PUBLIC_ACCESS_TOKEN=${logflare_public_access_token}" echo "LOGFLARE_PRIVATE_ACCESS_TOKEN=${logflare_private_access_token}" echo "S3_PROTOCOL_ACCESS_KEY_ID=${s3_protocol_access_key_id}" echo "S3_PROTOCOL_ACCESS_KEY_SECRET=${s3_protocol_access_key_secret}" +echo "MINIO_ROOT_PASSWORD=${minio_root_password}" echo "" postgres_password=$(gen_hex 16) @@ -114,6 +117,7 @@ sed \ -e "s|^LOGFLARE_PRIVATE_ACCESS_TOKEN=.*$|LOGFLARE_PRIVATE_ACCESS_TOKEN=${logflare_private_access_token}|" \ -e "s|^S3_PROTOCOL_ACCESS_KEY_ID=.*$|S3_PROTOCOL_ACCESS_KEY_ID=${s3_protocol_access_key_id}|" \ -e "s|^S3_PROTOCOL_ACCESS_KEY_SECRET=.*$|S3_PROTOCOL_ACCESS_KEY_SECRET=${s3_protocol_access_key_secret}|" \ + -e "s|^MINIO_ROOT_PASSWORD=.*$|MINIO_ROOT_PASSWORD=${minio_root_password}|" \ -e "s|^POSTGRES_PASSWORD=.*$|POSTGRES_PASSWORD=${postgres_password}|" \ -e "s|^DASHBOARD_PASSWORD=.*$|DASHBOARD_PASSWORD=${dashboard_password}|" \ .env diff --git a/docker/volumes/api/kong.yml b/docker/volumes/api/kong.yml index 7441cd31285ab..ae69cec282421 100644 --- a/docker/volumes/api/kong.yml +++ b/docker/volumes/api/kong.yml @@ -197,29 +197,29 @@ services: - name: cors ## Analytics routes - - name: analytics-v1-api - _comment: 'Analytics: /analytics/v1/api/endpoints/* -> http://logflare:4000/api/endpoints/*' - url: http://analytics:4000/api/endpoints - routes: - - name: analytics-v1-api - strip_path: true - paths: - - /analytics/v1/api/endpoints/ - - # auth on analytics dashboard - - name: analytics-v1 - _comment: 'Analytics: /analytics/v1/* -> http://logflare:4000/*' - url: http://analytics:4000/ - routes: - - name: dashboard-v1-all - strip_path: true - paths: - - /analytics/v1 - plugins: - - name: cors - - name: basic-auth - config: - hide_credentials: true + ## Not used - Studio and Vector talk directly to analytics via Docker networking. + ## If external access is needed, add routes with key-auth matching Logflare's x-api-key auth. + # - name: analytics-v1-api + # _comment: 'Analytics: /analytics/v1/api/endpoints/* -> http://logflare:4000/api/endpoints/*' + # url: http://analytics:4000/api/endpoints + # routes: + # - name: analytics-v1-api + # strip_path: true + # paths: + # - /analytics/v1/api/endpoints/ + # - name: analytics-v1 + # _comment: 'Analytics: /analytics/v1/* -> http://logflare:4000/*' + # url: http://analytics:4000/ + # routes: + # - name: dashboard-v1-all + # strip_path: true + # paths: + # - /analytics/v1 + # plugins: + # - name: cors + # - name: basic-auth + # config: + # hide_credentials: true ## Secure Database routes - name: meta diff --git a/docker/volumes/logs/vector.yml b/docker/volumes/logs/vector.yml index e79d7e8268570..d600bf2867795 100644 --- a/docker/volumes/logs/vector.yml +++ b/docker/volumes/logs/vector.yml @@ -49,10 +49,13 @@ transforms: .metadata.request.headers.referer = req.referer .metadata.request.headers.user_agent = req.agent .metadata.request.headers.cf_connecting_ip = req.client - .metadata.request.method = req.method - .metadata.request.path = req.path - .metadata.request.protocol = req.protocol .metadata.response.status_code = req.status + url, split_err = split(req.request, " ") + if split_err == null { + .metadata.request.method = url[0] + .metadata.request.path = url[1] + .metadata.request.protocol = url[2] + } } if err != null { abort @@ -101,7 +104,7 @@ transforms: parsed, err = parse_regex(.event_message, r'^(?P