import { yupResolver } from '@hookform/resolvers/yup' import { PermissionAction } from '@supabase/shared-types/out/constants' import { AlertTriangle, Eye, EyeOff } from 'lucide-react' import Link from 'next/link' import { useEffect, useState } from 'react' import { useForm } from 'react-hook-form' import { toast } from 'sonner' import * as yup from 'yup' import { useParams } from 'common' import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold' import NoPermission from 'components/ui/NoPermission' import { useAuthConfigQuery } from 'data/auth/auth-config-query' import { useAuthConfigUpdateMutation } from 'data/auth/auth-config-update-mutation' import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions' import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, Button, Card, CardContent, CardFooter, FormControl_Shadcn_, FormField_Shadcn_, Form_Shadcn_, Input_Shadcn_, PrePostTab, Switch, WarningIcon, } from 'ui' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' import { urlRegex } from '../Auth.constants' import { defaultDisabledSmtpFormValues } from './SmtpForm.constants' import { generateFormValues, isSmtpEnabled } from './SmtpForm.utils' interface SmtpFormValues { SMTP_ADMIN_EMAIL?: string SMTP_SENDER_NAME?: string SMTP_HOST?: string SMTP_PORT?: number SMTP_MAX_FREQUENCY?: number SMTP_USER?: string SMTP_PASS?: string ENABLE_SMTP: boolean } const SmtpForm = () => { const { ref: projectRef } = useParams() const { data: authConfig, error: authConfigError, isError } = useAuthConfigQuery({ projectRef }) const { mutate: updateAuthConfig, isLoading: isUpdatingConfig } = useAuthConfigUpdateMutation() const [enableSmtp, setEnableSmtp] = useState(false) const [hidden, setHidden] = useState(true) const { can: canReadConfig } = useAsyncCheckProjectPermissions( PermissionAction.READ, 'custom_config_gotrue' ) const { can: canUpdateConfig } = useAsyncCheckProjectPermissions( PermissionAction.UPDATE, 'custom_config_gotrue' ) const smtpSchema = yup.object({ SMTP_ADMIN_EMAIL: yup.string().when('ENABLE_SMTP', { is: true, then: (schema) => schema.email('Must be a valid email').required('Sender email is required'), otherwise: (schema) => schema, }), SMTP_SENDER_NAME: yup.string().when('ENABLE_SMTP', { is: true, then: (schema) => schema.required('Sender name is required'), otherwise: (schema) => schema, }), SMTP_HOST: yup.string().when('ENABLE_SMTP', { is: true, then: (schema) => schema .matches(urlRegex({ excludeSimpleDomains: false }), 'Must be a valid URL or IP address') .required('Host URL is required.'), otherwise: (schema) => schema, }), SMTP_PORT: yup.number().when('ENABLE_SMTP', { is: true, then: (schema) => schema .required('Port number is required.') .min(1, 'Must be a valid port number more than 0') .max(65535, 'Must be a valid port number no more than 65535'), otherwise: (schema) => schema, }), SMTP_MAX_FREQUENCY: yup.number().when('ENABLE_SMTP', { is: true, then: (schema) => schema .required('Rate limit is required.') .min(1, 'Must be more than 0') .max(32767, 'Must not be more than 32,767 an hour'), otherwise: (schema) => schema, }), SMTP_USER: yup.string().when('ENABLE_SMTP', { is: true, then: (schema) => schema.required('SMTP Username is required'), otherwise: (schema) => schema, }), SMTP_PASS: yup.string(), ENABLE_SMTP: yup.boolean().required(), }) const form = useForm({ resolver: yupResolver(smtpSchema), defaultValues: { SMTP_ADMIN_EMAIL: '', SMTP_SENDER_NAME: '', SMTP_HOST: '', SMTP_PORT: undefined, SMTP_MAX_FREQUENCY: undefined, SMTP_USER: '', SMTP_PASS: '', ENABLE_SMTP: false, }, }) // Update form values when auth config is loaded useEffect(() => { if (authConfig) { const formValues = generateFormValues(authConfig) // Convert SMTP_PORT from string to number if it exists if (formValues.SMTP_PORT) { formValues.SMTP_PORT = Number(formValues.SMTP_PORT) as any } form.reset({ ...formValues, ENABLE_SMTP: isSmtpEnabled(authConfig), } as SmtpFormValues) setEnableSmtp(isSmtpEnabled(authConfig)) } }, [authConfig, form]) // Update enableSmtp state when the form field changes useEffect(() => { const subscription = form.watch((value, { name }) => { if (name === 'ENABLE_SMTP') { setEnableSmtp(value.ENABLE_SMTP as boolean) } }) return () => subscription.unsubscribe() }, [form]) const onSubmit = (values: SmtpFormValues) => { const { ENABLE_SMTP, ...rest } = values const payload = ENABLE_SMTP ? rest : defaultDisabledSmtpFormValues // Format payload: Convert port to string if (payload.SMTP_PORT) { payload.SMTP_PORT = payload.SMTP_PORT.toString() as any } // the SMTP_PASS is write-only, it's never shown. If we don't delete it from the payload, it will replace the // previously saved value with an empty one if (payload.SMTP_PASS === '') { delete payload.SMTP_PASS } updateAuthConfig( { projectRef: projectRef!, config: payload as any }, { onError: (error) => { toast.error(`Failed to update settings: ${error.message}`) }, onSuccess: () => { setHidden(true) toast.success('Successfully updated settings') }, } ) } if (isError) { return ( Failed to retrieve auth configuration {authConfigError.message} ) } if (!canReadConfig) { return } return (
( Emails will be sent using your custom SMTP provider. Email rate limits can be adjusted{' '} here .

} >
)} /> {enableSmtp && !isSmtpEnabled(form.getValues() as any) && (
All fields below must be filled The following fields must be filled before custom SMTP can be properly enabled
)}
{enableSmtp && ( <>

Sender details

Configure the sender information for your emails.

( )} /> ( )} />

SMTP Provider Settings

Your SMTP Credentials will always be encrypted in our database.

{form.watch('SMTP_HOST')?.endsWith('.gmail.com') && ( Check your SMTP provider Not all SMTP providers are designed for the email sending required by Supabase Auth. It looks like the SMTP provider you entered is designed for sending personal email messages and not for sending transactional messages. Although you can ignore this warning, email deliverability may be impacted. )} ( )} /> ( Port used by your SMTP server. Common ports include 25, 465, and 587.{' '} Avoid using port 25 as modern SMTP email clients shouldn't use this port, it is traditionally blocked by residential ISPs and Cloud Hosting Providers, to curb the amount of spam. } > field.onChange(Number(e.target.value))} placeholder="587" disabled={!canUpdateConfig} /> )} /> ( field.onChange(Number(e.target.value))} disabled={!canUpdateConfig} /> )} /> ( )} /> ( setHidden(!hidden)} icon={hidden ? : } /> } > )} />
)} {form.formState.isDirty && ( )}
) } export default SmtpForm