Skip to content
4 changes: 2 additions & 2 deletions apps/docs/content/guides/platform/billing-on-supabase.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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 <Price price="0.09" /> per GB | Custom |
| Database Size | 500 MB | 8 GB disk per project included, then <Price price="0.125" /> per GB | Custom |
| Database Size | 500 MB per project | 8 GB disk per project included, then <Price price="0.125" /> per GB | Custom |
| Monthly Active Users | 50,000 MAU | 100,000 MAU included, then <Price price="0.00325" /> per MAU | Custom |
| Monthly Active Third-Party Users | 50 MAU | 50 MAU included, then <Price price="0.00325" /> per MAU | Custom |
| Monthly Active Third-Party Users | 50,000 MAU | 100,000 MAU included, then <Price price="0.00325" /> per MAU | Custom |
| Monthly Active SSO Users | Unavailable on Free Plan | 50 MAU included, then <Price price="0.015" /> per MAU | Custom |
| Storage Size | 1 GB | 100 GB included, then <Price price="0.021" /> per GB | Custom |
| Storage Images Transformed | Unavailable on Free Plan | 100 included, then <Price price="5" /> per 1000 | Custom |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
7 changes: 6 additions & 1 deletion apps/docs/lib/userAuth.ts
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down
Original file line number Diff line number Diff line change
@@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,19 @@ 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')

const gitlessBranchingEnabled = useFlag('gitlessBranching')
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
Expand All @@ -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 = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -27,6 +28,7 @@ const FEATURE_PREVIEW_KEY_TO_CONTENT: {
[LOCAL_STORAGE_KEYS.UI_PREVIEW_CLS]: <CLSPreview />,
[LOCAL_STORAGE_KEYS.UI_PREVIEW_UNIFIED_LOGS]: <UnifiedLogsPreview />,
[LOCAL_STORAGE_KEYS.UI_PREVIEW_NEW_STORAGE_UI]: <NewStorageUIPreview />,
[LOCAL_STORAGE_KEYS.UI_PREVIEW_SECURITY_NOTIFICATIONS]: <SecurityNotificationsPreview />,
}

const FeaturePreviewModal = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -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 (
<div className="text-sm text-foreground-light">
<Image
alt="Security notifications preview"
src={`${BASE_PATH}/img/previews/security-notifications-preview.png`}
width={1296}
height={900}
className="rounded border mb-4"
/>
<div className="space-y-2 !mt-4">
<p className=" mb-4">
Try out our expanded set of{' '}
<InlineLink
href={`/project/${ref ?? '_'}/auth/${isSecurityNotificationsEnabled ? 'email' : 'templates'}`}
>
email templates
</InlineLink>{' '}
with support for security-related notifications.
</p>
<p className="text-foreground">Enabling this preview will:</p>
<ul className="list-disc pl-6 space-y-1">
<li>Add a dedicated sidebar section for contact methods like email and SMS</li>
<li>Add new email templates for security-related notifications</li>
<li>Move each (existing and new) template into its own dynamic route</li>
</ul>
<p>
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!
</p>
</div>
</div>
)
}
5 changes: 5 additions & 0 deletions apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 && (
<ResizableAIWidget
Expand Down
16 changes: 12 additions & 4 deletions apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -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}`
}
Expand All @@ -320,7 +325,10 @@ const calcChartStart = (params: Partial<LogsEndpointParams>): [Dayjs, string] =>
return [its.add(-extendValue, trunc), trunc]
}

const basePgCronWhere = `where ( parsed.application_name = 'pg_cron' or regexp_contains(event_message, '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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ function CategorySelector({ form }: CategorySelectorProps) {
</SelectTrigger_Shadcn_>
<SelectContent_Shadcn_>
<SelectGroup_Shadcn_>
{CATEGORY_OPTIONS.map((option) => (
{CATEGORY_OPTIONS.filter((option) => !option.hidden).map((option) => (
<SelectItem_Shadcn_ key={option.value} value={option.value}>
{option.label}
<span className="block text-xs text-foreground-lighter">
Expand Down
10 changes: 9 additions & 1 deletion apps/studio/components/interfaces/Support/Support.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 = [
Expand Down
45 changes: 36 additions & 9 deletions apps/studio/components/interfaces/Support/SupportFormV2.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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<SupportFormValues>
initialError: string | null
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 (
<Form_Shadcn_ {...form}>
<form id="support-form" className="flex flex-col gap-y-6">
Expand All @@ -148,20 +169,26 @@ export const SupportFormV2 = ({ form, initialError, state, dispatch }: SupportFo
subscriptionPlanId={subscriptionPlanId}
category={category}
/>
<CategoryAndSeverityInfo
form={form}
category={category}
severity={severity}
projectRef={projectRef}
/>
{!simplifiedSupportForm && (
<CategoryAndSeverityInfo
form={form}
category={category}
severity={severity}
projectRef={projectRef}
/>
)}
</div>

<DialogSectionSeparator />

<div className="px-6 flex flex-col gap-y-8">
<SubjectAndSuggestionsInfo form={form} subject={subject} category={category} />
<ClientLibraryInfo form={form} library={library} category={category} />
<AffectedServicesSelector form={form} category={category} />
{!simplifiedSupportForm && (
<>
<ClientLibraryInfo form={form} library={library} category={category} />
<AffectedServicesSelector form={form} category={category} />
</>
)}
<MessageField form={form} originalError={initialError} />
<AttachmentUploadDisplay {...attachmentUpload} />
</div>
Expand Down
6 changes: 4 additions & 2 deletions apps/studio/components/layouts/AuthLayout/AuthLayout.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -46,6 +47,7 @@ const AuthProductMenu = () => {
authenticationAttackProtection,
authenticationAdvanced,
authenticationShowOverview,
authenticationShowSecurityNotifications,
})}
/>
)
Expand Down
Loading
Loading