mirror of
https://github.com/supabase/supabase.git
synced 2026-05-27 06:03:44 +08:00
* init PrePostTab component * init * support auto fixing rounded of inputs/select/textarea * remove form item layout prepost * Update FormLayout.tsx * Update FormLayout.tsx * Update FormLayout.tsx * form layout and advanced * Update advanced.tsx * protection and hooks * fix * parse --------- Co-authored-by: Jonathan Summers-Muir <MildTomato@users.noreply.github.com>
295 lines
10 KiB
TypeScript
295 lines
10 KiB
TypeScript
import { zodResolver } from '@hookform/resolvers/zod'
|
|
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
|
import { useEffect, useState } from 'react'
|
|
import { useForm } from 'react-hook-form'
|
|
import { toast } from 'sonner'
|
|
import * as z from 'zod'
|
|
|
|
import { useParams } from 'common'
|
|
import { ScaffoldSection, ScaffoldSectionTitle } from 'components/layouts/Scaffold'
|
|
import NoPermission from 'components/ui/NoPermission'
|
|
import UpgradeToPro from 'components/ui/UpgradeToPro'
|
|
import { useAuthConfigQuery } from 'data/auth/auth-config-query'
|
|
import { useAuthConfigUpdateMutation } from 'data/auth/auth-config-update-mutation'
|
|
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
|
|
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
|
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
|
import { IS_PLATFORM } from 'lib/constants'
|
|
import {
|
|
AlertDescription_Shadcn_,
|
|
AlertTitle_Shadcn_,
|
|
Alert_Shadcn_,
|
|
Button,
|
|
Card,
|
|
CardContent,
|
|
CardFooter,
|
|
FormControl_Shadcn_,
|
|
FormField_Shadcn_,
|
|
Form_Shadcn_,
|
|
Input_Shadcn_,
|
|
PrePostTab,
|
|
WarningIcon,
|
|
} from 'ui'
|
|
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
|
import { StringNumberOrNull } from 'components/ui/Forms/Form.constants'
|
|
|
|
const FormSchema = z.object({
|
|
API_MAX_REQUEST_DURATION: z.coerce
|
|
.number()
|
|
.min(5, 'Must be 5 or larger')
|
|
.max(30, 'Must be a value no greater than 30'),
|
|
DB_MAX_POOL_SIZE: StringNumberOrNull,
|
|
})
|
|
|
|
export const AdvancedAuthSettingsForm = () => {
|
|
const { ref: projectRef } = useParams()
|
|
const organization = useSelectedOrganization()
|
|
const canReadConfig = useCheckPermissions(PermissionAction.READ, 'custom_config_gotrue')
|
|
const canUpdateConfig = useCheckPermissions(PermissionAction.UPDATE, 'custom_config_gotrue')
|
|
|
|
const [isUpdatingRequestDurationForm, setIsUpdatingRequestDurationForm] = useState(false)
|
|
const [isUpdatingDatabaseForm, setIsUpdatingDatabaseForm] = useState(false)
|
|
|
|
const {
|
|
data: authConfig,
|
|
error: authConfigError,
|
|
isLoading,
|
|
isError,
|
|
} = useAuthConfigQuery({ projectRef })
|
|
|
|
const { data: subscription, isSuccess: isSuccessSubscription } = useOrgSubscriptionQuery({
|
|
orgSlug: organization?.slug,
|
|
})
|
|
|
|
const { mutate: updateAuthConfig } = useAuthConfigUpdateMutation()
|
|
|
|
const isTeamsEnterprisePlan =
|
|
isSuccessSubscription && subscription?.plan.id !== 'free' && subscription?.plan.id !== 'pro'
|
|
const promptTeamsEnterpriseUpgrade =
|
|
IS_PLATFORM && isSuccessSubscription && !isTeamsEnterprisePlan
|
|
|
|
const requestDurationForm = useForm({
|
|
resolver: zodResolver(
|
|
z.object({
|
|
API_MAX_REQUEST_DURATION: FormSchema.shape.API_MAX_REQUEST_DURATION,
|
|
})
|
|
),
|
|
defaultValues: {
|
|
API_MAX_REQUEST_DURATION: 10,
|
|
},
|
|
})
|
|
|
|
const databaseForm = useForm({
|
|
resolver: zodResolver(
|
|
z.object({
|
|
DB_MAX_POOL_SIZE: FormSchema.shape.DB_MAX_POOL_SIZE,
|
|
})
|
|
),
|
|
defaultValues: {
|
|
DB_MAX_POOL_SIZE: '',
|
|
},
|
|
})
|
|
|
|
useEffect(() => {
|
|
if (authConfig) {
|
|
if (!isUpdatingRequestDurationForm) {
|
|
requestDurationForm.reset({
|
|
API_MAX_REQUEST_DURATION: authConfig?.API_MAX_REQUEST_DURATION ?? 10,
|
|
})
|
|
}
|
|
|
|
if (!isUpdatingDatabaseForm) {
|
|
databaseForm.reset({
|
|
DB_MAX_POOL_SIZE:
|
|
authConfig?.DB_MAX_POOL_SIZE !== null ? String(authConfig?.DB_MAX_POOL_SIZE) : '',
|
|
})
|
|
}
|
|
}
|
|
}, [authConfig, isUpdatingRequestDurationForm, isUpdatingDatabaseForm])
|
|
|
|
const onSubmitRequestDurationForm = (values: any) => {
|
|
if (!projectRef) return console.error('Project ref is required')
|
|
if (!isTeamsEnterprisePlan) return
|
|
|
|
setIsUpdatingRequestDurationForm(true)
|
|
|
|
updateAuthConfig(
|
|
{ projectRef: projectRef, config: values },
|
|
{
|
|
onError: (error) => {
|
|
toast.error(`Failed to update request duration settings: ${error?.message}`)
|
|
setIsUpdatingRequestDurationForm(false)
|
|
},
|
|
onSuccess: () => {
|
|
toast.success('Successfully updated request duration settings')
|
|
setIsUpdatingRequestDurationForm(false)
|
|
},
|
|
}
|
|
)
|
|
}
|
|
|
|
const onSubmitDatabaseForm = (values: any) => {
|
|
if (!projectRef) return console.error('Project ref is required')
|
|
|
|
setIsUpdatingDatabaseForm(true)
|
|
|
|
const config = {
|
|
DB_MAX_POOL_SIZE: values.DB_MAX_POOL_SIZE,
|
|
}
|
|
|
|
updateAuthConfig(
|
|
{ projectRef: projectRef, config },
|
|
{
|
|
onError: (error) => {
|
|
toast.error(`Failed to update database connection settings: ${error?.message}`)
|
|
setIsUpdatingDatabaseForm(false)
|
|
},
|
|
onSuccess: () => {
|
|
toast.success('Successfully updated database connection settings')
|
|
setIsUpdatingDatabaseForm(false)
|
|
},
|
|
}
|
|
)
|
|
}
|
|
|
|
if (isError) {
|
|
return (
|
|
<Alert_Shadcn_ variant="destructive">
|
|
<WarningIcon />
|
|
<AlertTitle_Shadcn_>Failed to retrieve auth configuration</AlertTitle_Shadcn_>
|
|
<AlertDescription_Shadcn_>{authConfigError.message}</AlertDescription_Shadcn_>
|
|
</Alert_Shadcn_>
|
|
)
|
|
}
|
|
|
|
if (!canReadConfig) {
|
|
return <NoPermission resourceText="view auth configuration settings" />
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{promptTeamsEnterpriseUpgrade && (
|
|
<div className="my-4">
|
|
<UpgradeToPro
|
|
primaryText="Upgrade to Team or Enterprise"
|
|
secondaryText="Advanced Auth server settings are only available on the Team Plan and up."
|
|
buttonText="Upgrade to Team"
|
|
/>
|
|
</div>
|
|
)}
|
|
<ScaffoldSection isFullWidth>
|
|
<ScaffoldSectionTitle className="mb-4">Request Duration</ScaffoldSectionTitle>
|
|
|
|
<Form_Shadcn_ {...requestDurationForm}>
|
|
<form
|
|
onSubmit={requestDurationForm.handleSubmit(onSubmitRequestDurationForm)}
|
|
className="space-y-4"
|
|
>
|
|
<Card>
|
|
<CardContent className="pt-6">
|
|
<FormField_Shadcn_
|
|
control={requestDurationForm.control}
|
|
name="API_MAX_REQUEST_DURATION"
|
|
render={({ field }) => (
|
|
<FormItemLayout
|
|
layout="flex-row-reverse"
|
|
label="Maximum time allowed for an Auth request to last"
|
|
description="Number of seconds to wait for an Auth request to complete before canceling it. In certain high-load situations setting a larger or smaller value can be used to control load-shedding. Recommended: 10 seconds."
|
|
>
|
|
<FormControl_Shadcn_>
|
|
<div className="relative">
|
|
<PrePostTab postTab="seconds">
|
|
<Input_Shadcn_
|
|
type="number"
|
|
min={5}
|
|
max={30}
|
|
{...field}
|
|
disabled={!canUpdateConfig || promptTeamsEnterpriseUpgrade}
|
|
/>
|
|
</PrePostTab>
|
|
</div>
|
|
</FormControl_Shadcn_>
|
|
</FormItemLayout>
|
|
)}
|
|
/>
|
|
</CardContent>
|
|
|
|
<CardFooter className="justify-end space-x-2">
|
|
{requestDurationForm.formState.isDirty && (
|
|
<Button type="default" onClick={() => requestDurationForm.reset()}>
|
|
Cancel
|
|
</Button>
|
|
)}
|
|
<Button
|
|
type="primary"
|
|
htmlType="submit"
|
|
disabled={
|
|
!canUpdateConfig ||
|
|
isUpdatingRequestDurationForm ||
|
|
!requestDurationForm.formState.isDirty ||
|
|
promptTeamsEnterpriseUpgrade
|
|
}
|
|
loading={isUpdatingRequestDurationForm}
|
|
>
|
|
Save changes
|
|
</Button>
|
|
</CardFooter>
|
|
</Card>
|
|
</form>
|
|
</Form_Shadcn_>
|
|
</ScaffoldSection>
|
|
|
|
<ScaffoldSection isFullWidth>
|
|
<ScaffoldSectionTitle className="mb-4">Auth Database Connections</ScaffoldSectionTitle>
|
|
|
|
<Form_Shadcn_ {...databaseForm}>
|
|
<form onSubmit={databaseForm.handleSubmit(onSubmitDatabaseForm)} className="space-y-4">
|
|
<Card>
|
|
<CardContent className="pt-6">
|
|
<FormField_Shadcn_
|
|
control={databaseForm.control}
|
|
name="DB_MAX_POOL_SIZE"
|
|
render={({ field }) => (
|
|
<FormItemLayout
|
|
layout="flex-row-reverse"
|
|
label="Max Direct Auth Connections"
|
|
description="Auth will take up no more than this number of connections from the total number of available connections to serve requests. These connections are not reserved, so when unused they are released. Defaults to 10 connections."
|
|
>
|
|
<FormControl_Shadcn_>
|
|
<Input_Shadcn_
|
|
type="number"
|
|
placeholder="10"
|
|
{...field}
|
|
disabled={!canUpdateConfig || promptTeamsEnterpriseUpgrade}
|
|
/>
|
|
</FormControl_Shadcn_>
|
|
</FormItemLayout>
|
|
)}
|
|
/>
|
|
</CardContent>
|
|
|
|
<CardFooter className="justify-end space-x-2">
|
|
{databaseForm.formState.isDirty && (
|
|
<Button type="default" onClick={() => databaseForm.reset()}>
|
|
Cancel
|
|
</Button>
|
|
)}
|
|
<Button
|
|
type="primary"
|
|
htmlType="submit"
|
|
disabled={
|
|
!canUpdateConfig || isUpdatingDatabaseForm || !databaseForm.formState.isDirty
|
|
}
|
|
loading={isUpdatingDatabaseForm}
|
|
>
|
|
Save changes
|
|
</Button>
|
|
</CardFooter>
|
|
</Card>
|
|
</form>
|
|
</Form_Shadcn_>
|
|
</ScaffoldSection>
|
|
</>
|
|
)
|
|
}
|