Files
supabase/studio/components/interfaces/Auth/SmtpForm/SmtpForm.tsx
2022-08-23 16:56:27 +08:00

303 lines
11 KiB
TypeScript

import { observer } from 'mobx-react-lite'
import { useEffect, useState } from 'react'
import { number, object, string } from 'yup'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { Alert, Button, Form, Input, InputNumber, Toggle, IconEye, IconEyeOff } from '@supabase/ui'
import {
FormActions,
FormHeader,
FormPanel,
FormSection,
FormSectionContent,
FormSectionLabel,
} from 'components/ui/Forms'
import { useStore, checkPermissions } from 'hooks'
import { domainRegex } from './../Auth.constants'
import { defaultDisabledSmtpFormValues } from './SmtpForm.constants'
import { generateFormValues, isSmtpEnabled } from './SmtpForm.utils'
const SmtpForm = () => {
const { authConfig, ui } = useStore()
const { config, isLoaded } = authConfig
const [enableSmtp, setEnableSmtp] = useState(false)
const [hidden, setHidden] = useState(true)
const formId = 'auth-config-smtp-form'
const initialValues = generateFormValues(authConfig.config)
const canUpdateConfig = checkPermissions(PermissionAction.UPDATE, 'custom_config_gotrue')
useEffect(() => {
if (isLoaded && isSmtpEnabled(config)) {
setEnableSmtp(true)
}
}, [isLoaded])
const schema = object({
SMTP_ADMIN_EMAIL: string().when([], {
is: () => {
return enableSmtp
},
then: (schema) => schema.email('Must be a valid email').required('Sender email is required'),
otherwise: (schema) => schema,
}),
SMTP_SENDER_NAME: string().when([], {
is: () => {
return enableSmtp
},
then: (schema) => schema.required('Sender name is required'),
otherwise: (schema) => schema,
}),
SMTP_HOST: string().when([], {
is: () => {
return enableSmtp
},
then: (schema) =>
schema
.matches(domainRegex, 'Must be a valid URL or IP address')
.required('Host URL is required.'),
otherwise: (schema) => schema,
}),
SMTP_PORT: number().when([], {
is: () => {
return enableSmtp
},
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: number().when([], {
is: () => {
return enableSmtp
},
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: string().when([], {
is: () => {
return enableSmtp
},
then: (schema) => schema.required('SMTP Username is required'),
otherwise: (schema) => schema,
}),
SMTP_PASS: string().when([], {
is: () => {
return enableSmtp
},
then: (schema) => schema.required('SMTP password is required'),
otherwise: (schema) => schema,
}),
})
const onSubmit = async (values: any, { setSubmitting, resetForm }: any) => {
const payload = enableSmtp ? values : defaultDisabledSmtpFormValues
// Format payload: Remove redundant value + convert port to string
delete payload.ENABLE_SMTP
payload.SMTP_PORT = payload.SMTP_PORT ? payload.SMTP_PORT.toString() : payload.SMTP_PORT
setSubmitting(true)
const { error } = await authConfig.update(payload)
if (!error) {
setHidden(true)
const updatedFormValues = generateFormValues(payload)
resetForm({ values: updatedFormValues, initialValues: updatedFormValues })
ui.setNotification({ category: 'success', message: 'Successfully updated settings' })
} else {
ui.setNotification({ category: 'error', message: 'Failed to update settings', error })
}
setSubmitting(false)
}
return (
<Form id={formId} initialValues={initialValues} onSubmit={onSubmit} validationSchema={schema}>
{({ isSubmitting, resetForm, values }: any) => {
const isValidSmtpConfig = isSmtpEnabled(values)
const hasChanges = JSON.stringify(values) !== JSON.stringify(initialValues)
useEffect(() => {
if (isLoaded) {
const formValues = generateFormValues(config)
resetForm({ values: formValues, initialValues: formValues })
}
}, [isLoaded])
const onResetForm = () => {
setEnableSmtp(isSmtpEnabled(initialValues))
resetForm({ values: initialValues })
}
return (
<>
<FormHeader
title="SMTP Settings"
description="You can use your own SMTP server instead of the built-in email service."
/>
<FormPanel
footer={
<div className="flex py-4 px-8">
<FormActions
form={formId}
isSubmitting={isSubmitting}
hasChanges={hasChanges}
handleReset={onResetForm}
disabled={!canUpdateConfig}
helper={
!canUpdateConfig
? 'You need additional permissions to update authentication settings'
: undefined
}
/>
</div>
}
>
<FormSection>
<FormSectionContent loading={!isLoaded}>
<Toggle
name="ENABLE_SMTP"
size="small"
label="Enable Custom SMTP"
layout="flex"
checked={enableSmtp}
disabled={!canUpdateConfig}
// @ts-ignore
onChange={(value: boolean) => setEnableSmtp(value)}
descriptionText="Emails will be sent using your custom SMTP provider"
/>
</FormSectionContent>
</FormSection>
{enableSmtp && !isValidSmtpConfig && (
<div className="mx-8 mb-8 -mt-4">
<Alert withIcon variant="warning" title="All fields below must be filled">
The following fields must be filled before custom SMTP can be properly enabled
</Alert>
</div>
)}
<FormSection
visible={enableSmtp}
header={<FormSectionLabel>Sender details</FormSectionLabel>}
disabled={!enableSmtp}
>
<FormSectionContent loading={!isLoaded}>
<Input
name="SMTP_ADMIN_EMAIL"
id="SMTP_ADMIN_EMAIL"
label="Sender email"
descriptionText="This is the email address the emails are sent from"
placeholder="noreply@yourdomain.com"
disabled={!canUpdateConfig}
/>
<Input
name="SMTP_SENDER_NAME"
id="SMTP_SENDER_NAME"
label="Sender name"
descriptionText="Name displayed in the recipient's inbox"
placeholder="The name shown on the email"
disabled={!canUpdateConfig}
/>
</FormSectionContent>
</FormSection>
<FormSection
visible={enableSmtp}
disabled={!enableSmtp}
header={
<FormSectionLabel>
<span>SMTP Provider Settings</span>
<p className="text-scale-900 my-4">
Your SMTP Credentials will always be encrypted in our database.
</p>
</FormSectionLabel>
}
>
<FormSectionContent loading={!isLoaded}>
<Input
name="SMTP_HOST"
placeholder="your.smtp.host.com"
id="SMTP_HOST"
label="Host"
descriptionText="Hostname or IP address of your SMTP server."
disabled={!canUpdateConfig}
/>
<InputNumber
name="SMTP_PORT"
id="SMTP_PORT"
placeholder="587"
label="Port number"
descriptionText={
<>
<span className="block">
Port used by your SMTP server. Common ports include 25, 465, and 587.{' '}
</span>
<span className="mt-2 block">
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.
</span>
</>
}
disabled={!canUpdateConfig}
/>
<InputNumber
id="SMTP_MAX_FREQUENCY"
name="SMTP_MAX_FREQUENCY"
label="Minimum interval between emails being sent"
descriptionText="How long between each email can a new email be sent via your SMTP server."
actions={<span className="text-scale-900 mr-3">seconds</span>}
disabled={!canUpdateConfig}
/>
<InputNumber
name="RATE_LIMIT_EMAIL_SENT"
id="RATE_LIMIT_EMAIL_SENT"
min={0}
label="Rate limit for sending emails"
descriptionText="How many emails can be sent per hour."
actions={<span className="text-scale-900 mr-3">emails per hour</span>}
disabled={!canUpdateConfig}
/>
<Input
name="SMTP_USER"
id="SMTP_USER"
label="Username"
placeholder="SMTP Username"
disabled={!canUpdateConfig}
/>
<Input
name="SMTP_PASS"
id="SMTP_PASS"
type={hidden ? 'password' : 'text'}
label="Password"
placeholder="SMTP Password"
actions={
<Button
icon={hidden ? <IconEye /> : <IconEyeOff />}
type="default"
onClick={() => setHidden(!hidden)}
/>
}
disabled={!canUpdateConfig}
/>
</FormSectionContent>
</FormSection>
</FormPanel>
</>
)
}}
</Form>
)
}
export default observer(SmtpForm)