Merge pull request #904 from appwrite/fix-messaging-smtp-provider

Fix Email providers
This commit is contained in:
Torsten Dittmann
2024-03-01 13:56:40 +01:00
committed by GitHub
10 changed files with 302 additions and 202 deletions
+2 -1
View File
@@ -15,6 +15,7 @@
export let autocomplete = false;
export let maxlength: number = null;
export let isPopoverDefined = true;
export let fullWidth = false;
// https://www.geeksforgeeks.org/how-to-validate-a-domain-name-using-regular-expression/
const pattern = String.raw`(?!-)[A-Za-z0-9\-]+([\-\.]{1}[a-z0-9]+)*\.[A-Za-z]{2,18}`;
@@ -49,7 +50,7 @@
}
</script>
<FormItem>
<FormItem {fullWidth}>
<Label {required} hide={!showLabel} for={id}>
{label}{#if $$slots.popover && isPopoverDefined}
<Drop bind:show display="inline-block">
+2 -1
View File
@@ -18,6 +18,7 @@
export let autocomplete = false;
export let tooltip: string = null;
export let isPopoverDefined = true;
export let fullWidth = false;
let element: HTMLInputElement;
let error: string;
@@ -58,7 +59,7 @@
}
</script>
<FormItem>
<FormItem {fullWidth}>
<Label {required} {optionalText} {tooltip} hide={!showLabel} for={id}>
{label}{#if $$slots.popover && isPopoverDefined}
<Drop bind:show display="inline-block">
+2 -1
View File
@@ -17,6 +17,7 @@
export let minlength = 8;
export let maxlength: number = null;
export let isPopoverDefined = true;
export let fullWidth = false;
let element: HTMLInputElement;
let error: string;
@@ -49,7 +50,7 @@
}
</script>
<FormItem>
<FormItem {fullWidth}>
<Label {required} hide={!showLabel} for={id}>
{label}{#if $$slots.popover && isPopoverDefined}
<Drop bind:show={showPopover} display="inline-block">
+2 -1
View File
@@ -15,6 +15,7 @@
export let autocomplete = false;
export let maxlength: number = null;
export let isPopoverDefined = true;
export let fullWidth = false;
const pattern = String.raw`^\+?[1-9]\d{1,14}$`;
@@ -48,7 +49,7 @@
}
</script>
<FormItem>
<FormItem {fullWidth}>
<Label {required} hide={!showLabel} for={id}>
{label}{#if $$slots.popover && isPopoverDefined}
<Drop bind:show display="inline-block">
@@ -13,12 +13,13 @@
import { onMount } from 'svelte';
import CreateMember from '$routes/console/organization-[organization]/createMember.svelte';
export let inputs: ProviderInput[];
export let inputs: (ProviderInput | ProviderInput[])[];
export let providerType: MessagingProviderType;
export let provider: Providers;
export let providerId: string;
export let params: object;
let flattened: ProviderInput[] = [];
let formValues: object = {};
let originalfiles: Record<string, FileList> = {};
let files: Record<string, FileList> = {};
@@ -41,7 +42,15 @@
}
onMount(() => {
for (const input of inputs) {
flattened = [];
for (const i of inputs) {
if (Array.isArray(i)) {
flattened.push(...i);
} else {
flattened.push(i);
}
}
for (const input of flattened) {
const value = params[input.name];
formValues[input.name] = value;
if (input.type === 'file' && typeof value == 'string' && value.length > 0) {
@@ -198,7 +207,7 @@
}
$: updated =
inputs
flattened
.filter((input) => input.type !== 'file')
.some((input) => formValues[input.name] !== params[input.name]) ||
Object.keys(files).some((key) => files[key] !== originalfiles[key]);
@@ -0,0 +1,147 @@
<script lang="ts">
import {
InputDomain,
InputEmail,
InputFile,
InputSelect,
InputPhone,
InputSwitch,
InputText,
InputPassword
} from '$lib/elements/forms';
import InputCheckbox from '$lib/elements/forms/inputCheckbox.svelte';
import PopoverContent from './popoverContent.svelte';
import type {
ProviderInput,
TwilioProviderParams,
Msg91ProviderParams,
TelesignProviderParams,
TextmagicProviderParams,
VonageProviderParams,
MailgunProviderParams,
SendgridProviderParams,
SMTPProviderParams,
FCMProviderParams,
APNSProviderParams
} from './store';
export let files: Record<string, FileList>;
export let input: ProviderInput;
export let params: Partial<
| TwilioProviderParams
| Msg91ProviderParams
| TelesignProviderParams
| TextmagicProviderParams
| VonageProviderParams
| MailgunProviderParams
| SendgridProviderParams
| SMTPProviderParams
| FCMProviderParams
| APNSProviderParams
>;
export let fullWidth = false;
let popoverImage = input.popoverImage;
</script>
{#if input.type === 'text'}
<InputText
id={input.name}
label={input.label}
placeholder={input.placeholder}
required={!input.optional}
tooltip={input.tooltip}
isPopoverDefined={input.popover !== undefined}
{fullWidth}
bind:value={params[input.name]}>
<svelte:fragment slot="popover">
<PopoverContent lines={input.popover} {popoverImage} />
</svelte:fragment>
</InputText>
{:else if input.type === 'password'}
<InputPassword
id={input.name}
label={input.label}
placeholder={input.placeholder}
required={!input.optional}
showPasswordButton
isPopoverDefined={input.popover !== undefined}
{fullWidth}
bind:value={params[input.name]}>
<svelte:fragment slot="popover">
<PopoverContent lines={input.popover} {popoverImage} />
</svelte:fragment>
</InputPassword>
{:else if input.type === 'email'}
<InputEmail
id={input.name}
label={input.label}
placeholder={input.placeholder}
required={!input.optional}
tooltip={input.tooltip}
isPopoverDefined={input.popover !== undefined}
{fullWidth}
bind:value={params[input.name]}>
<svelte:fragment slot="popover">
<PopoverContent lines={input.popover} {popoverImage} />
</svelte:fragment>
</InputEmail>
{:else if input.type === 'domain'}
<InputDomain
id={input.name}
label={input.label}
placeholder={input.placeholder}
required={!input.optional}
isPopoverDefined={input.popover !== undefined}
{fullWidth}
bind:value={params[input.name]}>
<svelte:fragment slot="popover">
<PopoverContent lines={input.popover} {popoverImage} />
</svelte:fragment>
</InputDomain>
{:else if input.type === 'phone'}
<InputPhone
id={input.name}
label={input.label}
placeholder={input.placeholder}
required={!input.optional}
isPopoverDefined={input.popover !== undefined}
{fullWidth}
bind:value={params[input.name]}>
<svelte:fragment slot="popover">
<PopoverContent lines={input.popover} {popoverImage} />
</svelte:fragment>
</InputPhone>
{:else if input.type === 'file'}
<InputFile
label={input.label}
allowedFileExtensions={input.allowedFileExtensions}
required={!input.optional}
tooltip={input.tooltip}
isPopoverDefined={input.popover !== undefined}
bind:files={files[input.name]}>
{fullWidth}
<svelte:fragment slot="popover">
<PopoverContent lines={input.popover} {popoverImage} />
</svelte:fragment>
</InputFile>
{:else if input.type === 'switch'}
<InputSwitch label={input.label} id={input.name} bind:value={params[input.name]}>
<svelte:fragment slot="description">
<p>{@html input.description}</p>
</svelte:fragment>
</InputSwitch>
{:else if input.type === 'checkbox'}
<InputCheckbox label={input.label} id={input.name} bind:checked={params[input.name]}>
<svelte:fragment slot="description">
{input.description}
</svelte:fragment>
</InputCheckbox>
{:else if input.type === 'select'}
<InputSelect
label={input.label}
id={input.name}
options={input.options}
required={!input.optional}
bind:value={params[input.name]} />
{/if}
@@ -1,17 +1,6 @@
<script lang="ts">
import {
FormList,
InputDomain,
InputEmail,
InputFile,
InputSelect,
InputPhone,
InputSwitch,
InputText,
InputPassword
} from '$lib/elements/forms';
import InputCheckbox from '$lib/elements/forms/inputCheckbox.svelte';
import PopoverContent from './popoverContent.svelte';
import { FormItem, FormList } from '$lib/elements/forms';
import SettingsFormInput from './settingsFormInput.svelte';
import type {
ProviderInput,
TwilioProviderParams,
@@ -27,7 +16,7 @@
} from './store';
export let files: Record<string, FileList>;
export let inputs: ProviderInput[];
export let inputs: (ProviderInput | ProviderInput[])[];
export let params: Partial<
| TwilioProviderParams
| Msg91ProviderParams
@@ -44,98 +33,16 @@
<FormList>
{#each inputs as input}
{@const popoverImage = input.popoverImage}
{#if input.type === 'text'}
<InputText
id={input.name}
label={input.label}
placeholder={input.placeholder}
required={!input.optional}
isPopoverDefined={input.popover !== undefined}
bind:value={params[input.name]}>
<svelte:fragment slot="popover">
<PopoverContent lines={input.popover} {popoverImage} />
</svelte:fragment>
</InputText>
{:else if input.type === 'password'}
<InputPassword
id={input.name}
label={input.label}
placeholder={input.placeholder}
required={!input.optional}
showPasswordButton
isPopoverDefined={input.popover !== undefined}
bind:value={params[input.name]}>
<svelte:fragment slot="popover">
<PopoverContent lines={input.popover} {popoverImage} />
</svelte:fragment>
</InputPassword>
{:else if input.type === 'email'}
<InputEmail
id={input.name}
label={input.label}
placeholder={input.placeholder}
required={!input.optional}
isPopoverDefined={input.popover !== undefined}
bind:value={params[input.name]}>
<svelte:fragment slot="popover">
<PopoverContent lines={input.popover} {popoverImage} />
</svelte:fragment>
</InputEmail>
{:else if input.type === 'domain'}
<InputDomain
id={input.name}
label={input.label}
placeholder={input.placeholder}
required={!input.optional}
isPopoverDefined={input.popover !== undefined}
bind:value={params[input.name]}>
<svelte:fragment slot="popover">
<PopoverContent lines={input.popover} {popoverImage} />
</svelte:fragment>
</InputDomain>
{:else if input.type === 'phone'}
<InputPhone
id={input.name}
label={input.label}
placeholder={input.placeholder}
required={!input.optional}
isPopoverDefined={input.popover !== undefined}
bind:value={params[input.name]}>
<svelte:fragment slot="popover">
<PopoverContent lines={input.popover} {popoverImage} />
</svelte:fragment>
</InputPhone>
{:else if input.type === 'file'}
<InputFile
label={input.label}
allowedFileExtensions={input.allowedFileExtensions}
required={!input.optional}
isPopoverDefined={input.popover !== undefined}
bind:files={files[input.name]}>
<svelte:fragment slot="popover">
<PopoverContent lines={input.popover} {popoverImage} />
</svelte:fragment>
</InputFile>
{:else if input.type === 'switch'}
<InputSwitch label={input.label} id={input.name} bind:value={params[input.name]}>
<svelte:fragment slot="description">
<p>{@html input.description}</p>
</svelte:fragment>
</InputSwitch>
{:else if input.type === 'checkbox'}
<InputCheckbox label={input.label} id={input.name} bind:checked={params[input.name]}>
<svelte:fragment slot="description">
{input.description}
</svelte:fragment>
</InputCheckbox>
{:else if input.type === 'select'}
<InputSelect
label={input.label}
id={input.name}
options={input.options}
required={!input.optional}
bind:value={params[input.name]} />
{#if Array.isArray(input)}
<FormItem isMultiple>
{#each input as i}
<div class="u-flex u-flex-basis-50-percent">
<SettingsFormInput input={i} bind:params bind:files fullWidth />
</div>
{/each}
</FormItem>
{:else}
<SettingsFormInput {input} bind:params bind:files />
{/if}
{/each}
</FormList>
@@ -26,6 +26,7 @@ export type ProviderInput = {
| 'checkbox';
placeholder?: string;
description?: string;
tooltip?: string;
popover?: string[];
popoverImage?: { src: { light: string; dark: string }; alt: string };
allowedFileExtensions?: string[];
@@ -45,7 +46,7 @@ type ProvidersMap = {
title: string;
description: string;
needAHand?: string[];
configure: ProviderInput[];
configure: (ProviderInput | ProviderInput[])[];
};
};
};
@@ -218,33 +219,37 @@ export const providers: ProvidersMap = {
description:
'Enable the EU region setting if your domain is within the European Union.'
},
{
label: 'Sender email',
name: 'fromEmail',
type: 'email',
placeholder: 'Enter email'
},
{
label: 'Sender name',
name: 'fromName',
type: 'text',
optional: true,
placeholder: 'Enter name'
},
{
label: 'Reply to email',
name: 'replyToEmail',
type: 'email',
optional: true,
placeholder: 'Enter email'
},
{
label: 'Reply to name',
name: 'replyToName',
type: 'text',
optional: true,
placeholder: 'Enter name'
}
[
{
label: 'Sender email',
name: 'fromEmail',
type: 'email',
placeholder: 'Enter email'
},
{
label: 'Sender name',
name: 'fromName',
type: 'text',
optional: true,
placeholder: 'Enter name'
}
],
[
{
label: 'Reply-to email',
name: 'replyToEmail',
type: 'email',
optional: true,
placeholder: 'Enter email'
},
{
label: 'Reply-to name',
name: 'replyToName',
type: 'text',
optional: true,
placeholder: 'Enter name'
}
]
]
},
[Providers.Sendgrid]: {
@@ -263,66 +268,44 @@ export const providers: ProvidersMap = {
'Head to <b>Settings -> API Keys -> Create API key.</b>'
]
},
{
label: 'Sender email',
name: 'fromEmail',
type: 'email',
placeholder: 'Enter email'
},
{
label: 'Sender name',
name: 'fromName',
type: 'text',
optional: true,
placeholder: 'Enter name'
},
{
label: 'Reply to email',
name: 'replyToEmail',
type: 'email',
optional: true,
placeholder: 'Enter email'
},
{
label: 'Reply to name',
name: 'replyToName',
type: 'text',
optional: true,
placeholder: 'Enter name'
}
[
{
label: 'Sender email',
name: 'fromEmail',
type: 'email',
placeholder: 'Enter email'
},
{
label: 'Sender name',
name: 'fromName',
type: 'text',
optional: true,
placeholder: 'Enter name'
}
],
[
{
label: 'Reply-to email',
name: 'replyToEmail',
type: 'email',
optional: true,
placeholder: 'Enter email'
},
{
label: 'Reply-to name',
name: 'replyToName',
type: 'text',
optional: true,
placeholder: 'Enter name'
}
]
]
},
[Providers.SMTP]: {
classIcon: 'mail',
title: 'SMTP',
description: '',
description: 'Generic SMTP server.',
configure: [
{
label: 'Sender name',
name: 'fromName',
type: 'text',
optional: true,
placeholder: 'Enter name'
},
{
label: 'Sender email',
name: 'fromEmail',
type: 'email',
placeholder: 'Enter email'
},
{
label: 'Reply to name',
name: 'replyToName',
type: 'text',
optional: true,
placeholder: 'Enter name'
},
{
label: 'Reply to email',
name: 'replyToEmail',
type: 'email',
optional: true,
placeholder: 'Enter email'
},
{
label: 'Server host',
name: 'host',
@@ -367,16 +350,45 @@ export const providers: ProvidersMap = {
type: 'checkbox',
optional: true
},
[
{
label: 'Sender email',
name: 'fromEmail',
type: 'email',
placeholder: 'Enter email'
},
{
label: 'Sender name',
name: 'fromName',
type: 'text',
optional: true,
placeholder: 'Enter name'
}
],
[
{
label: 'Reply-to email',
name: 'replyToEmail',
type: 'email',
optional: true,
placeholder: 'Enter email'
},
{
label: 'Reply-to name',
name: 'replyToName',
type: 'text',
optional: true,
placeholder: 'Enter name'
}
],
{
label: 'Mailer',
label: 'X-Mailer header',
name: 'mailer',
type: 'text',
optional: true,
placeholder: 'Enter mailer',
popover: [
'<b>What is the Mailer?</b>',
'The value to use for the X-Mailer header.'
]
tooltip:
'The X-Mailer header is used to identify the software or client used for composing or sending the email. It typically includes information such as the name and version of the email program, aiding in troubleshooting and debugging.'
}
]
}
@@ -134,6 +134,7 @@
<WizardStep {beforeSubmit}>
<svelte:fragment slot="title">Provider</svelte:fragment>
<div class="u-sep-block-start u-padding-block-end-32" />
<FormList gap={16}>
<InputText
id="name"
@@ -173,3 +174,15 @@
</div>
</FormList>
</WizardStep>
<style lang="scss">
.grid-box {
--grid-gap: 1rem;
grid-template-columns: 1fr 1fr;
}
:global(body:not(.theme-dark)) .grid-box :global(.card) {
--p-card-bg-color-default: var(--color-neutral-5);
--p-card-bg-color-hover: var(--color-neutral-10);
}
</style>
@@ -21,7 +21,15 @@
let open = false;
onMount(() => {
for (const input of inputs) {
const flattened = [];
for (const i of inputs) {
if (Array.isArray(i)) {
flattened.push(...i);
} else {
flattened.push(i);
}
}
for (const input of flattened) {
if (input.type === 'file' && $providerParams[$provider][input.name].length > 0) {
const dataTransfer = new DataTransfer();
const f = new File(