import { zodResolver } from '@hookform/resolvers/zod' import { PermissionAction } from '@supabase/shared-types/out/constants' import { useEffect } from 'react' import { SubmitHandler, useForm } from 'react-hook-form' import { toast } from 'sonner' import * as z from 'zod' import { useParams } from 'common' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import { ScaffoldSection } from 'components/layouts/Scaffold' import AlertError from 'components/ui/AlertError' import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui/Forms/FormSection' import { InlineLink } from 'components/ui/InlineLink' import { useMaxConnectionsQuery } from 'data/database/max-connections-query' import { useRealtimeConfigurationUpdateMutation } from 'data/realtime/realtime-config-mutation' import { REALTIME_DEFAULT_CONFIG, useRealtimeConfigurationQuery, } from 'data/realtime/realtime-config-query' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' import { Button, Card, CardContent, CardFooter, Form_Shadcn_, FormControl_Shadcn_, FormField_Shadcn_, FormMessage_Shadcn_, Input_Shadcn_, Switch, } from 'ui' import { Admonition } from 'ui-patterns' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' const formId = 'realtime-configuration-form' export const RealtimeSettings = () => { const { ref: projectRef } = useParams() const { project } = useProjectContext() const organization = useSelectedOrganization() const canUpdateConfig = useCheckPermissions(PermissionAction.REALTIME_ADMIN_READ, '*') const { data: maxConn } = useMaxConnectionsQuery({ projectRef: project?.ref, connectionString: project?.connectionString, }) const { data, error, isLoading, isSuccess, isError } = useRealtimeConfigurationQuery({ projectRef, }) const isUsageBillingEnabled = organization?.usage_billing_enabled const { mutate: updateRealtimeConfig, isLoading: isUpdatingConfig } = useRealtimeConfigurationUpdateMutation({ onSuccess: () => { form.reset(form.getValues()) toast.success('Successfully updated realtime settings') }, }) const FormSchema = z.object({ connection_pool: z.coerce .number() .min(1) .max(maxConn?.maxConnections ?? 100), max_concurrent_users: z.coerce.number().min(1).max(50000), // [Joshen] These fields are temporarily hidden from the UI // max_events_per_second: z.coerce.number().min(1).max(50000), // max_bytes_per_second: z.coerce.number().min(1).max(10000000), // max_channels_per_client: z.coerce.number().min(1).max(10000), // max_joins_per_second: z.coerce.number().min(1).max(5000), allow_public: z.boolean(), }) const form = useForm>({ resolver: zodResolver(FormSchema), defaultValues: { ...REALTIME_DEFAULT_CONFIG, allow_public: !REALTIME_DEFAULT_CONFIG.private_only, }, }) const onSubmit: SubmitHandler> = (data) => { if (!projectRef) return console.error('Project ref is required') updateRealtimeConfig({ ref: projectRef, private_only: !data.allow_public, connection_pool: data.connection_pool, max_concurrent_users: data.max_concurrent_users, }) } useEffect(() => { // [Joshen] Temp typed with any - API typing marks all the properties as nullable, // but checked with Filipe that they're not supposed to if (isSuccess) { form.reset({ ...data, allow_public: !data.private_only } as any) } }, [isSuccess]) return (
{isError ? ( ) : ( ( Channel restrictions} > )} /> ( Realtime Authorization uses this database pool to check client access

} > Database connection pool size } > {!!maxConn && field.value > maxConn.maxConnections * 0.5 && ( )}
)} />
( Sets maximum number of concurrent clients that can connect to your Realtime service

} > Max concurrent clients } > {!isUsageBillingEnabled && ( You may adjust this setting in the{' '} organization billing settings } /> )}
)} />
{/* [Joshen] The following fields are hidden from the UI temporarily while we figure out what settings to expose to the users - Max events per second - Max bytes per second - Max channels per client - Max joins per second */} {/* ( Sets maximum number of events per second rate per channel limit

} > Max events per second } >
)} />
*/} {/* { const { value, unit } = convertFromBytes(field.value ?? 0) return ( Sets maximum number of bytes per second rate per channel limit

} > Max bytes per second } > {!!field.value ? ( This is equivalent to {value.toFixed(2)} {unit} ) : null}
) }} />
*/} {/* ( Sets maximum number of channels per client rate limit

} > Max channels per client } >
)} />
*/} {/* ( Sets maximum number of joins per second rate limit

} > Max joins per second } >
)} />
*/}
{!canUpdateConfig && (

You need additional permissions to update realtime settings

)}
{form.formState.isDirty && ( )}
)}
) }