Skip to content

Commit 93ef20c

Browse files
authored
fix: cannot disable realtime (supabase#45640)
## Problem Clicking button does nothing. toggling back to enable shows error related to pool size ## Solution Fix the form validation schema ## How to test - Go to Realtime Settings `/realtime/settings` - Try disabling it <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Two-step save: clicking Save opens a confirmation before changes are applied. * Warning displayed when database pool size exceeds 50% of max connections (max shown dynamically). * **Improvements** * Form adapts to suspended vs. active realtime states with fewer required fields when suspended. * Better field labeling and accessibility; form resets and consistent feedback after successful updates. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent e6f819f commit 93ef20c

1 file changed

Lines changed: 86 additions & 32 deletions

File tree

apps/studio/components/interfaces/Realtime/RealtimeSettings.tsx

Lines changed: 86 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -85,23 +85,43 @@ export const RealtimeSettings = () => {
8585
},
8686
})
8787

88-
const FormSchema = z.object({
89-
connection_pool: z.coerce
90-
.number()
91-
.min(1)
92-
.max(maxConn?.maxConnections ?? 100),
93-
max_concurrent_users: z.coerce.number().min(1).max(50000),
94-
max_events_per_second: z.coerce.number().min(1).max(10000),
95-
max_presence_events_per_second: z.coerce.number().min(1).max(10000),
96-
max_payload_size_in_kb: z.coerce.number().min(1).max(3000),
97-
suspend: z.boolean(),
98-
// [Joshen] These fields are temporarily hidden from the UI
99-
// max_bytes_per_second: z.coerce.number().min(1).max(10000000),
100-
// max_channels_per_client: z.coerce.number().min(1).max(10000),
101-
// max_joins_per_second: z.coerce.number().min(1).max(5000),
88+
const FormSchema = z.discriminatedUnion('suspend', [
89+
z.object({
90+
suspend: z.literal(true),
91+
connection_pool: z.coerce
92+
.number()
93+
.min(1)
94+
.max(maxConn?.maxConnections ?? 100)
95+
.optional(),
96+
max_concurrent_users: z.coerce.number().min(1).max(50000).optional(),
97+
max_events_per_second: z.coerce.number().min(1).max(10000).optional(),
98+
max_presence_events_per_second: z.coerce.number().min(1).max(10000).optional(),
99+
max_payload_size_in_kb: z.coerce.number().min(1).max(3000).optional(),
100+
// [Joshen] These fields are temporarily hidden from the UI
101+
// max_bytes_per_second: z.coerce.number().min(1).max(10000000).optional(),
102+
// max_channels_per_client: z.coerce.number().min(1).max(10000).optional(),
103+
// max_joins_per_second: z.coerce.number().min(1).max(5000).optional(),
102104

103-
allow_public: z.boolean(),
104-
})
105+
allow_public: z.boolean().optional(),
106+
}),
107+
z.object({
108+
suspend: z.literal(false),
109+
connection_pool: z.coerce
110+
.number()
111+
.min(1)
112+
.max(maxConn?.maxConnections ?? 100),
113+
max_concurrent_users: z.coerce.number().min(1).max(50000),
114+
max_events_per_second: z.coerce.number().min(1).max(10000),
115+
max_presence_events_per_second: z.coerce.number().min(1).max(10000),
116+
max_payload_size_in_kb: z.coerce.number().min(1).max(3000),
117+
// [Joshen] These fields are temporarily hidden from the UI
118+
// max_bytes_per_second: z.coerce.number().min(1).max(10000000),
119+
// max_channels_per_client: z.coerce.number().min(1).max(10000),
120+
// max_joins_per_second: z.coerce.number().min(1).max(5000),
121+
122+
allow_public: z.boolean(),
123+
}),
124+
])
105125

