diff --git a/studio/components/interfaces/Auth/AuthProvidersForm.tsx b/studio/components/interfaces/Auth/AuthProvidersForm.tsx new file mode 100644 index 00000000000..11b5c56551d --- /dev/null +++ b/studio/components/interfaces/Auth/AuthProvidersForm.tsx @@ -0,0 +1,309 @@ +import { + Alert, + Button, + Collapsible, + Form, + IconCheck, + IconChevronUp, + Input, + Listbox, + Toggle, + Menu, +} from '@supabase/ui' +import { useStore } from 'hooks' +import { observer } from 'mobx-react-lite' +import { useState } from 'react' +import ReactMarkdown from 'react-markdown' +import { PROVIDERS_SCHEMAS } from 'stores/authConfig/schema' + +interface Enum { + label: string + value: string + icon: string +} +interface Provider { + $schema: string + type: 'object' + title: string + properties: { + [x: string]: { + title: string + type: 'boolean' | 'string' | 'select' + description?: string + descriptionOptional?: string + enum: Enum[] + show: { + key: string + matches: string + } + } + } + validationSchema: any // todo: use Yup type + misc: { + iconKey: 'gitlab-icon' + requiresRedirect: true + helper: string + alert: { + title: string + description: string + } + } +} + +const AuthProvidersForm = () => { + const providers = PROVIDERS_SCHEMAS + + return ( +
+
+ { + // @ts-expect-error + // to do: fix type error, needs to be dynamic + providers.map((provider: Provider, i) => { + return + }) + } +
+
+ ) +} + +const ProviderForm = ({ provider }: { provider: Provider }) => { + const [open, setOpen] = useState(false) + const { authConfig, ui } = useStore() + const active = authConfig.config[`EXTERNAL_${provider?.title?.toUpperCase()}_ENABLED`] + const INITIAL_VALUES: { [x: string]: string } = {} + + /** + * construct values for INITIAL_VALUES + * + * return empty string `""` rather than `null` + * as it breaks form. null is not a valid value. + */ + Object.keys(provider.properties).forEach((key) => { + INITIAL_VALUES[key] = authConfig.config[key] ?? '' + }) + + return ( + + + + +
{ + try { + await authConfig.update(values) + setSubmitting(false) + setOpen(false) + } catch (error) { + console.error(error) + } + }} + > + {({ isSubmitting, handleReset, values }: any) => ( + +
+
+ {Object.keys(provider.properties).map((x: string) => { + /** + * Properties of the form + */ + const properties = provider.properties[x] + + /** + * Conditionally hide properties based on value of key + */ + if (properties.show && values[properties.show.key] !== properties.show.matches) { + return null + } + + switch (properties.type) { + case 'string': + return ( + + {properties.description} + + ) : null + } + // + // @ts-expect-error + // + // to do: change to "string" | React.ReactNode | undefined + // + labelOptional={ + properties.descriptionOptional ? ( + + {properties.descriptionOptional} + + ) : null + } + /> + ) + break + + case 'boolean': + return ( + + ) + break + + case 'select': + return ( + + {properties.enum.map((option: Enum) => { + return ( + ( + + )} + > + {option.label} + + ) + })} + + ) + break + + default: + break + } + })} + {provider?.misc?.alert && ( + + {provider.misc.alert.description} + + )} + {provider.misc.requiresRedirect && ( + <> + + {provider.misc.helper} + + + + )} +
+ + +
+
+
+
+ )} +
+
+ ) +} + +export default observer(AuthProvidersForm) diff --git a/studio/components/interfaces/Auth/index.tsx b/studio/components/interfaces/Auth/index.tsx new file mode 100644 index 00000000000..967ecf63bc8 --- /dev/null +++ b/studio/components/interfaces/Auth/index.tsx @@ -0,0 +1,3 @@ +import AuthProvidersForm from './AuthProvidersForm' + +export { AuthProvidersForm } diff --git a/studio/components/interfaces/index.ts b/studio/components/interfaces/index.ts index 3f7c43da567..e9065fdd504 100644 --- a/studio/components/interfaces/index.ts +++ b/studio/components/interfaces/index.ts @@ -1,4 +1,6 @@ import TableGridEditor from './TableGridEditor' import SQLEditor from './SQLEditor' +export * from './Auth' + export { TableGridEditor, SQLEditor } diff --git a/studio/components/layouts/AuthLayout/AuthLayout.tsx b/studio/components/layouts/AuthLayout/AuthLayout.tsx index da038f2bfa5..425df98f1ae 100644 --- a/studio/components/layouts/AuthLayout/AuthLayout.tsx +++ b/studio/components/layouts/AuthLayout/AuthLayout.tsx @@ -2,7 +2,7 @@ import { FC, ReactNode, useState, useEffect } from 'react' import { observer } from 'mobx-react-lite' import { useRouter } from 'next/router' -import { useStore } from 'hooks' +import { useStore, withAuth } from 'hooks' import BaseLayout from '../' import Error from 'components/ui/Error' import ProductMenu from 'components/ui/ProductMenu' @@ -57,4 +57,4 @@ const AuthLayout: FC = ({ title, children }) => { ) } -export default observer(AuthLayout) +export default withAuth(observer(AuthLayout)) diff --git a/studio/components/ui/Forms/AutoSchemaForm.tsx b/studio/components/ui/Forms/AutoSchemaForm.tsx index aa26c19ff50..e4f97dfded0 100644 --- a/studio/components/ui/Forms/AutoSchemaForm.tsx +++ b/studio/components/ui/Forms/AutoSchemaForm.tsx @@ -68,7 +68,7 @@ const AutoSchemaForm = () => { return errors }} > - {({ isSubmitting, handleReset }: any) => ( + {({ isSubmitting, handleReset, values }: any) => ( diff --git a/studio/package.json b/studio/package.json index 179cd063912..ae7e4e4263d 100644 --- a/studio/package.json +++ b/studio/package.json @@ -53,6 +53,7 @@ "react-grid-layout": "^1.3.0", "react-hot-toast": "^2.1.1", "react-inlinesvg": "^2.3.0", + "react-markdown": "^8.0.3", "react-split": "^2.0.13", "react-virtualized-auto-sizer": "^1.0.6", "react-window": "^1.8.6", @@ -64,6 +65,7 @@ "swr": "^1.2.1", "uniforms-bootstrap4": "^3.6.2", "uniforms-bridge-json-schema": "^3.6.2", + "yup": "^0.32.11", "zxcvbn": "^4.4.2" }, "devDependencies": { diff --git a/studio/pages/project/[ref]/auth/AuthFormSchema.json b/studio/pages/project/[ref]/auth/AuthFormSchema.json deleted file mode 100644 index faf00278cc5..00000000000 --- a/studio/pages/project/[ref]/auth/AuthFormSchema.json +++ /dev/null @@ -1,281 +0,0 @@ -[ - { - "name": "Email", - "icon": "email-icon", - "form": [ - { - "type": "text", - "name": "email-app-id", - "label": "email App Id", - "description": "" - }, - { - "type": "text", - "name": "email-id-key", - "label": "email client ID", - "description": "" - }, - { - "type": "text", - "name": "email-id-key", - "label": "email client ID", - "description": "" - }, - { - "type": "text", - "name": "email-id-key", - "label": "email client ID", - "description": "" - } - ] - }, - { - "name": "Phone", - "icon": "phone-icon", - "form": [ - { - "type": "select", - "name": "phone-app-provider", - "label": "Phone service provider", - "description": "You can use a provider etc etc" - }, - { - "type": "text", - "name": "phone-id-key", - "label": "phone client ID", - "description": "" - }, - { - "type": "text", - "name": "phone-id-key", - "label": "phone client ID", - "description": "" - }, - { - "type": "text", - "name": "phone-id-key", - "label": "phone client ID", - "description": "" - } - ] - }, - { - "name": "Google", - "icon": "google-icon", - "form": [ - { - "type": "text", - "name": "google-app-id", - "label": "Google App Id", - "description": "" - }, - { - "type": "text", - "name": "google-id-key", - "label": "Google client ID", - "description": "" - } - ] - }, - { - "name": "Facebook", - "icon": "facebook-icon", - "form": [ - { - "type": "text", - "name": "facebook-app-id", - "label": "Facebook App Id", - "description": "" - }, - { - "type": "text", - "name": "facebook-id-key", - "label": "Facebook client ID", - "description": "" - } - ] - }, - { - "name": "Twitter", - "icon": "twitter-icon", - "form": [ - { - "type": "text", - "name": "twitter-app-id", - "label": "Twitter App Id", - "description": "" - }, - { - "type": "text", - "name": "twitter-id-key", - "label": "Twitter client ID", - "description": "" - } - ] - }, - { - "name": "Apple", - "icon": "apple-icon", - "form": [ - { - "type": "text", - "name": "apple-app-id", - "label": "Apple App Id", - "description": "" - }, - { - "type": "text", - "name": "apple-id-key", - "label": "Apple client ID", - "description": "" - } - ] - }, - { - "name": "Microsoft", - "icon": "microsoft-icon", - "form": [ - { - "type": "text", - "name": "microsoft-app-id", - "label": "Microsoft App Id", - "description": "" - }, - { - "type": "text", - "name": "microsoft-id-key", - "label": "Microsoft client ID", - "description": "" - } - ] - }, - { - "name": "Gitlab", - "icon": "gitlab-icon", - "form": [ - { - "type": "text", - "name": "gitlab-app-id", - "label": "Gitlab App Id", - "description": "" - }, - { - "type": "text", - "name": "gitlab-id-key", - "label": "Gitlab client ID", - "description": "" - } - ] - }, - - { - "name": "Bitbucket", - "icon": "bitbucket-icon", - "form": [ - { - "type": "text", - "name": "bitbucket-app-id", - "label": "Bitbucket App Id", - "description": "" - }, - { - "type": "text", - "name": "bitbucket-id-key", - "label": "Bitbucket client ID", - "description": "" - } - ] - }, - { - "name": "Discord", - "icon": "discord-icon", - "form": [ - { - "type": "text", - "name": "discord-app-id", - "label": "Discord App Id", - "description": "" - }, - { - "type": "text", - "name": "discord-id-key", - "label": "Discord client ID", - "description": "" - } - ] - }, - - { - "name": "Github", - "icon": "github-icon-dark", - "form": [ - { - "type": "text", - "name": "github-app-id", - "label": "Github App Id", - "description": "" - }, - { - "type": "text", - "name": "github-id-key", - "label": "Github client ID", - "description": "" - } - ] - }, - - { - "name": "Slack", - "icon": "slack-icon", - "form": [ - { - "type": "text", - "name": "slack-app-id", - "label": "Slack App Id", - "description": "" - }, - { - "type": "text", - "name": "slack-id-key", - "label": "Slack client ID", - "description": "" - } - ] - }, - { - "name": "Spotify", - "icon": "spotify-icon", - "form": [ - { - "type": "text", - "name": "spotify-app-id", - "label": "Spotify App Id", - "description": "" - }, - { - "type": "text", - "name": "spotify-id-key", - "label": "Spotify client ID", - "description": "" - } - ] - }, - { - "name": "Twitch", - "icon": "twitch-icon", - "form": [ - { - "type": "text", - "name": "twitch-app-id", - "label": "Twitch App Id", - "description": "" - }, - { - "type": "text", - "name": "twitch-id-key", - "label": "Twitch client ID", - "description": "" - } - ] - } -] diff --git a/studio/pages/project/[ref]/auth/AuthFormSchema.tsx b/studio/pages/project/[ref]/auth/AuthFormSchema.tsx new file mode 100644 index 00000000000..09ed076006f --- /dev/null +++ b/studio/pages/project/[ref]/auth/AuthFormSchema.tsx @@ -0,0 +1,293 @@ +import { object, string } from 'yup' + +export default [ + { + name: 'Email', + icon: 'email-icon', + form: [ + { + type: 'text', + name: 'email-app-id', + label: 'email App Id', + description: '', + }, + { + type: 'text', + name: 'email-id-key', + label: 'email client ID', + description: '', + }, + { + type: 'text', + name: 'email-id-key', + label: 'email client ID', + description: '', + }, + { + type: 'text', + name: 'email-id-key', + label: 'email client ID', + description: '', + }, + ], + }, + { + name: 'Phone', + icon: 'phone-icon', + form: [ + { + type: 'select', + name: 'phone-app-provider', + label: 'Phone service provider', + description: 'You can use a provider etc etc', + }, + { + type: 'text', + name: 'phone-id-key', + label: 'phone client ID', + description: '', + }, + { + type: 'text', + name: 'phone-id-key', + label: 'phone client ID', + description: '', + }, + { + type: 'text', + name: 'phone-id-key', + label: 'phone client ID', + description: '', + }, + ], + }, + { + name: 'Google', + icon: 'google-icon', + validationSchema: object({ + 'google-app-id': string() + .required('You need THIS') + .min(7, 'Needs to be at least 7 characters') + .max(10, 'Can not be longer than 10 characters'), + 'google-id-key': string() + .required('You need THIS') + .min(7, 'Needs to be at least 7 characters') + .max(10, 'Can not be longer than 10 characters'), + }), + form: [ + { + type: 'text', + name: 'google-app-id', + label: 'Google App Id', + description: '', + }, + { + type: 'text', + name: 'google-id-key', + label: 'Google client ID', + description: '', + }, + ], + }, + { + name: 'Facebook', + icon: 'facebook-icon', + form: [ + { + type: 'text', + name: 'facebook-app-id', + label: 'Facebook App Id', + description: '', + }, + { + type: 'text', + name: 'facebook-id-key', + label: 'Facebook client ID', + description: '', + }, + ], + }, + { + name: 'Twitter', + icon: 'twitter-icon', + form: [ + { + type: 'text', + name: 'twitter-app-id', + label: 'Twitter App Id', + description: '', + }, + { + type: 'text', + name: 'twitter-id-key', + label: 'Twitter client ID', + description: '', + }, + ], + }, + { + name: 'Apple', + icon: 'apple-icon', + form: [ + { + type: 'text', + name: 'apple-app-id', + label: 'Apple App Id', + description: '', + }, + { + type: 'text', + name: 'apple-id-key', + label: 'Apple client ID', + description: '', + }, + ], + }, + { + name: 'Microsoft', + icon: 'microsoft-icon', + form: [ + { + type: 'text', + name: 'microsoft-app-id', + label: 'Microsoft App Id', + description: '', + }, + { + type: 'text', + name: 'microsoft-id-key', + label: 'Microsoft client ID', + description: '', + }, + ], + }, + { + name: 'Gitlab', + icon: 'gitlab-icon', + form: [ + { + type: 'text', + name: 'gitlab-app-id', + label: 'Gitlab App Id', + description: '', + }, + { + type: 'text', + name: 'gitlab-id-key', + label: 'Gitlab client ID', + description: '', + }, + ], + }, + + { + name: 'Bitbucket', + icon: 'bitbucket-icon', + form: [ + { + type: 'text', + name: 'bitbucket-app-id', + label: 'Bitbucket App Id', + description: '', + }, + { + type: 'text', + name: 'bitbucket-id-key', + label: 'Bitbucket client ID', + description: '', + }, + ], + }, + { + name: 'Discord', + icon: 'discord-icon', + form: [ + { + type: 'text', + name: 'discord-app-id', + label: 'Discord App Id', + description: '', + }, + { + type: 'text', + name: 'discord-id-key', + label: 'Discord client ID', + description: '', + }, + ], + }, + + { + name: 'Github', + icon: 'github-icon-dark', + form: [ + { + type: 'text', + name: 'github-app-id', + label: 'Github App Id', + description: '', + }, + { + type: 'text', + name: 'github-id-key', + label: 'Github client ID', + description: '', + }, + ], + }, + + { + name: 'Slack', + icon: 'slack-icon', + form: [ + { + type: 'text', + name: 'slack-app-id', + label: 'Slack App Id', + description: '', + }, + { + type: 'text', + name: 'slack-id-key', + label: 'Slack client ID', + description: '', + }, + ], + }, + { + name: 'Spotify', + icon: 'spotify-icon', + form: [ + { + type: 'text', + name: 'spotify-app-id', + label: 'Spotify App Id', + description: '', + }, + { + type: 'text', + name: 'spotify-id-key', + label: 'Spotify client ID', + description: '', + }, + ], + }, + { + name: 'Twitch', + icon: 'twitch-icon', + form: [ + { + type: 'text', + name: 'twitch-app-id', + label: 'Twitch App Id', + description: '', + }, + { + type: 'text', + name: 'twitch-id-key', + label: 'Twitch client ID', + description: '', + }, + ], + }, +] diff --git a/studio/pages/project/[ref]/auth/settings-new.tsx b/studio/pages/project/[ref]/auth/settings-new.tsx index eb083c4739c..b601da5efb5 100644 --- a/studio/pages/project/[ref]/auth/settings-new.tsx +++ b/studio/pages/project/[ref]/auth/settings-new.tsx @@ -1,53 +1,97 @@ -import { - Button, - Collapsible, - Form, - IconArrowRight, - IconCheck, - IconChevronUp, - IconGlobe, - IconTrash, - IconX, - Input, - InputNumber, - Modal, - Tabs, - Toggle, -} from '@supabase/ui' +import { Loading } from '@supabase/ui' +import { AuthProvidersForm } from 'components/interfaces' import { AuthLayout } from 'components/layouts' - -import { withAuth } from 'hooks' -import { observer } from 'mobx-react-lite' -import React, { useReducer, useState } from 'react' -// import AuthFormSchema from './AuthFormSchema.json' - import { AutoSchemaForm } from 'components/ui/Forms' +import { useStore, withAuth } from 'hooks' +import { get } from 'lib/common/fetch' +import { API_URL } from 'lib/constants' +import { observer } from 'mobx-react-lite' +import { useRouter } from 'next/router' +import React from 'react' +import { useState } from 'react' +import { useEffect } from 'react' +import useSWR from 'swr' +import { NextPageWithLayout } from 'types' -const Auth = () => { - return ( - -
- +// const Auth = () => { +// return ( +// +//
+// +//
+//
+// ) +// } + +const PageLayout: NextPageWithLayout = () => { + const router = useRouter() + const { ui, authConfig } = useStore() + + const URL = `${API_URL}/auth/${router.query.ref}/config` + // const { data: config, error }: any = useSWR(URL, get) + + // console.log(authConfig) + + useEffect(() => { + // temporary store loader + authConfig.load() + }, [ui.selectedProjectRef]) + + const [model, setModel] = useState({}) + const [isCustomSMTPEnabled, setCustomSMTP] = useState(false) + const [externalProvidersModel, setExternalProvidersModel] = useState({}) + + if (!authConfig.isLoaded) { + return {''} + } + + // if (error) { + // return ( + // + //

Error connecting to API

+ //

{`${error}`}

+ //
+ // ) + // } + + // console.log(config) + + // useEffect(() => { + // if (config) { + // const temp = + // config.SMTP_ADMIN_EMAIL || + // config.SMTP_HOST || + // config.SMTP_PORT || + // config.SMTP_USER || + // config.SMTP_PASS + // setCustomSMTP(temp) + // } + // setModel({ ...config }) + // setExternalProvidersModel({ ...config }) + // }, [config]) + + if (authConfig) + return ( +
+
+

General settings

+

+ Turn payment methods on and off in one click – no engineering time required. +

+

+ Use our guide to check which payment methods are compatible with your integration. +

+ +
+ +
- - ) + ) } -export function WholeForm() { - return ( -
-
-

General settings

-

- Turn payment methods on and off in one click – no engineering time required. -

-

- Use our guide to check which payment methods are compatible with your integration. -

- -
-
- ) +PageLayout.getLayout = (page) => { + // console.log() + return {page} } -export default withAuth(observer(Auth)) +export default observer(PageLayout) diff --git a/studio/pages/project/[ref]/auth/templates-new.tsx b/studio/pages/project/[ref]/auth/templates-new.tsx index c35d82c6bc5..7ce5ea5c41d 100644 --- a/studio/pages/project/[ref]/auth/templates-new.tsx +++ b/studio/pages/project/[ref]/auth/templates-new.tsx @@ -19,7 +19,7 @@ import AutoSchemaForm from 'components/ui/Forms/AutoSchemaForm' import { withAuth } from 'hooks' import { observer } from 'mobx-react-lite' import React, { useReducer, useState } from 'react' -import AuthFormSchema from './AuthFormSchema.json' +import AuthFormSchema from './AuthFormSchema' // import GoogleIcon './../../../static/icons/google-icon.svg' diff --git a/studio/public/img/icons/apple-icon.svg b/studio/public/img/icons/apple-icon.svg new file mode 100644 index 00000000000..3902c024ec0 --- /dev/null +++ b/studio/public/img/icons/apple-icon.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/studio/public/img/icons/bitbucket-icon.svg b/studio/public/img/icons/bitbucket-icon.svg new file mode 100644 index 00000000000..cdf84686d93 --- /dev/null +++ b/studio/public/img/icons/bitbucket-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/studio/public/img/icons/discord-icon.svg b/studio/public/img/icons/discord-icon.svg new file mode 100644 index 00000000000..3efe1ec110f --- /dev/null +++ b/studio/public/img/icons/discord-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/studio/public/img/icons/email-icon2.svg b/studio/public/img/icons/email-icon2.svg new file mode 100644 index 00000000000..1c65d5b7768 --- /dev/null +++ b/studio/public/img/icons/email-icon2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/studio/public/img/icons/facebook-icon.svg b/studio/public/img/icons/facebook-icon.svg new file mode 100644 index 00000000000..916c8eff2b7 --- /dev/null +++ b/studio/public/img/icons/facebook-icon.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + diff --git a/studio/public/img/icons/github-icon-dark.svg b/studio/public/img/icons/github-icon-dark.svg new file mode 100644 index 00000000000..2667c90a499 --- /dev/null +++ b/studio/public/img/icons/github-icon-dark.svg @@ -0,0 +1,6 @@ + + + diff --git a/studio/public/img/icons/github-icon.svg b/studio/public/img/icons/github-icon.svg new file mode 100644 index 00000000000..5e9611f831b --- /dev/null +++ b/studio/public/img/icons/github-icon.svg @@ -0,0 +1,6 @@ + + + diff --git a/studio/public/img/icons/gitlab-icon.svg b/studio/public/img/icons/gitlab-icon.svg new file mode 100644 index 00000000000..07da670ab19 --- /dev/null +++ b/studio/public/img/icons/gitlab-icon.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + 1x + + + 1x + + + 1x + + + + 1x + + + diff --git a/studio/public/img/icons/google-icon.svg b/studio/public/img/icons/google-icon.svg new file mode 100644 index 00000000000..06dc52f0aa6 --- /dev/null +++ b/studio/public/img/icons/google-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/studio/public/img/icons/keycloak-icon.svg b/studio/public/img/icons/keycloak-icon.svg new file mode 100644 index 00000000000..21c2bf0c57a --- /dev/null +++ b/studio/public/img/icons/keycloak-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/studio/public/img/icons/linkedin-icon.svg b/studio/public/img/icons/linkedin-icon.svg new file mode 100644 index 00000000000..1c15b7e715a --- /dev/null +++ b/studio/public/img/icons/linkedin-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/studio/public/img/icons/messagebird-icon.svg b/studio/public/img/icons/messagebird-icon.svg new file mode 100644 index 00000000000..b9c98603bc7 --- /dev/null +++ b/studio/public/img/icons/messagebird-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/studio/public/img/icons/microsoft-icon.svg b/studio/public/img/icons/microsoft-icon.svg new file mode 100644 index 00000000000..5334aa7ca68 --- /dev/null +++ b/studio/public/img/icons/microsoft-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/studio/public/img/icons/notion-icon.svg b/studio/public/img/icons/notion-icon.svg new file mode 100644 index 00000000000..38303fc72fd --- /dev/null +++ b/studio/public/img/icons/notion-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/studio/public/img/icons/phone-icon4.svg b/studio/public/img/icons/phone-icon4.svg new file mode 100644 index 00000000000..49a477ef9df --- /dev/null +++ b/studio/public/img/icons/phone-icon4.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/studio/public/img/icons/slack-icon.svg b/studio/public/img/icons/slack-icon.svg new file mode 100644 index 00000000000..69a4eb6a217 --- /dev/null +++ b/studio/public/img/icons/slack-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/studio/public/img/icons/spotify-icon.svg b/studio/public/img/icons/spotify-icon.svg new file mode 100644 index 00000000000..41d8137620f --- /dev/null +++ b/studio/public/img/icons/spotify-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/studio/public/img/icons/textlocal-icon.png b/studio/public/img/icons/textlocal-icon.png new file mode 100644 index 00000000000..101b67c0724 Binary files /dev/null and b/studio/public/img/icons/textlocal-icon.png differ diff --git a/studio/public/img/icons/twilio-icon.svg b/studio/public/img/icons/twilio-icon.svg new file mode 100644 index 00000000000..76d12c7164a --- /dev/null +++ b/studio/public/img/icons/twilio-icon.svg @@ -0,0 +1 @@ + diff --git a/studio/public/img/icons/twitch-icon.svg b/studio/public/img/icons/twitch-icon.svg new file mode 100644 index 00000000000..ee4a95fbf02 --- /dev/null +++ b/studio/public/img/icons/twitch-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/studio/public/img/icons/twitter-icon.svg b/studio/public/img/icons/twitter-icon.svg new file mode 100644 index 00000000000..c0364cc1527 --- /dev/null +++ b/studio/public/img/icons/twitter-icon.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/studio/public/img/icons/vonage-icon.svg b/studio/public/img/icons/vonage-icon.svg new file mode 100644 index 00000000000..f21da2bfbf4 --- /dev/null +++ b/studio/public/img/icons/vonage-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/studio/public/img/icons/workos-icon.svg b/studio/public/img/icons/workos-icon.svg new file mode 100644 index 00000000000..1827fa0b275 --- /dev/null +++ b/studio/public/img/icons/workos-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/studio/public/img/icons/zoom-icon.svg b/studio/public/img/icons/zoom-icon.svg new file mode 100644 index 00000000000..d68ee1779b5 --- /dev/null +++ b/studio/public/img/icons/zoom-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/studio/stores/RootStore.ts b/studio/stores/RootStore.ts index 729e7d3445d..2518c698b18 100644 --- a/studio/stores/RootStore.ts +++ b/studio/stores/RootStore.ts @@ -5,6 +5,9 @@ import MetaStore, { IMetaStore } from './pgmeta/MetaStore' import UiStore, { IUiStore } from './UiStore' import ProjectContentStore, { IProjectContentStore } from './content/ProjectContentStore' import ProjectFunctionsStore, { IProjectFunctionsStore } from './functions/ProjectFunctionsStore' +import ProjectAuthConfigStore, { + IProjectAuthConfigStore, +} from './authConfig/ProjectAuthConfigStore' // Temporary disable mobx warnings // TODO: need to remove this after refactoring old stores. @@ -16,6 +19,7 @@ export interface IRootStore { ui: IUiStore content: IProjectContentStore functions: IProjectFunctionsStore + authConfig: IProjectAuthConfigStore meta: IMetaStore app: IAppStore setProjectRef: (value?: string) => void @@ -25,6 +29,7 @@ export class RootStore implements IRootStore { ui: IUiStore content: IProjectContentStore functions: IProjectFunctionsStore + authConfig: IProjectAuthConfigStore meta: IMetaStore app: IAppStore @@ -33,6 +38,7 @@ export class RootStore implements IRootStore { // @ts-ignore this.content = new ProjectContentStore(this, { projectRef: '' }) this.functions = new ProjectFunctionsStore(this, { projectRef: '' }) + this.authConfig = new ProjectAuthConfigStore(this, { projectRef: '' }) this.meta = new MetaStore(this, { projectRef: '', connectionString: '', @@ -86,6 +92,7 @@ export class RootStore implements IRootStore { this.ui.setProjectRef(value) this.functions.setProjectRef(value) + this.authConfig.setProjectRef(value) this.content.setProjectRef(value) } diff --git a/studio/stores/authConfig/ProjectAuthConfigStore.ts b/studio/stores/authConfig/ProjectAuthConfigStore.ts new file mode 100644 index 00000000000..34ed236dde4 --- /dev/null +++ b/studio/stores/authConfig/ProjectAuthConfigStore.ts @@ -0,0 +1,124 @@ +import { makeAutoObservable, toJS } from 'mobx' +import { keyBy } from 'lodash' + +import { get, post, patch, delete_ } from 'lib/common/fetch' +import { UserContent, UserContentMap } from 'types' +import { IRootStore } from '../RootStore' +import { API_URL } from 'lib/constants' + +// [Joshen] This will be the new ProjectContentStore +// but use the one under the stores folder projectContentStore first while we transition + +export interface IProjectAuthConfigStore { + isLoading: boolean + isInitialized: boolean + isLoaded: boolean + error: any + config: any + + baseUrl: string + projectRef: string + + load: () => void + update: ({}) => void + setProjectRef: (ref?: string) => void +} + +export default class ProjectAuthConfigStore implements IProjectAuthConfigStore { + rootStore: IRootStore + STATES = { + INITIAL: 'initial', + LOADING: 'loading', + ERROR: 'error', + LOADED: 'loaded', + } + baseUrl: string + projectRef: string + // @ts-ignore + data: UserContentMap = [] + + state = this.STATES.INITIAL + error = null + + constructor(rootStore: IRootStore, options: { projectRef: string }) { + const { projectRef } = options + this.projectRef = projectRef + this.rootStore = rootStore + this.baseUrl = `` + makeAutoObservable(this) + } + + get isLoading() { + return this.state === this.STATES.INITIAL || this.state === this.STATES.LOADING + } + + get isInitialized() { + return this.state !== this.STATES.INITIAL + } + + get isLoaded() { + return this.state === this.STATES.LOADED + } + + get config() { + return this.data + } + + async fetchData() { + const headers = { + 'Content-Type': 'application/json', + } + console.log('this.baseUrl', this.baseUrl) + const response = await get(this.baseUrl, { headers }) + console.log('response in authCOnfig store', response) + if (response.error) { + throw response.error + } + this.data = response + return response + } + + async load() { + let { LOADING, ERROR, LOADED } = this.STATES + try { + this.error = null + this.state = LOADING + await this.fetchData() + this.state = LOADED + return this.data + } catch (e: any) { + console.error('Failed to load auth config', e.message) + this.error = e + this.state = ERROR + } + } + + byId(id: any) { + return toJS(this.data)[id] + } + + async update(updates: any) { + try { + const headers = { + 'Content-Type': 'application/json', + } + let payload = { + ...updates, + } + const url = `${this.baseUrl}` + const updated = await patch(url, payload, { headers }) + if (updated.error) throw updated.error + this.data = updated + return { data: updated, error: null } + } catch (error) { + return { data: null, error } + } + } + + setProjectRef(ref?: string) { + if (ref) { + this.projectRef = ref + this.baseUrl = `${API_URL}/auth/${ref}/config` + } + } +} diff --git a/studio/stores/authConfig/schema/AuthProviders/AuthProvidersFormValidation.tsx b/studio/stores/authConfig/schema/AuthProviders/AuthProvidersFormValidation.tsx new file mode 100644 index 00000000000..1c629f3454d --- /dev/null +++ b/studio/stores/authConfig/schema/AuthProviders/AuthProvidersFormValidation.tsx @@ -0,0 +1,1508 @@ +import { boolean, object, string } from 'yup' + +const JSON_SCHEMA_VERSION = 'http://json-schema.org/draft-07/schema#' + +export const PROVIDER_EMAIL = { + $schema: JSON_SCHEMA_VERSION, + type: 'object', + title: 'Email', + properties: { + EXTERNAL_EMAIL_ENABLED: { + title: 'Enable Email provider', + description: 'This will enable Email based login for your application', + type: 'boolean', + }, + MAILER_AUTOCONFIRM: { + title: 'Confirm email', + description: `Users will need to confirm their email address before signing in for the first time.`, + type: 'boolean', + }, + MAILER_SECURE_EMAIL_CHANGE_ENABLED: { + title: 'Secure email change', + description: `Users will be required to confirm any email change on both the old email address and new email address. + If disabled, only the new email is required to confirm.`, + type: 'boolean', + }, + }, + misc: { + iconKey: 'email-icon2', + helper: `To complete setup, add this authorisation callback URL to your app's configuration in the Apple Developer Console. + [Learn more](https://supabase.com/docs/guides/auth/auth-apple#configure-your-services-id)`, + }, +} + +export const PROVIDER_PHONE = { + $schema: JSON_SCHEMA_VERSION, + type: 'object', + title: 'Phone', + properties: { + EXTERNAL_PHONE_ENABLED: { + title: 'Enable Phone provider', + description: 'This will enable Email based login for your application', + type: 'boolean', + }, + SMS_PROVIDER: { + type: 'select', + title: 'SMS provider', + description: 'External provider that will handle sending SMS messages', + enum: [ + { label: 'Twilio', value: 'twilio', icon: 'twilio-icon.svg' }, + { label: 'Messagebird', value: 'messagebird', icon: 'messagebird-icon.svg' }, + { label: 'Textlocal', value: 'textlocal', icon: 'textlocal-icon.png' }, + { label: 'Vonage', value: 'vonage', icon: 'vonage-icon.svg' }, + ], + }, + + /** + * twilio + * */ + SMS_TWILIO_ACCOUNT_SID: { + type: 'string', + title: 'Twilio Account Sid', + show: { + key: 'SMS_PROVIDER', + matches: 'twilio', + }, + }, + SMS_TWILIO_AUTH_TOKEN: { + type: 'string', + title: 'Twilio Auth Token', + show: { + key: 'SMS_PROVIDER', + matches: 'twilio', + }, + }, + SMS_TWILIO_MESSAGE_SERVICE_SID: { + type: 'string', + title: 'Twilio Message Service Sid', + show: { + key: 'SMS_PROVIDER', + matches: 'twilio', + }, + }, + + /** + * messagebird + * */ + SMS_MESSAGEBIRD_ACCESS_KEY: { + type: 'string', + title: 'Messagebird Access Key', + show: { + key: 'SMS_PROVIDER', + matches: 'messagebird', + }, + }, + SMS_MESSAGEBIRD_ORIGINATOR: { + type: 'string', + title: 'Messagebird Originator', + show: { + key: 'SMS_PROVIDER', + matches: 'messagebird', + }, + }, + + /** + * textlocal + * */ + SMS_TEXTLOCAL_API_KEY: { + type: 'string', + title: 'Textlocal API Key', + show: { + key: 'SMS_PROVIDER', + matches: 'textlocal', + }, + }, + SMS_TEXTLOCAL_SENDER: { + type: 'string', + title: 'Textlocal Sender', + show: { + key: 'SMS_PROVIDER', + matches: 'textlocal', + }, + }, + + /** + * vonage + * */ + SMS_VONAGE_API_KEY: { + type: 'string', + title: 'Vonage API Key', + show: { + key: 'SMS_PROVIDER', + matches: 'vonage', + }, + }, + SMS_VONAGE_API_SECRET: { + type: 'string', + title: 'Vonage API Secret', + show: { + key: 'SMS_PROVIDER', + matches: 'vonage', + }, + }, + /** + * to do: verify what this is? + */ + SMS_VONAGE_FROM: { + type: 'string', + title: 'Vonage From', + show: { + key: 'SMS_PROVIDER', + matches: 'vonage', + }, + }, + + /** + * SMS confirm setting + */ + SMS_AUTOCONFIRM: { + title: 'Enable phone confirmations', + description: 'Users will need to confirm their phone number before signing in.', + type: 'boolean', + }, + }, + validationSchema: object().shape({ + EXTERNAL_PHONE_ENABLED: boolean().required(), + SMS_PROVIDER: string().required(), + /** + * twilio + */ + SMS_TWILIO_ACCOUNT_SID: string().when(['EXTERNAL_PHONE_ENABLED', 'SMS_PROVIDER'], { + is: (EXTERNAL_PHONE_ENABLED: boolean, SMS_PROVIDER: string) => { + return EXTERNAL_PHONE_ENABLED && SMS_PROVIDER === 'twilio' + }, + then: (schema) => schema.required('"Twilio Account Sid" is required'), + otherwise: (schema) => schema, + }), + SMS_TWILIO_AUTH_TOKEN: string().when(['EXTERNAL_PHONE_ENABLED', 'SMS_PROVIDER'], { + is: (EXTERNAL_PHONE_ENABLED: boolean, SMS_PROVIDER: string) => { + return EXTERNAL_PHONE_ENABLED && SMS_PROVIDER === 'twilio' + }, + then: (schema) => schema.required('"Twilio Auth Token" is required'), + otherwise: (schema) => schema, + }), + SMS_TWILIO_MESSAGE_SERVICE_SID: string().when(['EXTERNAL_PHONE_ENABLED', 'SMS_PROVIDER'], { + is: (EXTERNAL_PHONE_ENABLED: boolean, SMS_PROVIDER: string) => { + return EXTERNAL_PHONE_ENABLED && SMS_PROVIDER === 'twilio' + }, + then: (schema) => schema.required('"Twilio Message Service Sid" is required'), + otherwise: (schema) => schema, + }), + /** + * messagebird + */ + SMS_MESSAGEBIRD_ACCESS_KEY: string().when(['EXTERNAL_PHONE_ENABLED', 'SMS_PROVIDER'], { + is: (EXTERNAL_PHONE_ENABLED: boolean, SMS_PROVIDER: string) => { + return EXTERNAL_PHONE_ENABLED && SMS_PROVIDER === 'messagebird' + }, + then: (schema) => schema.required('"Messagebird Access Key" is required'), + otherwise: (schema) => schema, + }), + SMS_MESSAGEBIRD_ORIGINATOR: string().when(['EXTERNAL_PHONE_ENABLED', 'SMS_PROVIDER'], { + is: (EXTERNAL_PHONE_ENABLED: boolean, SMS_PROVIDER: string) => { + return EXTERNAL_PHONE_ENABLED && SMS_PROVIDER === 'messagebird' + }, + then: (schema) => schema.required('"Messagebird Originator" is required'), + otherwise: (schema) => schema, + }), + /** + * textlocal + */ + SMS_TEXTLOCAL_API_KEY: string().when(['EXTERNAL_PHONE_ENABLED', 'SMS_PROVIDER'], { + is: (EXTERNAL_PHONE_ENABLED: boolean, SMS_PROVIDER: string) => { + return EXTERNAL_PHONE_ENABLED && SMS_PROVIDER === 'textlocal' + }, + then: (schema) => schema.required('"Textlocal API Key" is required'), + otherwise: (schema) => schema, + }), + SMS_TEXTLOCAL_SENDER: string().when(['EXTERNAL_PHONE_ENABLED', 'SMS_PROVIDER'], { + is: (EXTERNAL_PHONE_ENABLED: boolean, SMS_PROVIDER: string) => { + return EXTERNAL_PHONE_ENABLED && SMS_PROVIDER === 'textlocal' + }, + then: (schema) => schema.required('"Textlocal Sender" is required'), + otherwise: (schema) => schema, + }), + /** + * vonage + */ + SMS_VONAGE_API_KEY: string().when(['EXTERNAL_PHONE_ENABLED', 'SMS_PROVIDER'], { + is: (EXTERNAL_PHONE_ENABLED: boolean, SMS_PROVIDER: string) => { + return EXTERNAL_PHONE_ENABLED && SMS_PROVIDER === 'vonage' + }, + then: (schema) => schema.required('"Vonage API" is required'), + otherwise: (schema) => schema, + }), + SMS_VONAGE_API_SECRET: string().when(['EXTERNAL_PHONE_ENABLED', 'SMS_PROVIDER'], { + is: (EXTERNAL_PHONE_ENABLED: boolean, SMS_PROVIDER: string) => { + return EXTERNAL_PHONE_ENABLED && SMS_PROVIDER === 'vonage' + }, + then: (schema) => schema.required('"Vonage API Secret" is required'), + otherwise: (schema) => schema, + }), + SMS_VONAGE_FROM: string().when(['EXTERNAL_PHONE_ENABLED', 'SMS_PROVIDER'], { + is: (EXTERNAL_PHONE_ENABLED: boolean, SMS_PROVIDER: string) => { + return EXTERNAL_PHONE_ENABLED && SMS_PROVIDER === 'vonage' + }, + then: (schema) => schema.required('"Vonage From" is required'), + otherwise: (schema) => schema, + }), + }), + misc: { + iconKey: 'phone-icon4', + helper: `To complete setup, add this authorisation callback URL to your app's configuration in the Apple Developer Console. + [Learn more](https://supabase.com/docs/guides/auth/auth-apple#configure-your-services-id)`, + }, +} + +export const EXTERNAL_PROVIDER_APPLE = { + $schema: JSON_SCHEMA_VERSION, + type: 'object', + title: 'Apple', + properties: { + EXTERNAL_APPLE_ENABLED: { + title: 'Enable Apple provider', + description: 'This will enable Apple login for your application', + type: 'boolean', + }, + EXTERNAL_APPLE_CLIENT_ID: { + /** + * to do: change docs + */ + title: 'Services ID', + description: ` +Client identifier when authenticating or validating users. +[learn more](https://developer.apple.com/documentation/sign_in_with_apple/configuring_your_environment_for_sign_in_with_apple)`, + type: 'string', + }, + EXTERNAL_APPLE_SECRET: { + /** + * to do: change docs + */ + title: 'Secret key', + description: ` +The secret key is a JWT token that must be generated. +[Learn more](https://supabase.com/docs/guides/auth/auth-apple#generate-a-client_secret)`, + type: 'string', + }, + }, + validationSchema: object().shape({ + EXTERNAL_APPLE_ENABLED: boolean().required(), + EXTERNAL_APPLE_CLIENT_ID: string().when('EXTERNAL_APPLE_ENABLED', { + is: true, + then: (schema) => schema.required('"Services ID" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_APPLE_SECRET: string().when('EXTERNAL_APPLE_ENABLED', { + is: true, + then: (schema) => schema.required('"Secret key" is required'), + otherwise: (schema) => schema, + }), + }), + misc: { + iconKey: 'apple-icon', + requiresRedirect: true, + helper: `To complete setup, add this authorisation callback URL to your app's configuration in the Apple Developer Console. + [Learn more](https://supabase.com/docs/guides/auth/auth-apple#configure-your-services-id)`, + alert: { + title: `Apple secrets will self expire every 6 months`, + description: `You will need to regenerate before the 6 months elapses otherwise your users using Apple Login will no longer be able to log back in.`, + }, + }, +} + +export const EXTERNAL_PROVIDER_AZURE = { + $schema: JSON_SCHEMA_VERSION, + type: 'object', + title: 'Azure', + properties: { + EXTERNAL_AZURE_ENABLED: { + title: 'Azure enabled', + type: 'boolean', + }, + EXTERNAL_AZURE_CLIENT_ID: { + /** + * to do: change docs + */ + title: 'Application (client) ID', + type: 'string', + }, + EXTERNAL_AZURE_SECRET: { + /** + * to do: change docs + */ + title: 'Secret ID', + type: 'string', + }, + EXTERNAL_AZURE_URL: { + /** + * to do: change docs + */ + title: 'Azure tenant URL', + descriptionOptional: 'Optional', + type: 'string', + }, + }, + validationSchema: object().shape({ + EXTERNAL_AZURE_ENABLED: boolean().required(), + EXTERNAL_AZURE_CLIENT_ID: string().when('EXTERNAL_AZURE_ENABLED', { + is: true, + then: (schema) => schema.required('"Application (client) ID" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_AZURE_SECRET: string().when('EXTERNAL_AZURE_ENABLED', { + is: true, + then: (schema) => schema.required('"Secret ID" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_AZURE_URL: string().url('Must be a valid URL').optional(), + }), + misc: { + iconKey: 'microsoft-icon', + requiresRedirect: true, + }, +} + +export const EXTERNAL_PROVIDER_BITBUCKET = { + $schema: JSON_SCHEMA_VERSION, + type: 'object', + title: 'Bitbucket', + properties: { + EXTERNAL_BITBUCKET_ENABLED: { + title: 'Bitbucket enabled', + type: 'boolean', + }, + EXTERNAL_BITBUCKET_CLIENT_ID: { + title: 'Key', + type: 'string', + }, + EXTERNAL_BITBUCKET_SECRET: { + title: 'Secret', + type: 'string', + }, + }, + validationSchema: object().shape({ + EXTERNAL_BITBUCKET_ENABLED: boolean().required(), + EXTERNAL_BITBUCKET_CLIENT_ID: string().when('EXTERNAL_BITBUCKET_ENABLED', { + is: true, + then: (schema) => schema.required('"Key" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_BITBUCKET_SECRET: string().when('EXTERNAL_BITBUCKET_ENABLED', { + is: true, + then: (schema) => schema.required('"Secret" is required'), + otherwise: (schema) => schema, + }), + }), + misc: { + iconKey: 'bitbucket-icon', + requiresRedirect: true, + }, +} + +export const EXTERNAL_PROVIDER_DISCORD = { + $schema: JSON_SCHEMA_VERSION, + type: 'object', + title: 'Discord', + properties: { + EXTERNAL_DISCORD_ENABLED: { + title: 'Discord enabled', + type: 'boolean', + }, + EXTERNAL_DISCORD_CLIENT_ID: { + title: 'Client ID', + type: 'string', + }, + EXTERNAL_DISCORD_SECRET: { + title: 'Client Secret', + type: 'string', + }, + }, + validationSchema: object().shape({ + EXTERNAL_DISCORD_ENABLED: boolean().required(), + EXTERNAL_DISCORD_CLIENT_ID: string().when('EXTERNAL_DISCORD_ENABLED', { + is: true, + then: (schema) => schema.required('"Client ID" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_DISCORD_SECRET: string().when('EXTERNAL_DISCORD_ENABLED', { + is: true, + then: (schema) => schema.required('"Client Secret" is required'), + otherwise: (schema) => schema, + }), + }), + misc: { + iconKey: 'discord-icon', + requiresRedirect: true, + }, +} + +export const EXTERNAL_PROVIDER_FACEBOOK = { + $schema: JSON_SCHEMA_VERSION, + type: 'object', + title: 'Facebook', + properties: { + EXTERNAL_FACEBOOK_ENABLED: { + title: 'Facebook enabled', + type: 'boolean', + }, + EXTERNAL_FACEBOOK_CLIENT_ID: { + title: 'Facebook client ID', + type: 'string', + }, + EXTERNAL_FACEBOOK_SECRET: { + title: 'Facebook secret', + type: 'string', + }, + }, + validationSchema: object().shape({ + EXTERNAL_FACEBOOK_ENABLED: boolean().required(), + EXTERNAL_FACEBOOK_CLIENT_ID: string().when('EXTERNAL_FACEBOOK_ENABLED', { + is: true, + then: (schema) => schema.required('"Facebook client ID" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_FACEBOOK_SECRET: string().when('EXTERNAL_FACEBOOK_ENABLED', { + is: true, + then: (schema) => schema.required('"Facebook secret" is required'), + otherwise: (schema) => schema, + }), + }), + misc: { + iconKey: 'facebook-icon', + requiresRedirect: true, + }, +} + +export const EXTERNAL_PROVIDER_GITHUB = { + $schema: JSON_SCHEMA_VERSION, + type: 'object', + title: 'GitHub', + properties: { + EXTERNAL_GITHUB_ENABLED: { + title: 'GitHub enabled', + type: 'boolean', + }, + EXTERNAL_GITHUB_CLIENT_ID: { + title: 'Client ID', + type: 'string', + }, + EXTERNAL_GITHUB_SECRET: { + title: 'Client Secret', + type: 'string', + }, + }, + validationSchema: object().shape({ + EXTERNAL_GITHUB_ENABLED: boolean().required(), + EXTERNAL_GITHUB_CLIENT_ID: string().when('EXTERNAL_GITHUB_ENABLED', { + is: true, + then: (schema) => schema.required('"Client ID" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_GITHUB_SECRET: string().when('EXTERNAL_GITHUB_ENABLED', { + is: true, + then: (schema) => schema.required('"Client Secret" is required'), + otherwise: (schema) => schema, + }), + }), + misc: { + iconKey: 'github-icon', + requiresRedirect: true, + }, +} + +export const EXTERNAL_PROVIDER_GITLAB = { + $schema: JSON_SCHEMA_VERSION, + type: 'object', + title: 'GitLab', + properties: { + EXTERNAL_GITLAB_ENABLED: { + title: 'GitLab enabled', + type: 'boolean', + }, + /** + * to do: update docs + */ + EXTERNAL_GITLAB_CLIENT_ID: { + title: 'Application ID', + type: 'string', + }, + /** + * to do: update docs + */ + EXTERNAL_GITLAB_SECRET: { + title: 'Secret', + type: 'string', + }, + EXTERNAL_GITLAB_URL: { + title: 'Self Hosted GitLab URL', + descriptionOptional: 'Optional', + type: 'string', + }, + }, + validationSchema: object().shape({ + EXTERNAL_GITLAB_ENABLED: boolean().required(), + EXTERNAL_GITLAB_CLIENT_ID: string().when('EXTERNAL_GITLAB_ENABLED', { + is: true, + then: (schema) => schema.required('"Client ID" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_GITLAB_SECRET: string().when('EXTERNAL_GITLAB_ENABLED', { + is: true, + then: (schema) => schema.required('"Client Secret" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_GITLAB_URL: string().url('Not a valid URL').optional(), + }), + misc: { + iconKey: 'gitlab-icon', + requiresRedirect: true, + }, +} + +export const EXTERNAL_PROVIDER_GOOGLE = { + $schema: JSON_SCHEMA_VERSION, + type: 'object', + title: 'Google', + properties: { + EXTERNAL_GOOGLE_ENABLED: { + title: 'Google enabled', + type: 'boolean', + }, + /** + * to do: update docs + */ + EXTERNAL_GOOGLE_CLIENT_ID: { + title: 'Client ID', + type: 'string', + }, + /** + * to do: update docs + */ + EXTERNAL_GOOGLE_SECRET: { + title: 'Client Secret', + type: 'string', + }, + }, + validationSchema: object().shape({ + EXTERNAL_GOOGLE_ENABLED: boolean().required(), + EXTERNAL_GOOGLE_CLIENT_ID: string().when('EXTERNAL_GOOGLE_ENABLED', { + is: true, + then: (schema) => schema.required('"Client ID" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_GOOGLE_SECRET: string().when('EXTERNAL_GOOGLE_ENABLED', { + is: true, + then: (schema) => schema.required('"Client Secret" is required'), + otherwise: (schema) => schema, + }), + }), + misc: { + iconKey: 'google-icon', + requiresRedirect: true, + }, +} + +/** + * TO DO: clarify the EXTERNAL_KEYCLOAK_URL property + */ +export const EXTERNAL_PROVIDER_KEYCLOAK = { + $schema: JSON_SCHEMA_VERSION, + type: 'object', + title: 'KeyCloak', + properties: { + EXTERNAL_KEYCLOAK_ENABLED: { + title: 'Keycloak enabled', + type: 'boolean', + }, + EXTERNAL_KEYCLOAK_CLIENT_ID: { + title: 'Client ID', + type: 'string', + }, + EXTERNAL_KEYCLOAK_SECRET: { + title: 'Secret', + type: 'string', + }, + EXTERNAL_KEYCLOAK_URL: { + title: 'Realm URL', + type: 'string', + }, + }, + validationSchema: object().shape({ + EXTERNAL_KEYCLOAK_ENABLED: boolean().required(), + EXTERNAL_KEYCLOAK_CLIENT_ID: string().when('EXTERNAL_KEYCLOAK_ENABLED', { + is: true, + then: (schema) => schema.required('"Client ID" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_KEYCLOAK_SECRET: string().when('EXTERNAL_KEYCLOAK_ENABLED', { + is: true, + then: (schema) => schema.required('"Client Secret" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_KEYCLOAK_URL: string().when('EXTERNAL_KEYCLOAK_ENABLED', { + is: true, + then: (schema) => schema.required('"Realm URL" is required').url(), + otherwise: (schema) => schema.url(), + }), + }), + misc: { + iconKey: 'keycloak-icon', + requiresRedirect: true, + }, +} + +export const EXTERNAL_PROVIDER_LINKEDIN = { + $schema: JSON_SCHEMA_VERSION, + type: 'object', + title: 'LinkedIn', + properties: { + EXTERNAL_LINKEDIN_ENABLED: { + title: 'Linkedin enabled', + type: 'boolean', + }, + /** + * to do: change in docs + */ + EXTERNAL_LINKEDIN_CLIENT_ID: { + title: 'API Key', + type: 'string', + }, + /** + * to do: change in docs + */ + EXTERNAL_LINKEDIN_SECRET: { + title: 'API Secret Key', + type: 'string', + }, + }, + validationSchema: object().shape({ + EXTERNAL_LINKEDIN_ENABLED: boolean().required(), + EXTERNAL_LINKEDIN_CLIENT_ID: string().when('EXTERNAL_LINKEDIN_ENABLED', { + is: true, + then: (schema) => schema.required('"API Key" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_LINKEDIN_SECRET: string().when('EXTERNAL_LINKEDIN_ENABLED', { + is: true, + then: (schema) => schema.required('"API Secret Key" is required'), + otherwise: (schema) => schema, + }), + }), + misc: { + iconKey: 'linkedin-icon', + requiresRedirect: true, + }, +} + +export const EXTERNAL_PROVIDER_NOTION = { + $schema: JSON_SCHEMA_VERSION, + type: 'object', + title: 'Notion', + properties: { + EXTERNAL_NOTION_ENABLED: { + title: 'Notion enabled', + type: 'boolean', + }, + EXTERNAL_NOTION_CLIENT_ID: { + title: 'OAuth client ID', + type: 'string', + }, + EXTERNAL_NOTION_SECRET: { + title: 'OAuth client secret', + type: 'string', + }, + }, + validationSchema: object().shape({ + EXTERNAL_NOTION_ENABLED: boolean().required(), + EXTERNAL_NOTION_CLIENT_ID: string().when('EXTERNAL_NOTION_ENABLED', { + is: true, + then: (schema) => schema.required('"OAuth client ID" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_NOTION_SECRET: string().when('EXTERNAL_NOTION_ENABLED', { + is: true, + then: (schema) => schema.required('"OAuth client secret" is required'), + otherwise: (schema) => schema, + }), + }), + misc: { + iconKey: 'notion-icon', + requiresRedirect: true, + }, +} + +export const EXTERNAL_PROVIDER_TWITCH = { + $schema: JSON_SCHEMA_VERSION, + type: 'object', + title: 'twitch', + properties: { + EXTERNAL_TWITCH_ENABLED: { + title: 'Twitch enabled', + type: 'boolean', + }, + EXTERNAL_TWITCH_CLIENT_ID: { + title: 'Client ID', + type: 'string', + }, + EXTERNAL_TWITCH_SECRET: { + title: 'Client secret', + type: 'string', + }, + }, + validationSchema: object().shape({ + EXTERNAL_TWITCH_ENABLED: boolean().required(), + EXTERNAL_TWITCH_CLIENT_ID: string().when('EXTERNAL_TWITCH_ENABLED', { + is: true, + then: (schema) => schema.required('"Client ID" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_TWITCH_SECRET: string().when('EXTERNAL_TWITCH_ENABLED', { + is: true, + then: (schema) => schema.required('"Client secret" is required'), + otherwise: (schema) => schema, + }), + }), + misc: { + iconKey: 'twitch-icon', + requiresRedirect: true, + }, +} + +export const EXTERNAL_PROVIDER_TWITTER = { + $schema: JSON_SCHEMA_VERSION, + type: 'object', + title: 'Twitter', + properties: { + EXTERNAL_TWITTER_ENABLED: { + title: 'Twitter enabled', + type: 'boolean', + }, + EXTERNAL_TWITTER_CLIENT_ID: { + title: 'API Key', + type: 'string', + }, + EXTERNAL_TWITTER_SECRET: { + title: 'API Secret Key', + type: 'string', + }, + }, + validationSchema: object().shape({ + EXTERNAL_TWITTER_ENABLED: boolean().required(), + EXTERNAL_TWITTER_CLIENT_ID: string().when('EXTERNAL_TWITTER_ENABLED', { + is: true, + then: (schema) => schema.required('"API Key" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_TWITTER_SECRET: string().when('EXTERNAL_TWITTER_ENABLED', { + is: true, + then: (schema) => schema.required('"API Secret Key" is required'), + otherwise: (schema) => schema, + }), + }), + misc: { + iconKey: 'twitter-icon', + requiresRedirect: true, + }, +} + +export const EXTERNAL_PROVIDER_SLACK = { + $schema: JSON_SCHEMA_VERSION, + type: 'object', + title: 'Slack', + properties: { + EXTERNAL_SLACK_ENABLED: { + title: 'Slack enabled', + type: 'boolean', + }, + EXTERNAL_SLACK_CLIENT_ID: { + title: 'Client ID', + type: 'string', + }, + EXTERNAL_SLACK_SECRET: { + title: 'Client Secret', + type: 'string', + }, + }, + validationSchema: object().shape({ + EXTERNAL_SLACK_ENABLED: boolean().required(), + EXTERNAL_SLACK_CLIENT_ID: string().when('EXTERNAL_SLACK_ENABLED', { + is: true, + then: (schema) => schema.required('"Client ID" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_SLACK_SECRET: string().when('EXTERNAL_SLACK_ENABLED', { + is: true, + then: (schema) => schema.required('"Client Secret" is required'), + otherwise: (schema) => schema, + }), + }), + misc: { + iconKey: 'slack-icon', + requiresRedirect: true, + }, +} + +export const EXTERNAL_PROVIDER_SPOTIFY = { + $schema: JSON_SCHEMA_VERSION, + type: 'object', + title: 'Spotify', + properties: { + EXTERNAL_SPOTIFY_ENABLED: { + title: 'Spotify enabled', + type: 'boolean', + }, + EXTERNAL_SPOTIFY_CLIENT_ID: { + title: 'Client ID', + type: 'string', + }, + EXTERNAL_SPOTIFY_SECRET: { + title: 'Client Secret', + type: 'string', + }, + }, + validationSchema: object().shape({ + EXTERNAL_SPOTIFY_ENABLED: boolean().required(), + EXTERNAL_SPOTIFY_CLIENT_ID: string().when('EXTERNAL_SPOTIFY_ENABLED', { + is: true, + then: (schema) => schema.required('"Client ID" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_SPOTIFY_SECRET: string().when('EXTERNAL_SPOTIFY_ENABLED', { + is: true, + then: (schema) => schema.required('"Client Secret" is required'), + otherwise: (schema) => schema, + }), + }), + misc: { + iconKey: 'spotify-icon', + requiresRedirect: true, + }, +} + +export const EXTERNAL_PROVIDER_WORKOS = { + $schema: JSON_SCHEMA_VERSION, + type: 'object', + title: 'WorkOS', + properties: { + EXTERNAL_WORKOS_ENABLED: { + title: 'WorkOS enabled', + type: 'boolean', + }, + /** + * to do: not sure why we have this. + */ + EXTERNAL_WORKOS_URL: { + title: 'WorkOS URL', + type: 'string', + }, + EXTERNAL_WORKOS_CLIENT_ID: { + title: 'Client ID', + type: 'string', + }, + EXTERNAL_WORKOS_SECRET: { + title: 'Secret Key', + type: 'string', + }, + }, + validationSchema: object().shape({ + EXTERNAL_WORKOS_ENABLED: boolean().required(), + EXTERNAL_WORKOS_URL: string() + .url('WorkOS URL must be a valid URL') + .when('EXTERNAL_WORKOS_ENABLED', { + is: true, + then: (schema) => schema.required('"WorkOS URL" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_WORKOS_CLIENT_ID: string().when('EXTERNAL_WORKOS_ENABLED', { + is: true, + then: (schema) => schema.required('"Client ID" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_WORKOS_SECRET: string().when('EXTERNAL_WORKOS_ENABLED', { + is: true, + then: (schema) => schema.required('"Client Secret" is required'), + otherwise: (schema) => schema, + }), + }), + misc: { + iconKey: 'workos-icon', + requiresRedirect: true, + }, +} + +export const EXTERNAL_PROVIDER_ZOOM = { + $schema: JSON_SCHEMA_VERSION, + type: 'object', + title: 'zoom', + properties: { + EXTERNAL_ZOOM_ENABLED: { + title: 'Zoom enabled', + type: 'boolean', + }, + EXTERNAL_ZOOM_CLIENT_ID: { + title: 'Client ID', + type: 'string', + }, + EXTERNAL_ZOOM_SECRET: { + title: 'Client secret', + type: 'string', + }, + }, + validationSchema: object().shape({ + EXTERNAL_ZOOM_ENABLED: boolean().required(), + EXTERNAL_ZOOM_CLIENT_ID: string().when('EXTERNAL_ZOOM_ENABLED', { + is: true, + then: (schema) => schema.required('"Client ID" is required'), + otherwise: (schema) => schema, + }), + EXTERNAL_ZOOM_SECRET: string().when('EXTERNAL_ZOOM_ENABLED', { + is: true, + then: (schema) => schema.required('"Client secret" is required'), + otherwise: (schema) => schema, + }), + }), + misc: { + iconKey: 'zoom-icon', + requiresRedirect: true, + }, +} + +export const OLD = { + type: 'object', + required: [ + 'SITE_URL', + 'DISABLE_SIGNUP', + 'JWT_EXP', + 'JWT_AUD', + 'JWT_DEFAULT_GROUP_NAME', + 'SECURITY_UPDATE_PASSWORD_REQUIRE_REAUTHENTICATION', + 'EXTERNAL_EMAIL_ENABLED', + 'EXTERNAL_PHONE_ENABLED', + 'EXTERNAL_APPLE_ENABLED', + 'EXTERNAL_AZURE_ENABLED', + 'EXTERNAL_BITBUCKET_ENABLED', + 'EXTERNAL_DISCORD_ENABLED', + 'EXTERNAL_FACEBOOK_ENABLED', + 'EXTERNAL_GITHUB_ENABLED', + 'EXTERNAL_GITLAB_ENABLED', + 'EXTERNAL_GOOGLE_ENABLED', + 'EXTERNAL_KEYCLOAK_ENABLED', + 'EXTERNAL_LINKEDIN_ENABLED', + 'EXTERNAL_NOTION_ENABLED', + 'EXTERNAL_TWITCH_ENABLED', + 'EXTERNAL_TWITTER_ENABLED', + 'EXTERNAL_SLACK_ENABLED', + 'EXTERNAL_SPOTIFY_ENABLED', + 'EXTERNAL_WORKOS_ENABLED', + 'EXTERNAL_ZOOM_ENABLED', + 'SMS_AUTOCONFIRM', + 'SMS_MAX_FREQUENCY', + 'SMS_OTP_EXP', + 'SMS_OTP_LENGTH', + 'SMS_PROVIDER', + 'SMS_TEMPLATE', + 'MAILER_AUTOCONFIRM', + 'MAILER_URLPATHS_INVITE', + 'MAILER_URLPATHS_CONFIRMATION', + 'MAILER_URLPATHS_RECOVERY', + 'MAILER_URLPATHS_EMAIL_CHANGE', + 'MAILER_SUBJECTS_INVITE', + 'MAILER_SUBJECTS_CONFIRMATION', + 'MAILER_SUBJECTS_RECOVERY', + 'MAILER_SUBJECTS_MAGIC_LINK', + 'MAILER_SUBJECTS_EMAIL_CHANGE', + 'MAILER_TEMPLATES_INVITE_CONTENT', + 'MAILER_TEMPLATES_CONFIRMATION_CONTENT', + 'MAILER_TEMPLATES_RECOVERY_CONTENT', + 'MAILER_TEMPLATES_MAGIC_LINK_CONTENT', + 'MAILER_TEMPLATES_EMAIL_CHANGE_CONTENT', + ], + properties: { + SITE_URL: { + title: 'Site URL', + type: 'string', + help: 'The base URL of your website. Used as an allow-list for redirects and for constructing URLs used in emails.', + }, + URI_ALLOW_LIST: { + title: 'Additional redirect URLs', + type: 'string', + help: 'A comma separated list of *exact* URLs that auth providers are permitted to redirect to post authentication.', + }, + DISABLE_SIGNUP: { + title: 'Disable signup', + type: 'boolean', + help: 'Allow/disallow new user signups to your project.', + }, + EXTERNAL_EMAIL_ENABLED: { + title: 'Enable email signup', + type: 'boolean', + help: 'Allow/disallow new user signups via email to your project.', + }, + EXTERNAL_PHONE_ENABLED: { + title: 'Enable phone signup', + type: 'boolean', + help: 'Allow/disallow new user signups via phone to your project.', + }, + JWT_EXP: { + title: 'JWT expiry', + type: 'integer', + help: 'How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 seconds (one week).', + minimum: 1, + maximum: 604800, + multipleof: 1, + }, + JWT_AUD: { + title: 'JWT audience', + type: 'string', + }, + JWT_DEFAULT_GROUP_NAME: { + title: 'Default user group', + type: 'string', + }, + EXTERNAL_APPLE_ENABLED: { + title: 'Apple enabled', + type: 'boolean', + }, + EXTERNAL_APPLE_CLIENT_ID: { + title: 'Apple client ID', + type: 'string', + }, + EXTERNAL_APPLE_SECRET: { + title: 'Apple secret', + type: 'string', + }, + EXTERNAL_AZURE_ENABLED: { + title: 'Azure enabled', + type: 'boolean', + }, + EXTERNAL_AZURE_CLIENT_ID: { + title: 'Azure client ID', + type: 'string', + }, + EXTERNAL_AZURE_SECRET: { + title: 'Azure secret', + type: 'string', + }, + EXTERNAL_AZURE_URL: { + title: 'Azure tenant URL', + type: 'string', + }, + EXTERNAL_BITBUCKET_ENABLED: { + title: 'Bitbucket enabled', + type: 'boolean', + }, + EXTERNAL_BITBUCKET_CLIENT_ID: { + title: 'Bitbucket client ID', + type: 'string', + }, + EXTERNAL_BITBUCKET_SECRET: { + title: 'Bitbucket secret', + type: 'string', + }, + EXTERNAL_DISCORD_ENABLED: { + title: 'Discord enabled', + type: 'boolean', + }, + EXTERNAL_DISCORD_CLIENT_ID: { + title: 'Discord client ID', + type: 'string', + }, + EXTERNAL_DISCORD_SECRET: { + title: 'Discord secret', + type: 'string', + }, + EXTERNAL_FACEBOOK_ENABLED: { + title: 'Facebook enabled', + type: 'boolean', + }, + EXTERNAL_FACEBOOK_CLIENT_ID: { + title: 'Facebook client ID', + type: 'string', + }, + EXTERNAL_FACEBOOK_SECRET: { + title: 'Facebook secret', + type: 'string', + }, + EXTERNAL_GITHUB_ENABLED: { + title: 'GitHub enabled', + type: 'boolean', + }, + EXTERNAL_GITHUB_CLIENT_ID: { + title: 'GitHub client ID', + type: 'string', + }, + EXTERNAL_GITHUB_SECRET: { + title: 'GitHub secret', + type: 'string', + }, + EXTERNAL_GITLAB_ENABLED: { + title: 'GitLab enabled', + type: 'boolean', + }, + EXTERNAL_GITLAB_CLIENT_ID: { + title: 'GitLab client ID', + type: 'string', + }, + EXTERNAL_GITLAB_SECRET: { + title: 'Gitlab secret', + type: 'string', + }, + EXTERNAL_GITLAB_URL: { + title: + 'The base URL used for constructing the URLs to request authorization and access tokens.', + type: 'string', + }, + EXTERNAL_GOOGLE_ENABLED: { + title: 'Google enabled', + type: 'boolean', + }, + EXTERNAL_GOOGLE_CLIENT_ID: { + title: 'Google client ID', + type: 'string', + }, + EXTERNAL_GOOGLE_SECRET: { + title: 'Google secret', + type: 'string', + }, + EXTERNAL_KEYCLOAK_ENABLED: { + title: 'Keycloak enabled', + type: 'boolean', + }, + EXTERNAL_KEYCLOAK_CLIENT_ID: { + title: 'Keycloak client ID', + type: 'string', + }, + EXTERNAL_KEYCLOAK_SECRET: { + title: 'Keycloak secret', + type: 'string', + }, + EXTERNAL_KEYCLOAK_URL: { + title: 'Keycloak URL', + type: 'string', + }, + EXTERNAL_LINKEDIN_ENABLED: { + title: 'Linkedin enabled', + type: 'boolean', + }, + EXTERNAL_LINKEDIN_CLIENT_ID: { + title: 'Linkedin client ID', + type: 'string', + }, + EXTERNAL_LINKEDIN_SECRET: { + title: 'Linkedin secret', + type: 'string', + }, + EXTERNAL_NOTION_ENABLED: { + title: 'Notion enabled', + type: 'boolean', + }, + EXTERNAL_NOTION_CLIENT_ID: { + title: 'Notion client ID', + type: 'string', + }, + EXTERNAL_NOTION_SECRET: { + title: 'Notion secret', + type: 'string', + }, + EXTERNAL_TWITCH_ENABLED: { + title: 'Twitch enabled', + type: 'boolean', + }, + EXTERNAL_TWITCH_CLIENT_ID: { + title: 'Twitch client ID', + type: 'string', + }, + EXTERNAL_TWITCH_SECRET: { + title: 'Twitch secret', + type: 'string', + }, + EXTERNAL_TWITTER_ENABLED: { + title: 'Twitter enabled', + type: 'boolean', + }, + EXTERNAL_TWITTER_CLIENT_ID: { + title: 'Twitter client ID', + type: 'string', + }, + EXTERNAL_TWITTER_SECRET: { + title: 'Twitter secret', + type: 'string', + }, + EXTERNAL_SLACK_ENABLED: { + title: 'Slack enabled', + type: 'boolean', + }, + EXTERNAL_SLACK_CLIENT_ID: { + title: 'Slack client ID', + type: 'string', + }, + EXTERNAL_SLACK_SECRET: { + title: 'Slack secret', + type: 'string', + }, + EXTERNAL_SPOTIFY_ENABLED: { + title: 'Spotify enabled', + type: 'boolean', + }, + EXTERNAL_SPOTIFY_CLIENT_ID: { + title: 'Spotify client ID', + type: 'string', + }, + EXTERNAL_SPOTIFY_SECRET: { + title: 'Spotify secret', + type: 'string', + }, + EXTERNAL_WORKOS_ENABLED: { + title: 'WorkOS enabled', + type: 'boolean', + }, + EXTERNAL_WORKOS_CLIENT_ID: { + title: 'WorkOS client ID', + type: 'string', + }, + EXTERNAL_WORKOS_SECRET: { + title: 'WorkOS secret', + type: 'string', + }, + EXTERNAL_WORKOS_URL: { + title: 'WorkOS URL', + type: 'string', + }, + EXTERNAL_ZOOM_ENABLED: { + title: 'Zoom enabled', + type: 'boolean', + }, + EXTERNAL_ZOOM_CLIENT_ID: { + title: 'Zoom client ID', + type: 'string', + }, + EXTERNAL_ZOOM_SECRET: { + title: 'Zoom secret', + type: 'string', + }, + SMTP_ADMIN_EMAIL: { + title: 'SMTP admin email', + type: 'string', + }, + SMTP_HOST: { + title: 'SMTP host', + type: 'string', + }, + SMTP_PORT: { + title: 'SMTP port', + type: 'string', + }, + SMTP_USER: { + title: 'SMTP user', + type: 'string', + }, + SMTP_PASS: { + title: 'SMTP password', + type: 'string', + }, + SMTP_PASS_ENCRYPTED: { + title: 'SMTP password', + type: 'string', + }, + SMTP_SENDER_NAME: { + title: 'SMTP sender name', + type: 'string', + }, + RATE_LIMIT_EMAIL_SENT: { + title: 'Rate limit', + type: 'number', + help: 'Maximum number of emails sent per hour (Default: 30, Max: 32,767)', + minimum: 1, + maximum: 32767, + multipleof: 1, + }, + MAILER_SECURE_EMAIL_CHANGE_ENABLED: { + title: 'Double confirm email changes', + type: 'boolean', + help: 'If enabled, a user will be required to confirm any email change on both the old, and new email addresses. If disabled, only the new email is required to confirm', + }, + MAILER_AUTOCONFIRM: { + title: 'Enable email confirmations', + type: 'boolean', + help: 'If enabled, users need to confirm their email address before signing in.', + }, + MAILER_URLPATHS_INVITE: { + title: 'Confirmation URL', + type: 'string', + }, + MAILER_URLPATHS_CONFIRMATION: { + title: 'Path', + type: 'string', + }, + MAILER_URLPATHS_RECOVERY: { + title: 'Confirmation URL', + type: 'string', + }, + MAILER_URLPATHS_EMAIL_CHANGE: { + title: 'Confirmation URL', + type: 'string', + }, + MAILER_SUBJECTS_INVITE: { + title: 'Subject', + type: 'string', + }, + MAILER_SUBJECTS_CONFIRMATION: { + title: 'Subject', + type: 'string', + }, + MAILER_SUBJECTS_MAGIC_LINK: { + title: 'Subject', + type: 'string', + }, + MAILER_SUBJECTS_RECOVERY: { + title: 'Subject', + type: 'string', + }, + MAILER_SUBJECTS_EMAIL_CHANGE: { + title: 'Subject', + type: 'string', + }, + MAILER_TEMPLATES_INVITE: { + type: 'string', + }, + MAILER_TEMPLATES_INVITE_CONTENT: { + title: 'Body', + type: 'string', + }, + MAILER_TEMPLATES_CONFIRMATION: { + type: 'string', + }, + MAILER_TEMPLATES_CONFIRMATION_CONTENT: { + title: 'Body', + type: 'string', + }, + MAILER_TEMPLATES_RECOVERY: { + type: 'string', + }, + MAILER_TEMPLATES_MAGIC_LINK: { + type: 'string', + }, + MAILER_TEMPLATES_RECOVERY_CONTENT: { + title: 'Body', + type: 'string', + }, + MAILER_TEMPLATES_MAGIC_LINK_CONTENT: { + title: 'Body', + type: 'string', + }, + MAILER_TEMPLATES_EMAIL_CHANGE: { + type: 'string', + }, + MAILER_TEMPLATES_EMAIL_CHANGE_CONTENT: { + title: 'Body', + type: 'string', + }, + PASSWORD_MIN_LENGTH: { + title: 'Minimum password length', + type: 'integer', + }, + SECURITY_UPDATE_PASSWORD_REQUIRE_REAUTHENTICATION: { + title: 'Enable strict password updates', + type: 'boolean', + help: 'If enabled, users need to reauthenticate via email or phone before updating their passwords.', + }, + SMS_AUTOCONFIRM: { + title: 'Enable phone confirmations', + type: 'boolean', + help: 'If enabled, users need to confirm their phone number before signing in.', + }, + SMS_PROVIDER: { + title: 'Sms provider', + type: 'string', + options: [ + { + value: 'twilio', + label: 'Twilio', + }, + { + value: 'messagebird', + label: 'Messagebird', + }, + { + value: 'vonage', + label: 'Vonage', + }, + { + value: 'textlocal', + label: 'Textlocal', + }, + ], + }, + SMS_TEXTLOCAL_API_KEY: { + title: 'Textlocal API key', + type: 'string', + }, + SMS_TEXTLOCAL_SENDER: { + title: 'Textlocal sender', + type: 'string', + help: 'Textlocal sender phone number', + }, + SMS_TWILIO_ACCOUNT_SID: { + title: 'Twilio account SID', + type: 'string', + }, + SMS_TWILIO_AUTH_TOKEN: { + title: 'Twilio auth token', + type: 'string', + }, + SMS_TWILIO_MESSAGE_SERVICE_SID: { + title: 'Twilio message service SID', + type: 'string', + help: 'Twilio message service SID or twilio phone number', + }, + SMS_MESSAGEBIRD_ACCESS_KEY: { + title: 'Messagebird access key', + type: 'string', + }, + SMS_MESSAGEBIRD_ORIGINATOR: { + title: 'Messagebird originator', + type: 'string', + help: 'Messagebird sender name or phone number', + }, + SMS_VONAGE_API_KEY: { + title: 'Vonage API key', + type: 'string', + }, + SMS_VONAGE_API_SECRET: { + title: 'Vonage API secret', + type: 'string', + }, + SMS_VONAGE_FROM: { + title: 'Vonage sender', + type: 'string', + help: 'Vonage sender phone number', + }, + SMS_TEMPLATE: { + title: 'Body', + type: 'string', + }, + }, +} + +export const PROVIDERS_SCHEMAS = [ + PROVIDER_EMAIL, + PROVIDER_PHONE, + + EXTERNAL_PROVIDER_APPLE, + EXTERNAL_PROVIDER_AZURE, + EXTERNAL_PROVIDER_BITBUCKET, + EXTERNAL_PROVIDER_DISCORD, + EXTERNAL_PROVIDER_FACEBOOK, + EXTERNAL_PROVIDER_GITHUB, + EXTERNAL_PROVIDER_GITLAB, + EXTERNAL_PROVIDER_GOOGLE, + EXTERNAL_PROVIDER_KEYCLOAK, + EXTERNAL_PROVIDER_LINKEDIN, + EXTERNAL_PROVIDER_NOTION, + EXTERNAL_PROVIDER_TWITCH, + EXTERNAL_PROVIDER_TWITTER, + EXTERNAL_PROVIDER_SLACK, + EXTERNAL_PROVIDER_SPOTIFY, + EXTERNAL_PROVIDER_WORKOS, + EXTERNAL_PROVIDER_ZOOM, +] diff --git a/studio/stores/authConfig/schema/index.ts b/studio/stores/authConfig/schema/index.ts new file mode 100644 index 00000000000..e9b8d674cb5 --- /dev/null +++ b/studio/stores/authConfig/schema/index.ts @@ -0,0 +1 @@ +export { PROVIDERS_SCHEMAS } from './AuthProviders/AuthProvidersFormValidation'