From efc0ad3fcee33d146f3ce38dd2e6f39cd1029ae7 Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Thu, 23 Jul 2026 12:38:10 +0200 Subject: [PATCH 1/7] fix: fix migration dialog does not show the correct content in some edge cases (#48239) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem When two projects have migrations with the same version but different content, the details panel does not update the content and shows the first loaded migration one. ## Solution This is because the CodeEditor does not react to content only changes. Settings its `key` ensures it does. Unfortunately, we can't unit test that the CodeEditor content changes correctly. ## How to test - create two projects and push a migration with the same version but different content on them - open the _Database/Migrations_ page for the first project - click the _View migration SQL_ and ensure its content matches the migration for this project - select the other project using the top bar - click the _View migration SQL_ and ensure its content matches the migration for this project ## Summary by CodeRabbit * **Bug Fixes** * Improved migration SQL display when switching between migrations or projects. * Ensured the code editor consistently refreshes with the currently selected migration’s statements. * Improved type safety for the migration search input. --- .../interfaces/Database/Migrations/Migrations.tsx | 12 +++++++----- .../pages/project/[ref]/database/migrations.tsx | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/studio/components/interfaces/Database/Migrations/Migrations.tsx b/apps/studio/components/interfaces/Database/Migrations/Migrations.tsx index 24f4763fa216f..b893c62e324be 100644 --- a/apps/studio/components/interfaces/Database/Migrations/Migrations.tsx +++ b/apps/studio/components/interfaces/Database/Migrations/Migrations.tsx @@ -32,7 +32,7 @@ import { formatMigrationVersionLabel, parseMigrationVersion } from '@/lib/migrat import { SHORTCUT_IDS } from '@/state/shortcuts/registry' import { useShortcut } from '@/state/shortcuts/useShortcut' -const Migrations = () => { +export const Migrations = () => { const [search, setSearch] = useState('') const [selectedMigration, setSelectedMigration] = useState() const searchInputRef = useRef(null) @@ -116,7 +116,7 @@ const Migrations = () => { placeholder="Search for a migration" value={search} className="w-full lg:w-52" - onChange={(e: any) => setSearch(e.target.value)} + onChange={(e) => setSearch(e.target.value)} icon={} /> @@ -221,10 +221,14 @@ const Migrations = () => {
{ ) } - -export default Migrations diff --git a/apps/studio/pages/project/[ref]/database/migrations.tsx b/apps/studio/pages/project/[ref]/database/migrations.tsx index e57b15dd6fd58..425a6ec87626c 100644 --- a/apps/studio/pages/project/[ref]/database/migrations.tsx +++ b/apps/studio/pages/project/[ref]/database/migrations.tsx @@ -9,7 +9,7 @@ import { } from 'ui-patterns/PageHeader' import { PageSection, PageSectionContent } from 'ui-patterns/PageSection' -import Migrations from '@/components/interfaces/Database/Migrations/Migrations' +import { Migrations } from '@/components/interfaces/Database/Migrations/Migrations' import DatabaseLayout from '@/components/layouts/DatabaseLayout/DatabaseLayout' import { DefaultLayout } from '@/components/layouts/DefaultLayout' import { DocsButton } from '@/components/ui/DocsButton' From 81085286828925562344c3d2188155d64f856fd4 Mon Sep 17 00:00:00 2001 From: Ivan Vasilov Date: Thu, 23 Jul 2026 12:48:33 +0200 Subject: [PATCH 2/7] Fix: Update the auth user field in the Logs page (#48237) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix the user filter to work with `edge_logs`. - Update the `auth_user` field to be derived from other log attributes. - Removed filtering for `postgres_logs` since it didn't really filter by user actions, only by user id mentions. ## Summary by CodeRabbit - **Bug Fixes** - Improved unified logs user filtering to rely only on exact attribution identifiers from authentication and edge log sources, removing partial message-based matching. - Updated unified logs user identification by deriving `auth_user` from authentication actor IDs or edge JWT subject values. - Refined “user filter reachability” logic to consider only attributable log types (auth and edge). - **Tests** - Adjusted unified logs query tests to match the updated attribution routing and reachability outcomes. --- .../UnifiedLogs/UnifiedLogs.queries.test.ts | 24 +++++++++++-------- .../UnifiedLogs/UnifiedLogs.queries.ts | 23 +++++++++++------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.queries.test.ts b/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.queries.test.ts index 15f713a34747f..a9b62e271bdc5 100644 --- a/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.queries.test.ts +++ b/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.queries.test.ts @@ -282,14 +282,18 @@ describe('UnifiedLogs.queries (OTEL flat)', () => { }) describe('user filter', () => { - it('restricts to auth_logs/postgres_logs and skips the default postgres+edge restriction', () => { + it('restricts to auth_logs/edge_logs and skips the default postgres+edge restriction', () => { const sql = getUnifiedLogsQuery(withUser('user-123')) const where = sql.split(/\bWHERE\b/)[1] ?? '' - expect(where).toContain(`log_attributes['auth_event.actor_id'] = 'user-123'`) - expect(where).toContain(`source = 'postgres_logs'`) - // The unfiltered default (postgres_logs OR edge_logs) would incorrectly exclude + expect(where).toContain( + `(source = 'auth_logs' AND log_attributes['auth_event.actor_id'] = 'user-123')` + ) + expect(where).toContain( + `(source = 'edge_logs' AND log_attributes['request.sb.jwt.authorization.payload.subject'] = 'user-123')` + ) + // The unfiltered default (edge_logs OR postgres_logs) would incorrectly exclude // auth_logs, the primary attributable source, so it must not appear here. - expect(where).not.toContain(`(source = 'postgres_logs') OR (source = 'edge_logs')`) + expect(where).not.toContain(`(source = 'edge_logs') OR (source = 'postgres_logs')`) }) it('does not restrict sources when the user filter is inactive', () => { @@ -321,8 +325,8 @@ describe('UnifiedLogs.queries (OTEL flat)', () => { expect(isUserFilterUnreachable(withUser('user-123', 'log_type:eq:auth'))).toBe(false) }) - it('is false when the explicit log_type filter includes an attributable source (postgres)', () => { - expect(isUserFilterUnreachable(withUser('user-123', 'log_type:eq:postgres'))).toBe(false) + it('is false when the explicit log_type filter includes an attributable source (edge)', () => { + expect(isUserFilterUnreachable(withUser('user-123', 'log_type:eq:edge'))).toBe(false) }) it('is false when at least one of several selected log types is attributable', () => { @@ -332,12 +336,12 @@ describe('UnifiedLogs.queries (OTEL flat)', () => { }) it('is true when the explicit log_type filter restricts to a single non-attributable source', () => { - expect(isUserFilterUnreachable(withUser('user-123', 'log_type:eq:edge'))).toBe(true) + expect(isUserFilterUnreachable(withUser('user-123', 'log_type:eq:storage'))).toBe(true) }) it('is true when every selected log type is non-attributable', () => { expect( - isUserFilterUnreachable(withUser('user-123', 'log_type:eq:edge', 'log_type:eq:storage')) + isUserFilterUnreachable(withUser('user-123', 'log_type:eq:realtime', 'log_type:eq:storage')) ).toBe(true) }) @@ -347,7 +351,7 @@ describe('UnifiedLogs.queries (OTEL flat)', () => { it('(neq) is true only when both attributable sources are excluded', () => { expect( - isUserFilterUnreachable(withUser('user-123', 'log_type:neq:auth', 'log_type:neq:postgres')) + isUserFilterUnreachable(withUser('user-123', 'log_type:neq:auth', 'log_type:neq:edge')) ).toBe(true) }) }) diff --git a/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.queries.ts b/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.queries.ts index 1b56bae859e6d..619fd33ceefdf 100644 --- a/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.queries.ts +++ b/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.queries.ts @@ -104,6 +104,13 @@ const LEVEL_EXPR: SafeLogSqlFragment = safeSql`CASE ELSE 'success' END` +// The `auth_user` column is derived from the source-specific fields that can be attributed to a user. +const AUTH_USER_EXPR: SafeLogSqlFragment = safeSql`nullIf(CASE + WHEN source = 'auth_logs' THEN log_attributes['auth_event.actor_id'] + WHEN source = 'edge_logs' THEN log_attributes['request.sb.jwt.authorization.payload.subject'] + ELSE null + END, '')` + const logTypeWhereCondition = (logTypes: string[]): SafeLogSqlFragment => { const effective = logTypes.filter((t) => t in LOG_TYPE_CONDITION) const types = effective.length ? effective : [...DEFAULT_LOG_TYPES] @@ -248,7 +255,7 @@ const truncationFunction = (level: 'MINUTE' | 'HOUR' | 'DAY'): SafeLogSqlFragmen * inlined so the result can be referenced (or filtered) at the same query * level — the OTEL endpoint rejects subqueries. */ -const rowProjection = (): SafeLogSqlFragment => safeSql` +const ROW_PROJECTION: SafeLogSqlFragment = safeSql` id, null AS source_id, timestamp, @@ -258,6 +265,7 @@ const rowProjection = (): SafeLogSqlFragment => safeSql` ${ATTR.path} AS pathname, event_message, ${ATTR.method} AS method, + ${AUTH_USER_EXPR} AS auth_user, null AS log_count, null AS logs ` @@ -335,17 +343,14 @@ const userAttributionCondition = (search: QuerySearchParamsType): SafeLogSqlFrag const value = userFilterValue(search) if (!value) return null const exact = lit(value) - const contains = lit('%' + value + '%') return safeSql`( - (source = 'auth_logs' AND ( - log_attributes['auth_event.actor_id'] = ${exact} - OR event_message ILIKE ${contains} - )) - OR (source = 'postgres_logs' AND event_message ILIKE ${contains}) + (source = 'auth_logs' AND log_attributes['auth_event.actor_id'] = ${exact}) + OR + (source = 'edge_logs' AND log_attributes['request.sb.jwt.authorization.payload.subject'] = ${exact}) )` } -const USER_ATTRIBUTABLE_SOURCES = new Set([LOG_TYPE_TO_SOURCE.auth, LOG_TYPE_TO_SOURCE.postgres]) +const USER_ATTRIBUTABLE_SOURCES = new Set([LOG_TYPE_TO_SOURCE.auth, LOG_TYPE_TO_SOURCE.edge]) /** * True when the user filter is active but an explicit log_type filter restricts the @@ -411,7 +416,7 @@ const applySearchParamsFilter = (search: QuerySearchParamsType): SafeLogSqlFragm export const getUnifiedLogsQuery = (search: QuerySearchParamsType): SafeLogSqlFragment => { const conditions = buildBaseWhere(search) return safeSql`-- unified logs: row list -SELECT ${rowProjection()} +SELECT ${ROW_PROJECTION} FROM logs ${whereClause(conditions)} ` From 4272aba4b16c111b0ce5922a6d5891e35bf4fdb3 Mon Sep 17 00:00:00 2001 From: Jeremias Menichelli Date: Thu, 23 Jul 2026 14:42:07 +0200 Subject: [PATCH 3/7] fix: Update Grafana Cloud integration guides (#47776) --- .../docs/components/MetricsStackCards.data.ts | 5 +-- .../telemetry/metrics/grafana-cloud.mdx | 44 +++++++++++++------ .../telemetry/metrics/grafana-self-hosted.mdx | 8 ++-- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/apps/docs/components/MetricsStackCards.data.ts b/apps/docs/components/MetricsStackCards.data.ts index 1cbd0a4a15698..637af23172874 100644 --- a/apps/docs/components/MetricsStackCards.data.ts +++ b/apps/docs/components/MetricsStackCards.data.ts @@ -17,10 +17,7 @@ export const metricsStackOptions: MetricsStackOption[] = [ iconKind: 'grafana', iconColor: '#F05A28', iconBg: 'rgba(240,90,40,0.1)', - badges: [ - { label: 'Supabase guide', variant: 'default' }, - { label: 'Community', variant: 'community' }, - ], + badges: [{ label: 'Supabase guide', variant: 'default' }], }, { title: 'Grafana + self-hosted Prometheus', diff --git a/apps/docs/content/guides/telemetry/metrics/grafana-cloud.mdx b/apps/docs/content/guides/telemetry/metrics/grafana-cloud.mdx index 39d96d066ba4d..42f894f2b8794 100644 --- a/apps/docs/content/guides/telemetry/metrics/grafana-cloud.mdx +++ b/apps/docs/content/guides/telemetry/metrics/grafana-cloud.mdx @@ -6,37 +6,55 @@ description: 'Use Grafana Cloud’s managed Prometheus to visualize Supabase met Grafana Cloud gives you a fully managed Prometheus endpoint plus hosted Grafana dashboards, which makes it the fastest way to explore the Supabase Metrics API without operating your own infrastructure. - +## Installation -Grafana maintains a [Supabase integration guide](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/integrations/integration-reference/integration-supabase/) for Grafana Cloud. It runs on the same Metrics API documented here, but is community-maintained by Grafana, so feature coverage might differ from what Supabase officially supports. +The Grafana Cloud integration is available in the Supabase Dashboard. [Add the integration](/dashboard/project/_/integrations/grafana-cloud/overview?utm_source=metrics-api&utm_medium=docs&utm_campaign=supabase_grafana_cloud_one_click_integration) and select a project to get a fully-configured instance in one click: authentication, metric scraping, and a pre-built dashboard tracking 200+ metrics, set up automatically. + +By integrating Supabase with Grafana Cloud, users gain monitoring capabilities for Supabase performance and operations. The included dashboard offers a comprehensive overview of Supabase performance, supplemented with Postgres metrics. + +## Manual setup + +The [Grafana Cloud integration](/dashboard/project/_/integrations/grafana-cloud/overview?utm_source=metrics-api&utm_medium=docs&utm_campaign=supabase_grafana_cloud_one_click_integration) available in the Supabase Dashboard is the recommended path. You can still configure and run the setup manually by following the steps below. + + + +Use this guide only if you need full manual control (custom scrape topology, self-hosted +Prometheus or non-standard auth). -## Prerequisites +### Prerequisites - A Supabase project with access to the Metrics API (Secret API key `sb_secret_...`). - A Grafana Cloud account with Prometheus metrics enabled (Free or Pro tier). - A Grafana API token with the `metrics:write` and `metrics:read` scopes if you plan to push data manually. -<$Partial path="metrics_access.mdx" /> - -## 1. Create a Grafana Cloud stack +### 1. Create a Grafana Cloud stack 1. Sign in to [Grafana Cloud](https://grafana.com/auth/sign-in). -2. Create (or select) a stack that has **Prometheus Metrics** enabled (Free and Pro tiers both work for this guide). +2. Create or select a stack that has **Prometheus Metrics** enabled. + +### 2. Install the Supabase integration for Grafana Cloud + +1. In your Grafana Cloud stack, click **Connections** in the left-hand menu. +2. Select the **Supabase** integration and follow the steps outlined on the **Configuration** page: +3. Give your scrape job a descriptive name like `production-eu-central-1`. +4. Set your Project ID and enter the service account API key. +5. Test the connection and save the scrape job. +6. Once scrape jobs are configured, click Install to add the prebuilt dashboards to your Grafana Cloud instance. -## 2. Configure the Supabase integration +### 3. Configure the Supabase integration 1. Navigate to **Connections → Add new connection → Supabase** inside Grafana Cloud. 2. Provide: - Your Supabase project ref (e.g. `abcd1234`). - - The Metrics API endpoint: `https://.supabase.co/customer/v1/privileged/metrics`. + - The Metrics API endpoint (e.g. `https://.supabase.co/customer/v1/privileged/metrics`). - HTTP Basic Auth credentials (`sb_secret_...`). -3. Choose the scrape interval (1 minute recommended) and test the connection. Grafana Cloud will deploy an agent in the background that scrapes the Metrics API and forwards the data to Prometheus. +3. Choose the scrape interval. 1 minute is recommended, and test the connection. Grafana Cloud will deploy an agent in the background that scrapes the Metrics API and forwards the data to Prometheus. If you prefer to reuse an existing Grafana Agent deployment, configure an [integration pipeline](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/integrations/integration-reference/integration-supabase/) with the same URL and credentials. -## 3. Import the Supabase dashboard +### 4. Import the Supabase dashboard 1. Open your Grafana Cloud dashboard list and click **New → Import**. 2. Paste the raw contents of [`supabase-grafana/dashboard.json`](https://raw.githubusercontent.com/supabase/supabase-grafana/refs/heads/main/grafana/dashboard.json). @@ -51,11 +69,11 @@ This dashboard includes 200+ charts grouped by CPU, IO, connections, replication height="1509" /> -## 4. Configure alerts (optional) +### 5. Configure alerts (optional) The [`docs/example-alerts.md`](https://github.com/supabase/supabase-grafana/blob/main/docs/example-alerts.md) file contains suggested alert rules (disk saturation, long-running queries, replication lag, etc.). Import the alert rules into Grafana Cloud’s Alerting UI or translate them into Grafana Cloud’s managed alert rule format. -## 5. Troubleshooting +### 6. Troubleshooting - Metrics missing? Ensure the Grafana Cloud agent can reach `https://.supabase.co` and that the selected Secret API key is still valid. - 401 errors? Create/rotate a Secret API key in [Project Settings → API Keys](/dashboard/project/_/settings/api-keys) and update the Grafana Cloud credentials. diff --git a/apps/docs/content/guides/telemetry/metrics/grafana-self-hosted.mdx b/apps/docs/content/guides/telemetry/metrics/grafana-self-hosted.mdx index 917ecfbc9e89c..868e095ea6c83 100644 --- a/apps/docs/content/guides/telemetry/metrics/grafana-self-hosted.mdx +++ b/apps/docs/content/guides/telemetry/metrics/grafana-self-hosted.mdx @@ -6,11 +6,11 @@ description: 'Deploy Prometheus and Grafana yourself to monitor Supabase metrics Self-hosting [Prometheus](https://prometheus.io/docs/prometheus/latest/installation/) and Grafana gives you full control over retention, alert routing, and dashboards. The Supabase Metrics API slots into any standard Prometheus scrape job, so you can run everything locally, on a VM, or inside Kubernetes. -<$Partial path="metrics_access.mdx" /> + - +Use this guide only if you need full manual control (custom scrape topology, self-hosted Prometheus or non-standard auth). -Grafana also documents a [Supabase integration reference](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/integrations/integration-reference/integration-supabase/). While it targets Grafana Cloud, the scrape and agent settings apply equally to self-hosted clusters and offer a community-maintained companion to this guide. +Otherwise, use the [Grafana Cloud integration](/docs/guides/telemetry/metrics/grafana-cloud#installation) available in the Supabase Dashboard. @@ -18,7 +18,7 @@ Grafana also documents a [Supabase integration reference](https://grafana.com/do 1. **Prometheus** scrapes `https://.supabase.co/customer/v1/privileged/metrics` every minute using HTTP Basic Auth. 2. **Grafana** reads from Prometheus and renders dashboards/alerts. -3. (Optional) **Alertmanager** or your preferred system sends notifications when Prometheus rules fire. +3. **Prometheus Alertmanager** or your preferred system sends notifications when Prometheus rules fire (optional) . ## 1. Deploy Prometheus From 3effea71aa7f205cdac3cbac034913683d40758d Mon Sep 17 00:00:00 2001 From: shaziya <99940835+shaziyabandukia@users.noreply.github.com> Date: Thu, 23 Jul 2026 06:56:48 -0700 Subject: [PATCH 4/7] feat(www): add Grafana Cloud partner drop blog post (#47716) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 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? Launch blog post - Grafana! ## What is the current behavior? Nonexistent 😆 ## What is the new behavior? Adds the "Observability for every Supabase project with Grafana Cloud" partner drop post, announcing the one-click Grafana Cloud integration. - Post: [`apps/www/_blog/2026-07-23-observability-for-every-supabase-project-with-grafana-cloud.mdx`](https://github.com/supabase/supabase/blob/blog/grafana-cloud-partner-drop/apps/www/_blog/2026-07-23-observability-for-every-supabase-project-with-grafana-cloud.mdx) - Images: `apps/www/public/images/blog/observability-for-every-supabase-project-with-grafana-cloud/` (og + thumb) - Author: `raminder_singh` · Date: 2026-07-23 · Category: product - Includes the YouTube walkthrough embed and UTM-tagged dashboard links ## Summary by CodeRabbit * **New Features** * Added a blog post announcing one-click Grafana Cloud observability integration for Supabase projects. * Highlights include preconfigured authentication, metric scraping, and dashboards available on all plans, including free. * Documents dashboard portability, Metrics API usage, setup instructions, and upcoming log support. --- ...ry-supabase-project-with-grafana-cloud.mdx | 60 ++++++++++++++++++ .../grafana-og.png | Bin 0 -> 313262 bytes .../grafana-thumb.png | Bin 0 -> 236862 bytes 3 files changed, 60 insertions(+) create mode 100644 apps/www/_blog/2026-07-23-observability-for-every-supabase-project-with-grafana-cloud.mdx create mode 100644 apps/www/public/images/blog/observability-for-every-supabase-project-with-grafana-cloud/grafana-og.png create mode 100644 apps/www/public/images/blog/observability-for-every-supabase-project-with-grafana-cloud/grafana-thumb.png diff --git a/apps/www/_blog/2026-07-23-observability-for-every-supabase-project-with-grafana-cloud.mdx b/apps/www/_blog/2026-07-23-observability-for-every-supabase-project-with-grafana-cloud.mdx new file mode 100644 index 0000000000000..1ae2bfa11cc7d --- /dev/null +++ b/apps/www/_blog/2026-07-23-observability-for-every-supabase-project-with-grafana-cloud.mdx @@ -0,0 +1,60 @@ +--- +title: 'Observability for every Supabase project with Grafana Cloud' +description: 'Connect your Supabase project to Grafana Cloud in one click. A pre-built dashboard, alerting, and metrics: available on every plan, including free.' +author: raminder_singh +date: '2026-07-23' +categories: + - product +tags: + - integrations + - observability + - monitoring + - postgres +imgSocial: observability-for-every-supabase-project-with-grafana-cloud/grafana-og.png +imgThumb: observability-for-every-supabase-project-with-grafana-cloud/grafana-thumb.png +toc_depth: 2 +--- + +The [Grafana Cloud integration](https://supabase.com/dashboard/project/_/integrations/grafana-cloud/overview?utm_source=supabase-announcement&utm_medium=blog&utm_campaign=supabase_grafana_cloud_one_click_integration_launch) is available in Supabase. You've been able to connect Supabase to Grafana Cloud for years, and now you can connect to it instantly with the push of a button. + +From your Supabase Dashboard, in a single click, you get a fully-configured Grafana Cloud instance with authentication, metric scraping, and a pre-built observability dashboard already set up on any plan, including free. + +[Grafana Cloud](https://grafana.com/products/cloud/) is the AI-powered open observability cloud built on open source and open standards. Used by developers everywhere, the full-stack observability platform helps you improve the reliability of your applications, resolve incidents quickly, and optimize your telemetry to reduce noise and costs. + +
+