106126
const form = useForm<z.infer<typeof FormSchema>>({
107127
resolver: zodResolver(FormSchema),
@@ -127,19 +147,37 @@ export const RealtimeSettings = () => {
127147

128148
const onConfirmSave = () => {
129149
if (!projectRef) return console.error('Project ref is required')
130-
const data = form.getValues()
150+
const values = form.getValues()
131151

132152
// [Joshen] Casting to `Number` here as the values are being set as string when edited in the form
133153
// and returned in form.getValues() - I might be missing some easy util function from RHF though
134154
updateRealtimeConfig({
135155
ref: projectRef,
136-
private_only: !data.allow_public,
137-
connection_pool: Number(data.connection_pool),
138-
max_concurrent_users: Number(data.max_concurrent_users),
139-
max_events_per_second: Number(data.max_events_per_second),
140-
max_presence_events_per_second: Number(data.max_presence_events_per_second),
141-
max_payload_size_in_kb: Number(data.max_payload_size_in_kb),
142-
suspend: data.suspend,
156+
private_only: !values.allow_public,
157+
connection_pool: Number(
158+
values.connection_pool ?? data?.connection_pool ?? REALTIME_DEFAULT_CONFIG.connection_pool
159+
),
160+
max_concurrent_users: Number(
161+
values.max_concurrent_users ??
162+
data?.max_concurrent_users ??
163+
REALTIME_DEFAULT_CONFIG.max_concurrent_users
164+
),
165+
max_events_per_second: Number(
166+
values.max_events_per_second ??
167+
data?.max_events_per_second ??
168+
REALTIME_DEFAULT_CONFIG.max_events_per_second
169+
),
170+
max_presence_events_per_second: Number(
171+
values.max_presence_events_per_second ??
172+
data?.max_presence_events_per_second ??
173+
REALTIME_DEFAULT_CONFIG.max_presence_events_per_second
174+
),
175+
max_payload_size_in_kb: Number(
176+
values.max_payload_size_in_kb ??
177+
data?.max_payload_size_in_kb ??
178+
REALTIME_DEFAULT_CONFIG.max_payload_size_in_kb
179+
),
180+
suspend: values.suspend,
143181
})
144182
}
145183

@@ -158,12 +196,14 @@ export const RealtimeSettings = () => {
158196
render={({ field }) => (
159197
<>
160198
<FormItemLayout
199+
id="suspend"
161200
layout="flex-row-reverse"
162201
label="Enable Realtime service"
163202
description="If disabled, no clients will be able to connect and new connections will be rejected"
164203
>
165204
<FormControl>
166205
<Switch
206+
id="suspend"
167207
checked={!field.value}
168208
onCheckedChange={(checked) => field.onChange(!checked)}
169209
disabled={!canUpdateConfig}
@@ -214,12 +254,14 @@ export const RealtimeSettings = () => {
214254
render={({ field }) => (
215255
<>
216256
<FormItemLayout
257+
id="allow_public"
217258
layout="flex-row-reverse"
218259
label="Allow public access to channels"
219260
description="If disabled, only private channels will be allowed"
220261
>
221262
<FormControl>
222263
<Switch
264+
id="allow_public"
223265
checked={field.value}
224266
onCheckedChange={field.onChange}
225267
disabled={!canUpdateConfig}
@@ -266,6 +308,7 @@ export const RealtimeSettings = () => {
266308
render={({ field }) => (
267309
<>
268310
<FormItemLayout
311+
id="connection_pool"
269312
layout="flex-row-reverse"
270313
label="Database connection pool size"
271314
description="Realtime Authorization uses this database pool to check client access"
@@ -274,6 +317,7 @@ export const RealtimeSettings = () => {
274317
<InputGroup>
275318
<FormInputGroupInput
276319
{...field}
320+
id="connection_pool"
277321
type="number"
278322
disabled={!canUpdateConfig}
279323
value={field.value || ''}
@@ -284,14 +328,16 @@ export const RealtimeSettings = () => {
284328
</InputGroup>
285329
</FormControl>
286330
</FormItemLayout>
287-
{!!maxConn && field.value > maxConn.maxConnections * 0.5 && (
288-
<Admonition
289-
showIcon={false}
290-
type="warning"
291-
title={`Pool size is greater than 50% of the max connections (${maxConn.maxConnections}) on your database`}
292-
description="This may result in instability and unreliability with your database connections."
293-
/>
294-
)}
331+
{!!maxConn &&
332+
field.value &&
333+
field.value > maxConn.maxConnections * 0.5 && (
334+
<Admonition
335+
showIcon={false}
336+
type="warning"
337+
title={`Pool size is greater than 50% of the max connections (${maxConn.maxConnections}) on your database`}
338+
description="This may result in instability and unreliability with your database connections."
339+
/>
340+
)}
295341
</>
296342
)}
297343
/>
@@ -302,6 +348,7 @@ export const RealtimeSettings = () => {
302348
name="max_concurrent_users"
303349
render={({ field }) => (
304350
<FormItemLayout
351+
id="max_concurrent_users"
305352
layout="flex-row-reverse"
306353
label="Max concurrent clients"
307354
description="Sets maximum number of concurrent clients that can connect to your Realtime service"
@@ -310,6 +357,7 @@ export const RealtimeSettings = () => {
310357
<InputGroup>
311358
<FormInputGroupInput
312359
{...field}
360+
id="max_concurrent_users"
313361
type="number"
314362
disabled={!canUpdateConfig}
315363
value={field.value || ''}
@@ -329,6 +377,7 @@ export const RealtimeSettings = () => {
329377
name="max_events_per_second"
330378
render={({ field }) => (
331379
<FormItemLayout
380+
id="max_events_per_second"
332381
layout="flex-row-reverse"
333382
label="Max events per second"
334383
description="Sets maximum number of events per second that can be sent to your Realtime service"
@@ -337,6 +386,7 @@ export const RealtimeSettings = () => {
337386
<InputGroup>
338387
<FormInputGroupInput
339388
{...field}
389+
id="max_events_per_second"
340390
type="number"
341391
disabled={!isUsageBillingEnabled || !canUpdateConfig}
342392
value={field.value || ''}
@@ -383,6 +433,7 @@ export const RealtimeSettings = () => {
383433
name="max_presence_events_per_second"
384434
render={({ field }) => (
385435
<FormItemLayout
436+
id="max_presence_events_per_second"
386437
layout="flex-row-reverse"
387438
label="Max presence events per second"
388439
description="Sets maximum number of presence events per second that can be sent to your Realtime service"
@@ -391,6 +442,7 @@ export const RealtimeSettings = () => {
391442
<InputGroup>
392443
<FormInputGroupInput
393444
{...field}
445+
id="max_presence_events_per_second"
394446
type="number"
395447
disabled={!isUsageBillingEnabled || !canUpdateConfig}
396448
value={field.value || ''}
@@ -437,6 +489,7 @@ export const RealtimeSettings = () => {
437489
name="max_payload_size_in_kb"
438490
render={({ field }) => (
439491
<FormItemLayout
492+
id="max_payload_size_in_kb"
440493
layout="flex-row-reverse"
441494
label="Max payload size in KB"
442495
description="Sets maximum number of payload size in KB that can be sent to your Realtime service"
@@ -445,6 +498,7 @@ export const RealtimeSettings = () => {
445498
<InputGroup>
446499
<FormInputGroupInput
447500
{...field}
501+
id="max_payload_size_in_kb"
448502
type="number"
449503
disabled={!isUsageBillingEnabled || !canUpdateConfig}
450504
value={field.value || ''}

0 commit comments

Comments
 (0)