From c1681cfcc2ff37ca5f6c6505ee6abaafcd4c08dd Mon Sep 17 00:00:00 2001 From: Danny White <3104761+dnywh@users.noreply.github.com> Date: Tue, 21 Oct 2025 14:18:56 +1000 Subject: [PATCH 1/9] chore(studio): add feature preview for auth security template UI (#39661) * feature preview logic * sidebar changes based on feature preview * singular naming * minor * Flip isPlatformOnly for new preview --------- Co-authored-by: Joshen Lim --- .../FeaturePreview.constants.tsx | 7 +++ .../FeaturePreview/FeaturePreviewContext.tsx | 16 +++++- .../FeaturePreview/FeaturePreviewModal.tsx | 2 + .../SecurityNotificationsPreview.tsx | 46 ++++++++++++++++++ .../layouts/AuthLayout/AuthLayout.tsx | 6 ++- .../layouts/AuthLayout/AuthLayout.utils.ts | 24 ++++++++- .../security-notifications-preview.png | Bin 0 -> 21423 bytes packages/common/constants/local-storage.ts | 1 + 8 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 apps/studio/components/interfaces/App/FeaturePreview/SecurityNotificationsPreview.tsx create mode 100644 apps/studio/public/img/previews/security-notifications-preview.png diff --git a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreview.constants.tsx b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreview.constants.tsx index 9a56fd87b21e4..03d791ca8914e 100644 --- a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreview.constants.tsx +++ b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreview.constants.tsx @@ -1,6 +1,13 @@ import { LOCAL_STORAGE_KEYS } from 'common' export const FEATURE_PREVIEWS = [ + { + key: LOCAL_STORAGE_KEYS.UI_PREVIEW_SECURITY_NOTIFICATIONS, + name: 'Security notification templates', + discussionsUrl: undefined, + isNew: true, + isPlatformOnly: true, + }, { key: LOCAL_STORAGE_KEYS.UI_PREVIEW_NEW_STORAGE_UI, name: 'New Storage interface', diff --git a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx index 885724aca13c0..bb2b449c4d512 100644 --- a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx +++ b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx @@ -115,6 +115,11 @@ export const useIsNewStorageUIEnabled = () => { return flags[LOCAL_STORAGE_KEYS.UI_PREVIEW_NEW_STORAGE_UI] } +export const useIsSecurityNotificationsEnabled = () => { + const { flags } = useFeaturePreviewContext() + return flags[LOCAL_STORAGE_KEYS.UI_PREVIEW_SECURITY_NOTIFICATIONS] +} + export const useFeaturePreviewModal = () => { const [featurePreviewModal, setFeaturePreviewModal] = useQueryState('featurePreviewModal') @@ -122,6 +127,7 @@ export const useFeaturePreviewModal = () => { const advisorRulesEnabled = useFlag('advisorRules') const isUnifiedLogsPreviewAvailable = useFlag('unifiedLogs') const isNewStorageUIAvailable = useFlag('storageAnalyticsVector') + const isSecurityNotificationsAvailable = useFlag('securityNotifications') const selectedFeatureKeyFromQuery = featurePreviewModal?.trim() ?? null const showFeaturePreviewModal = selectedFeatureKeyFromQuery !== null @@ -138,11 +144,19 @@ export const useFeaturePreviewModal = () => { return isUnifiedLogsPreviewAvailable case 'new-storage-ui': return isNewStorageUIAvailable + case 'security-notifications': + return isSecurityNotificationsAvailable default: return true } }, - [gitlessBranchingEnabled, advisorRulesEnabled, isUnifiedLogsPreviewAvailable] + [ + gitlessBranchingEnabled, + advisorRulesEnabled, + isUnifiedLogsPreviewAvailable, + isNewStorageUIAvailable, + isSecurityNotificationsAvailable, + ] ) const selectedFeatureKey = ( diff --git a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx index ec463be8a6477..3fa262e4b123e 100644 --- a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx +++ b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx @@ -16,6 +16,7 @@ import { useFeaturePreviewContext, useFeaturePreviewModal } from './FeaturePrevi import { InlineEditorPreview } from './InlineEditorPreview' import { NewStorageUIPreview } from './NewStorageUIPreview' import { UnifiedLogsPreview } from './UnifiedLogsPreview' +import { SecurityNotificationsPreview } from './SecurityNotificationsPreview' const FEATURE_PREVIEW_KEY_TO_CONTENT: { [key: string]: ReactNode @@ -27,6 +28,7 @@ const FEATURE_PREVIEW_KEY_TO_CONTENT: { [LOCAL_STORAGE_KEYS.UI_PREVIEW_CLS]: , [LOCAL_STORAGE_KEYS.UI_PREVIEW_UNIFIED_LOGS]: , [LOCAL_STORAGE_KEYS.UI_PREVIEW_NEW_STORAGE_UI]: , + [LOCAL_STORAGE_KEYS.UI_PREVIEW_SECURITY_NOTIFICATIONS]: , } const FeaturePreviewModal = () => { diff --git a/apps/studio/components/interfaces/App/FeaturePreview/SecurityNotificationsPreview.tsx b/apps/studio/components/interfaces/App/FeaturePreview/SecurityNotificationsPreview.tsx new file mode 100644 index 0000000000000..8857881dfa4fb --- /dev/null +++ b/apps/studio/components/interfaces/App/FeaturePreview/SecurityNotificationsPreview.tsx @@ -0,0 +1,46 @@ +import Image from 'next/image' + +import { useParams } from 'common' +import { InlineLink } from 'components/ui/InlineLink' +import { BASE_PATH } from 'lib/constants' +import { useIsSecurityNotificationsEnabled } from './FeaturePreviewContext' + +export const SecurityNotificationsPreview = () => { + const { ref } = useParams() + const isSecurityNotificationsEnabled = useIsSecurityNotificationsEnabled() + + return ( +
+ Security notifications preview +
+

+ Try out our expanded set of{' '} + + email templates + {' '} + with support for security-related notifications. +

+

Enabling this preview will:

+
    +
  • Add a dedicated sidebar section for contact methods like email and SMS
  • +
  • Add new email templates for security-related notifications
  • +
  • Move each (existing and new) template into its own dynamic route
  • +
+

+ These changes are necessary to support incoming security-related notification templates. + Given that the list of our email templates is doubling in size, this change requires some + wider interface changes. Ones that we think make for a clearer experience overall. Win + win! +

+
+
+ ) +} diff --git a/apps/studio/components/layouts/AuthLayout/AuthLayout.tsx b/apps/studio/components/layouts/AuthLayout/AuthLayout.tsx index 70c4cfe7b3dac..5e869db10b5ba 100644 --- a/apps/studio/components/layouts/AuthLayout/AuthLayout.tsx +++ b/apps/studio/components/layouts/AuthLayout/AuthLayout.tsx @@ -1,20 +1,21 @@ import { useRouter } from 'next/router' import { PropsWithChildren } from 'react' -import { useParams } from 'common' +import { useFlag, useParams } from 'common' +import { useIsSecurityNotificationsEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext' import { ProductMenu } from 'components/ui/ProductMenu' import { useAuthConfigPrefetch } from 'data/auth/auth-config-query' import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' import { withAuth } from 'hooks/misc/withAuth' import { ProjectLayout } from '../ProjectLayout/ProjectLayout' import { generateAuthMenu } from './AuthLayout.utils' -import { useFlag } from 'common' const AuthProductMenu = () => { const router = useRouter() const { ref: projectRef = 'default' } = useParams() const authenticationShowOverview = useFlag('authOverviewPage') + const authenticationShowSecurityNotifications = useIsSecurityNotificationsEnabled() const { authenticationSignInProviders, @@ -46,6 +47,7 @@ const AuthProductMenu = () => { authenticationAttackProtection, authenticationAdvanced, authenticationShowOverview, + authenticationShowSecurityNotifications, })} /> ) diff --git a/apps/studio/components/layouts/AuthLayout/AuthLayout.utils.ts b/apps/studio/components/layouts/AuthLayout/AuthLayout.utils.ts index 67bad8717a388..27f0a52e396c5 100644 --- a/apps/studio/components/layouts/AuthLayout/AuthLayout.utils.ts +++ b/apps/studio/components/layouts/AuthLayout/AuthLayout.utils.ts @@ -11,6 +11,7 @@ export const generateAuthMenu = ( authenticationAttackProtection: boolean authenticationAdvanced: boolean authenticationShowOverview: boolean + authenticationShowSecurityNotifications: boolean } ): ProductMenuGroup[] => { const { @@ -21,6 +22,7 @@ export const generateAuthMenu = ( authenticationAttackProtection, authenticationAdvanced, authenticationShowOverview, + authenticationShowSecurityNotifications, } = flags ?? {} return [ @@ -33,6 +35,26 @@ export const generateAuthMenu = ( { name: 'Users', key: 'users', url: `/project/${ref}/auth/users`, items: [] }, ], }, + ...(authenticationEmails && authenticationShowSecurityNotifications && IS_PLATFORM + ? [ + { + title: 'Notifications', + items: [ + ...(authenticationEmails + ? [ + { + name: 'Email', + key: 'email', + pages: ['templates', 'smtp'], + url: `/project/${ref}/auth/templates`, + items: [], + }, + ] + : []), + ], + }, + ] + : []), { title: 'Configuration', items: [ @@ -71,7 +93,7 @@ export const generateAuthMenu = ( }, ] : []), - ...(authenticationEmails + ...(authenticationEmails && !authenticationShowSecurityNotifications ? [ { name: 'Emails', diff --git a/apps/studio/public/img/previews/security-notifications-preview.png b/apps/studio/public/img/previews/security-notifications-preview.png new file mode 100644 index 0000000000000000000000000000000000000000..262ed16f4d95745acaf0004da6e8d864784f846f GIT binary patch literal 21423 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 OVLWevGOs}V5&v%?H!JY~ literal 0 HcmV?d00001 diff --git a/packages/common/constants/local-storage.ts b/packages/common/constants/local-storage.ts index c77786c766194..3784822e32b76 100644 --- a/packages/common/constants/local-storage.ts +++ b/packages/common/constants/local-storage.ts @@ -18,6 +18,7 @@ export const LOCAL_STORAGE_KEYS = { UI_PREVIEW_BRANCHING_2_0: 'supabase-ui-branching-2-0', UI_PREVIEW_ADVISOR_RULES: 'supabase-ui-advisor-rules', UI_PREVIEW_NEW_STORAGE_UI: 'new-storage-ui', + UI_PREVIEW_SECURITY_NOTIFICATIONS: 'security-notifications', NEW_LAYOUT_NOTICE_ACKNOWLEDGED: 'new-layout-notice-acknowledge', TABS_INTERFACE_ACKNOWLEDGED: 'tabs-interface-acknowledge', From 02a232d1f4b6ea7bd69eacddcff44977546d8492 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Tue, 21 Oct 2025 13:28:46 +0800 Subject: [PATCH 2/9] Chore/simplified support form (#39554) * Simplified support form * Nit --- .../Support/CategoryAndSeverityInfo.tsx | 2 +- .../interfaces/Support/Support.constants.ts | 10 ++++- .../interfaces/Support/SupportFormV2.tsx | 45 +++++++++++++++---- 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/apps/studio/components/interfaces/Support/CategoryAndSeverityInfo.tsx b/apps/studio/components/interfaces/Support/CategoryAndSeverityInfo.tsx index 7d612c2d650fb..ae10f3997b09f 100644 --- a/apps/studio/components/interfaces/Support/CategoryAndSeverityInfo.tsx +++ b/apps/studio/components/interfaces/Support/CategoryAndSeverityInfo.tsx @@ -83,7 +83,7 @@ function CategorySelector({ form }: CategorySelectorProps) { - {CATEGORY_OPTIONS.map((option) => ( + {CATEGORY_OPTIONS.filter((option) => !option.hidden).map((option) => ( {option.label} diff --git a/apps/studio/components/interfaces/Support/Support.constants.ts b/apps/studio/components/interfaces/Support/Support.constants.ts index 777d187c6507b..f7ddf76f92005 100644 --- a/apps/studio/components/interfaces/Support/Support.constants.ts +++ b/apps/studio/components/interfaces/Support/Support.constants.ts @@ -3,13 +3,14 @@ import { isFeatureEnabled } from 'common' const billingEnabled = isFeatureEnabled('billing:all') -export type ExtendedSupportCategories = SupportCategories | 'Plan_upgrade' +export type ExtendedSupportCategories = SupportCategories | 'Plan_upgrade' | 'Others' export const CATEGORY_OPTIONS: { value: ExtendedSupportCategories label: string description: string query?: string + hidden?: boolean }[] = [ { value: SupportCategories.PROBLEM, @@ -77,6 +78,13 @@ export const CATEGORY_OPTIONS: { query: undefined, }, ]), + { + value: 'Others' as const, + label: 'Others', + description: 'Issues that are not related to any of the other categories', + query: undefined, + hidden: true, + }, ] export const SEVERITY_OPTIONS = [ diff --git a/apps/studio/components/interfaces/Support/SupportFormV2.tsx b/apps/studio/components/interfaces/Support/SupportFormV2.tsx index e431ddd52115c..04b62436b1409 100644 --- a/apps/studio/components/interfaces/Support/SupportFormV2.tsx +++ b/apps/studio/components/interfaces/Support/SupportFormV2.tsx @@ -1,8 +1,9 @@ -import { type Dispatch, type MouseEventHandler } from 'react' +import { useEffect, type Dispatch, type MouseEventHandler } from 'react' import type { SubmitHandler, UseFormReturn } from 'react-hook-form' // End of third-party imports import { SupportCategories } from '@supabase/shared-types/out/constants' +import { useFlag } from 'common' import { CLIENT_LIBRARIES } from 'common/constants' import { getProjectAuthConfig } from 'data/auth/auth-config-query' import { useSendSupportTicketMutation } from 'data/feedback/support-ticket-send' @@ -33,6 +34,15 @@ import { NO_PROJECT_MARKER, } from './SupportForm.utils' +const useIsSimplifiedForm = (slug: string) => { + const simplifiedSupportForm = useFlag('simplifiedSupportForm') + if (typeof simplifiedSupportForm === 'string') { + const slugs = (simplifiedSupportForm as string).split(',').map((x) => x.trim()) + return slugs.includes(slug) + } + return false +} + interface SupportFormV2Props { form: UseFormReturn initialError: string | null @@ -45,6 +55,7 @@ export const SupportFormV2 = ({ form, initialError, state, dispatch }: SupportFo const respondToEmail = profile?.primary_email ?? 'your email' const { organizationSlug, projectRef, category, severity, subject, library } = form.watch() + const simplifiedSupportForm = useIsSimplifiedForm(organizationSlug) const selectedOrgSlug = organizationSlug === NO_ORG_MARKER ? null : organizationSlug const selectedProjectRef = projectRef === NO_PROJECT_MARKER ? null : projectRef @@ -85,6 +96,7 @@ export const SupportFormV2 = ({ form, initialError, state, dispatch }: SupportFo const payload = { ...values, + category, organizationSlug: values.organizationSlug ?? NO_ORG_MARKER, projectRef: values.projectRef ?? NO_PROJECT_MARKER, allowSupportAccess: SUPPORT_ACCESS_CATEGORIES.includes(values.category) @@ -134,6 +146,15 @@ export const SupportFormV2 = ({ form, initialError, state, dispatch }: SupportFo handleFormSubmit(event) } + useEffect(() => { + if (simplifiedSupportForm) { + form.setValue('category', 'Others') + } else { + form.setValue('category', '' as any) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [simplifiedSupportForm]) + return (
@@ -148,20 +169,26 @@ export const SupportFormV2 = ({ form, initialError, state, dispatch }: SupportFo subscriptionPlanId={subscriptionPlanId} category={category} /> - + {!simplifiedSupportForm && ( + + )}
- - + {!simplifiedSupportForm && ( + <> + + + + )}
From 011ded418cd17987b24841dfd072b8452a0e5a0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kevin=20Gr=C3=BCneberg?= Date: Tue, 21 Oct 2025 14:18:21 +0800 Subject: [PATCH 3/9] docs: TP MAU and Database size quotas (#39699) Quotas have been relaxed a long time ago, missed this in the docs --- apps/docs/content/guides/platform/billing-on-supabase.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/docs/content/guides/platform/billing-on-supabase.mdx b/apps/docs/content/guides/platform/billing-on-supabase.mdx index 54293ddd95284..868a0f87059d2 100644 --- a/apps/docs/content/guides/platform/billing-on-supabase.mdx +++ b/apps/docs/content/guides/platform/billing-on-supabase.mdx @@ -64,9 +64,9 @@ The quota is applied to your entire organization, independent of how many projec | Usage Item | Free | Pro/Team | Enterprise | | -------------------------------- | ------------------------ | ------------------------------------------------------------------- | ---------- | | Egress | 5 GB | 250 GB included, then per GB | Custom | -| Database Size | 500 MB | 8 GB disk per project included, then per GB | Custom | +| Database Size | 500 MB per project | 8 GB disk per project included, then per GB | Custom | | Monthly Active Users | 50,000 MAU | 100,000 MAU included, then per MAU | Custom | -| Monthly Active Third-Party Users | 50 MAU | 50 MAU included, then per MAU | Custom | +| Monthly Active Third-Party Users | 50,000 MAU | 100,000 MAU included, then per MAU | Custom | | Monthly Active SSO Users | Unavailable on Free Plan | 50 MAU included, then per MAU | Custom | | Storage Size | 1 GB | 100 GB included, then per GB | Custom | | Storage Images Transformed | Unavailable on Free Plan | 100 included, then per 1000 | Custom | From 13654076feeb1fb15217e3ff6e80bddac325521b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kevin=20Gr=C3=BCneberg?= Date: Tue, 21 Oct 2025 14:18:29 +0800 Subject: [PATCH 4/9] docs: edge function preflight usage docs (#39700) * docs: edge function preflight usage docs * Update edge-function-invocations.mdx --- .../platform/manage-your-usage/edge-function-invocations.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/content/guides/platform/manage-your-usage/edge-function-invocations.mdx b/apps/docs/content/guides/platform/manage-your-usage/edge-function-invocations.mdx index db31cb0d45693..9109b2d3261a0 100644 --- a/apps/docs/content/guides/platform/manage-your-usage/edge-function-invocations.mdx +++ b/apps/docs/content/guides/platform/manage-your-usage/edge-function-invocations.mdx @@ -5,7 +5,7 @@ title: 'Manage Edge Function Invocations usage' ## What you are charged for -You are charged for the number of times your functions get invoked, regardless of the response status code. +You are charged for the number of times your functions get invoked, regardless of the response status code. Preflight (OPTIONS) requests are not billed. ## How charges are calculated From a8dbcfe7893701d8311491a39ad67e18c3f45be5 Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Tue, 21 Oct 2025 09:14:51 +0200 Subject: [PATCH 5/9] chore: update self-hosted image versions (#39468) * chore: update image versions for docker/docker-compose.yml * chore: update image versions for docker/docker-compose.yml * Update docker-compose.yml --------- Co-authored-by: Han Qiao --- docker/docker-compose.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 0f470f2fa30e0..536ea8860d5bc 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -11,7 +11,7 @@ services: studio: container_name: supabase-studio - image: supabase/studio:2025.10.01-sha-8460121 + image: supabase/studio:2025.10.20-sha-5005fc6 restart: unless-stopped healthcheck: test: @@ -190,7 +190,7 @@ services: realtime: # This container name looks inconsistent but is correct because realtime constructs tenant id by parsing the subdomain container_name: realtime-dev.supabase-realtime - image: supabase/realtime:v2.51.11 + image: supabase/realtime:v2.56.0 restart: unless-stopped depends_on: db: @@ -235,7 +235,7 @@ services: # To use S3 backed storage: docker compose -f docker-compose.yml -f docker-compose.s3.yml up storage: container_name: supabase-storage - image: supabase/storage-api:v1.28.0 + image: supabase/storage-api:v1.28.1 restart: unless-stopped volumes: - ./volumes/storage:/var/lib/storage:z @@ -300,7 +300,7 @@ services: meta: container_name: supabase-meta - image: supabase/postgres-meta:v0.91.6 + image: supabase/postgres-meta:v0.93.0 restart: unless-stopped depends_on: db: @@ -319,7 +319,7 @@ services: functions: container_name: supabase-edge-functions - image: supabase/edge-runtime:v1.69.6 + image: supabase/edge-runtime:v1.69.14 restart: unless-stopped volumes: - ./volumes/functions:/home/deno/functions:Z @@ -480,7 +480,7 @@ services: # Update the DATABASE_URL if you are using an external Postgres database supavisor: container_name: supabase-pooler - image: supabase/supavisor:2.7.0 + image: supabase/supavisor:2.7.3 restart: unless-stopped ports: - ${POSTGRES_PORT}:5432 From be9ba28be058a1a823a3784e48dadd061db184c6 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Tue, 21 Oct 2025 16:43:53 +0800 Subject: [PATCH 6/9] Silence text model error in diff editor (#39703) --- apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx b/apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx index 52f5444c228a4..58b820518666c 100644 --- a/apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx +++ b/apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx @@ -705,6 +705,11 @@ export const SQLEditor = () => { padding: { top: 4 }, lineNumbersMinChars: 3, }} + // [Joshen] These ones are meant to solve a UI issue that seems to only be happening locally + // Happens when you use the inline assistant in the SQL Editor and accept the suggestion + // Error: TextModel got disposed before DiffEditorWidget model got reset + keepCurrentModifiedModel={true} + keepCurrentOriginalModel={true} /> {showWidget && ( Date: Tue, 21 Oct 2025 16:45:59 +0800 Subject: [PATCH 7/9] fix: cron logs query to handle type cast (#39704) --- .../interfaces/Settings/Logs/Logs.utils.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts b/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts index b08cb3ce1f13a..b0572f6c69ce0 100644 --- a/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts +++ b/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts @@ -228,8 +228,7 @@ limit ${limit} return `select id, postgres_logs.timestamp, event_message, parsed.error_severity, parsed.query from postgres_logs - cross join unnest(metadata) as m - cross join unnest(m.parsed) as parsed +${joins} ${pgCronWhere} ${orderBy} limit ${limit} @@ -293,7 +292,13 @@ export const maybeShowUpgradePrompt = (from: string | null | undefined, planId?: } export const genCountQuery = (table: LogsTableName, filters: Filters): string => { - const where = genWhereStatement(table, filters) + let where = genWhereStatement(table, filters) + // pg_cron logs are a subset of postgres logs + // to calculate the chart, we need to query postgres logs + if (table === LogsTableName.PG_CRON) { + table = LogsTableName.POSTGRES + where = basePgCronWhere + } const joins = genCrossJoinUnnests(table) return `SELECT count(*) as count FROM ${table} ${joins} ${where}` } @@ -320,7 +325,7 @@ const calcChartStart = (params: Partial): [Dayjs, string] => return [its.add(-extendValue, trunc), trunc] } -const basePgCronWhere = `where ( parsed.application_name = 'pg_cron' or regexp_contains(event_message, 'cron job') )` +const basePgCronWhere = `where ( parsed.application_name = 'pg_cron' or event_message::text LIKE '%cron job%' )` /** * * generates log event chart query From bda94ac18735ceb37a033835fe9d6d4be5f3828b Mon Sep 17 00:00:00 2001 From: Han Qiao Date: Tue, 21 Oct 2025 17:16:10 +0800 Subject: [PATCH 8/9] fix: use separate query clause for self hosted (#39711) --- .../studio/components/interfaces/Settings/Logs/Logs.utils.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts b/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts index b0572f6c69ce0..279a148a57732 100644 --- a/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts +++ b/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts @@ -325,7 +325,10 @@ const calcChartStart = (params: Partial): [Dayjs, string] => return [its.add(-extendValue, trunc), trunc] } -const basePgCronWhere = `where ( parsed.application_name = 'pg_cron' or event_message::text LIKE '%cron job%' )` +// TODO(qiao): workaround for self-hosted cron logs error until logflare is fixed +const basePgCronWhere = IS_PLATFORM + ? `where ( parsed.application_name = 'pg_cron' or regexp_contains(event_message, 'cron job') )` + : `where ( parsed.application_name = 'pg_cron' or event_message::text LIKE '%cron job%' )` /** * * generates log event chart query From 401d6fe892f6341b44479c0f9d1ca4554de33c14 Mon Sep 17 00:00:00 2001 From: Stojan Dimitrovski Date: Tue, 21 Oct 2025 11:40:33 +0200 Subject: [PATCH 9/9] feat: add navigator lock debugging to track down some rare issue (#39688) * feat: add navigator lock debugging to track down some rare issue * send exception to sentry --- apps/docs/lib/userAuth.ts | 7 ++++- apps/studio/lib/gotrue.ts | 7 ++++- packages/common/gotrue.ts | 62 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/apps/docs/lib/userAuth.ts b/apps/docs/lib/userAuth.ts index 12fec885fe721..973c5eb8bb177 100644 --- a/apps/docs/lib/userAuth.ts +++ b/apps/docs/lib/userAuth.ts @@ -1,6 +1,11 @@ -import { gotrueClient } from 'common' +import * as Sentry from '@sentry/nextjs' +import { gotrueClient, setCaptureException } from 'common' import { useEffect } from 'react' +setCaptureException((e: any) => { + Sentry.captureException(e) +}) + export const auth = gotrueClient export async function getAccessToken() { diff --git a/apps/studio/lib/gotrue.ts b/apps/studio/lib/gotrue.ts index b51fdd29e7111..ab350710682dd 100644 --- a/apps/studio/lib/gotrue.ts +++ b/apps/studio/lib/gotrue.ts @@ -1,6 +1,11 @@ +import * as Sentry from '@sentry/nextjs' import type { JwtPayload } from '@supabase/supabase-js' import { getAccessToken, type User } from 'common/auth' -import { gotrueClient } from 'common/gotrue' +import { gotrueClient, setCaptureException } from 'common/gotrue' + +setCaptureException((e: any) => { + Sentry.captureException(e) +}) export const auth = gotrueClient export { getAccessToken } diff --git a/packages/common/gotrue.ts b/packages/common/gotrue.ts index 0456a464e56a2..6ab6fb2ec72b7 100644 --- a/packages/common/gotrue.ts +++ b/packages/common/gotrue.ts @@ -115,12 +115,72 @@ const logIndexedDB = (message: string, ...args: any[]) => { })() } +let captureException: ((e: any) => any) | null = null + +export function setCaptureException(fn: typeof captureException) { + captureException = fn +} + +async function debuggableNavigatorLock( + name: string, + acquireTimeout: number, + fn: () => Promise +): Promise { + let stackException: any + + try { + throw new Error('Lock is being held for over 2s here') + } catch (e: any) { + stackException = e + } + + const debugTimeout = setTimeout(() => { + ;(async () => { + const bc = new BroadcastChannel('who-is-holding-the-lock') + try { + bc.postMessage({}) + } finally { + bc.close() + } + + console.error( + `Waited for over 2s to acquire an Auth client lock`, + await navigator.locks.query(), + stackException + ) + })() + }, 2000) + + try { + return await navigatorLock(name, acquireTimeout, async () => { + clearTimeout(debugTimeout) + + const bc = new BroadcastChannel('who-is-holding-the-lock') + bc.addEventListener('message', () => { + console.error('Lock is held here', stackException) + + if (captureException) { + captureException(stackException) + } + }) + + try { + return await fn() + } finally { + bc.close() + } + }) + } finally { + clearTimeout(debugTimeout) + } +} + export const gotrueClient = new AuthClient({ url: process.env.NEXT_PUBLIC_GOTRUE_URL, storageKey: STORAGE_KEY, detectSessionInUrl: shouldDetectSessionInUrl, debug: debug ? (persistedDebug ? logIndexedDB : true) : false, - lock: navigatorLockEnabled ? navigatorLock : undefined, + lock: navigatorLockEnabled ? debuggableNavigatorLock : undefined, ...('localStorage' in globalThis ? { storage: globalThis.localStorage, userStorage: globalThis.localStorage }