Add provider details route

This commit is contained in:
Steven Nguyen
2023-12-19 16:15:08 -08:00
parent c8ef515875
commit bb705cbf8a
12 changed files with 787 additions and 1 deletions
+2 -1
View File
@@ -46,7 +46,8 @@ export enum Dependencies {
MIGRATIONS = 'dependency:migrations',
COLLECTIONS = 'dependency:collections',
RUNTIMES = 'dependency:runtimes',
CONSOLE_VARIABLES = 'dependency:console_variables'
CONSOLE_VARIABLES = 'dependency:console_variables',
MESSAGING_PROVIDER = 'dependency:messaging_provider'
}
export const scopes: {
@@ -0,0 +1,5 @@
<svelte:head>
<title>Provider - Appwrite</title>
</svelte:head>
<slot />
@@ -0,0 +1,32 @@
import type { LayoutLoad } from './$types';
import Breadcrumbs from './breadcrumbs.svelte';
import Header from './header.svelte';
import { sdk } from '$lib/stores/sdk';
import { Dependencies } from '$lib/constants';
import { error } from '@sveltejs/kit';
export const load: LayoutLoad = async ({ params, depends }) => {
depends(Dependencies.MESSAGING_PROVIDER);
const response = await sdk.forProject.client.call(
'GET',
new URL(sdk.forProject.client.config.endpoint + '/messaging/providers/' + params.provider),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
}
);
console.log(response);
try {
return {
header: Header,
breadcrumbs: Breadcrumbs,
provider: response
};
} catch (e) {
throw error(e.code, e.message);
}
};
@@ -0,0 +1,12 @@
<script lang="ts">
import { Container } from '$lib/layout';
import DangerZone from './dangerZone.svelte';
import UpdateName from './updateName.svelte';
import UpdateStatus from './updateStatus.svelte';
</script>
<Container>
<UpdateStatus />
<UpdateName />
<DangerZone />
</Container>
@@ -0,0 +1,27 @@
<script lang="ts">
import { Breadcrumbs } from '$lib/layout';
import { organization } from '$lib/stores/organization';
import { project } from '$routes/console/project-[project]/store';
import { provider } from './store';
$: breadcrumbs = [
{
href: `/console/organization-${$organization.$id}`,
title: $organization.name
},
{
href: `/console/project-${$project.$id}`,
title: $project.name
},
{
href: `/console/project-${$project.$id}/messaging`,
title: 'Messaging'
},
{
href: `/console/project-${$project.$id}/messaging/providers/provider-${$provider?.$id}`,
title: $provider?.name
}
];
</script>
<Breadcrumbs {breadcrumbs} />
@@ -0,0 +1,45 @@
<script lang="ts" context="module">
import { get } from 'svelte/store';
let showDelete = writable(false);
export const promptDeleteUser = (id: string) => {
showDelete.set(true);
goto(`/console/project-${get(project).$id}/auth/user-${id}`);
};
</script>
<script lang="ts">
import { CardGrid, BoxAvatar, Heading } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { writable } from 'svelte/store';
import { provider } from './store';
import { goto } from '$app/navigation';
import { toLocaleDateTime } from '$lib/helpers/date';
import { project } from '$routes/console/project-[project]/store';
import DeleteProvider from './deleteProvider.svelte';
</script>
<CardGrid danger>
<div>
<Heading tag="h6" size="7">Delete provider</Heading>
</div>
<p>The provider's instance will be permanently deleted. This action is irreversible.</p>
<svelte:fragment slot="aside">
<BoxAvatar>
<svelte:fragment slot="title">
<h6 class="u-bold u-trim-1">{$provider.name}</h6>
</svelte:fragment>
<p>
Last updated: {toLocaleDateTime($provider.$updatedAt)}
</p>
</BoxAvatar>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button secondary on:click={() => ($showDelete = true)} event="delete_messaging_provider"
>Delete</Button>
</svelte:fragment>
</CardGrid>
<DeleteProvider bind:showDelete={$showDelete} />
@@ -0,0 +1,59 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { base } from '$app/paths';
import { page } from '$app/stores';
import { Modal } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { provider } from './store';
import { project } from '../../../store';
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
export let showDelete = false;
const deleteProvider = async () => {
try {
await sdk.forProject.client.call(
'DELETE',
new URL(
sdk.forProject.client.config.endpoint + '/messaging/providers/' + $provider.$id
),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
}
);
showDelete = false;
addNotification({
type: 'success',
message: `${$provider.name} has been deleted`
});
trackEvent(Submit.MessagingProviderDelete);
await goto(`${base}/console/project-${$page.params.project}/messaging/providers`);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.MessagingProviderDelete);
}
};
</script>
<Modal
title="Delete provider"
bind:show={showDelete}
onSubmit={deleteProvider}
icon="exclamation"
state="warning"
headerDivider={false}>
<p data-private>
Are you sure you want to delete <b>{$provider.name}</b> from '{$project.name}'?
</p>
<svelte:fragment slot="footer">
<Button text on:click={() => (showDelete = false)}>Cancel</Button>
<Button secondary submit>Delete</Button>
</svelte:fragment>
</Modal>
@@ -0,0 +1,17 @@
<script lang="ts">
import { page } from '$app/stores';
import { Id } from '$lib/components';
import { Cover, CoverTitle } from '$lib/layout';
import { provider } from './store';
const projectId = $page.params.project;
</script>
<Cover>
<svelte:fragment slot="header">
<CoverTitle href={`/console/project-${projectId}/messaging/providers`}>
{$provider?.name ? $provider?.name : '-'}
</CoverTitle>
<Id value={$provider?.$id} event="provider">{$provider?.$id}</Id>
</svelte:fragment>
</Cover>
@@ -0,0 +1,9 @@
import { derived } from 'svelte/store';
import { page } from '$app/stores';
import type { Provider } from '../../store';
export const provider = derived(
page,
// TODO: Set actual type
($page) => $page.data.provider as Provider
);
@@ -0,0 +1,51 @@
<script lang="ts">
import { CardGrid, Heading } from '$lib/components';
import { Button, Form, InputText } from '$lib/elements/forms';
import { onMount } from 'svelte';
import { provider } from './store';
let providerName: string = null;
onMount(async () => {
providerName ??= $provider.name;
});
async function updateName() {
// TODO: switch on provider and update name
// try {
// await sdk.forProject.users.updateName($provider.$id, providerName);
// await invalidate(Dependencies.USER);
// addNotification({
// message: 'Name has been updated',
// type: 'success'
// });
// trackEvent(Submit.UserUpdateName);
// } catch (error) {
// addNotification({
// message: error.message,
// type: 'error'
// });
// trackError(error, Submit.UserUpdateName);
// }
}
</script>
<Form onSubmit={updateName}>
<CardGrid>
<Heading tag="h6" size="7">Name</Heading>
<svelte:fragment slot="aside">
<ul data-private>
<InputText
id="name"
label="Name"
placeholder="Enter name"
autocomplete={false}
bind:value={providerName} />
</ul>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={providerName === $provider.name} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
@@ -0,0 +1,367 @@
<script lang="ts">
import { CardGrid, Heading } from '$lib/components';
import { Button, InputSwitch } from '$lib/elements/forms';
import { toLocaleDateTime } from '$lib/helpers/date';
import { onMount } from 'svelte';
import { provider } from './store';
import { sdk } from '$lib/stores/sdk';
import { addNotification } from '$lib/stores/notifications';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { Dependencies } from '$lib/constants';
import { invalidate } from '$app/navigation';
import Provider, { Providers } from '../../provider.svelte';
import ProviderType from '../../providerType.svelte';
import { provider as wizardProvider, providerType, providerParams } from '../wizard/store';
import { wizard } from '$lib/stores/wizard';
import Update from '../update.svelte';
let enabled: boolean = null;
onMount(() => {
enabled ??= $provider.enabled;
});
function configure() {
$providerType = $provider.type;
$wizardProvider = $provider.provider;
switch ($wizardProvider) {
case Providers.Twilio:
$providerParams[$wizardProvider] = {
providerId: $provider.$id,
name: $provider.name,
default: $provider.default,
enabled: $provider.enabled,
accountSid: $provider.credentials['accountSid'],
authToken: $provider.credentials['authToken'],
from: $provider.options['from']
};
break;
case Providers.Msg91:
$providerParams[$wizardProvider] = {
providerId: $provider.$id,
name: $provider.name,
default: $provider.default,
enabled: $provider.enabled,
from: $provider.options['from'],
senderId: $provider.credentials['senderId'],
authKey: $provider.credentials['authKey']
};
break;
case Providers.Telesign:
$providerParams[$wizardProvider] = {
providerId: $provider.$id,
name: $provider.name,
default: $provider.default,
enabled: $provider.enabled,
username: $provider.credentials['username'],
password: $provider.credentials['password'],
from: $provider.options['from']
};
break;
case Providers.Textmagic:
$providerParams[$wizardProvider] = {
providerId: $provider.$id,
name: $provider.name,
default: $provider.default,
enabled: $provider.enabled,
username: $provider.credentials['username'],
apiKey: $provider.credentials['apiKey'],
from: $provider.options['from']
};
break;
case Providers.Vonage:
$providerParams[$wizardProvider] = {
providerId: $provider.$id,
name: $provider.name,
default: $provider.default,
enabled: $provider.enabled,
apiKey: $provider.credentials['apiKey'],
apiSecret: $provider.credentials['apiSecret'],
from: $provider.options['from']
};
break;
case Providers.Mailgun:
$providerParams[$wizardProvider] = {
providerId: $provider.$id,
name: $provider.name,
default: $provider.default,
enabled: $provider.enabled,
isEuRegion: false,
from: $provider.options['from'],
apiKey: $provider.credentials['apiKey'],
domain: $provider.credentials['domain']
};
break;
case Providers.Sendgrid:
$providerParams[$wizardProvider] = {
providerId: $provider.$id,
name: $provider.name,
default: $provider.default,
enabled: $provider.enabled,
apiKey: $provider.credentials['apiKey'],
from: $provider.options['from']
};
break;
case Providers.FCM:
$providerParams[$wizardProvider] = {
providerId: $provider.$id,
name: $provider.name,
default: $provider.default,
enabled: $provider.enabled,
serverKey: $provider.credentials['serverKey']
};
break;
case Providers.APNS:
$providerParams[$wizardProvider] = {
providerId: $provider.$id,
name: $provider.name,
default: $provider.default,
enabled: $provider.enabled,
authKey: $provider.credentials['authKey'],
authKeyId: $provider.credentials['authKeyId'],
teamId: $provider.credentials['teamId'],
bundleId: $provider.credentials['bundleId']
};
break;
case Providers.MQTT:
break;
}
wizard.start(Update);
}
async function updateStatus() {
try {
let response = { $id: '', name: '' };
const providerId = $provider.$id;
switch ($provider.provider) {
case Providers.Twilio:
response = await sdk.forProject.client.call(
'PATCH',
new URL(
sdk.forProject.client.config.endpoint +
'/messaging/providers/twilio/' +
providerId
),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
},
{
enabled: enabled
}
);
break;
case Providers.Msg91:
response = await sdk.forProject.client.call(
'PATCH',
new URL(
sdk.forProject.client.config.endpoint +
'/messaging/providers/msg91/' +
providerId
),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
},
{
enabled: enabled
}
);
break;
case Providers.Telesign:
response = await sdk.forProject.client.call(
'PATCH',
new URL(
sdk.forProject.client.config.endpoint +
'/messaging/providers/telesign/' +
providerId
),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
},
{
enabled: enabled
}
);
break;
case Providers.Textmagic:
response = await sdk.forProject.client.call(
'PATCH',
new URL(
sdk.forProject.client.config.endpoint +
'/messaging/providers/textmagic/' +
providerId
),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
},
{
enabled: enabled
}
);
break;
case Providers.Vonage:
response = await sdk.forProject.client.call(
'PATCH',
new URL(
sdk.forProject.client.config.endpoint +
'/messaging/providers/vonage/' +
providerId
),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
},
{
enabled: enabled
}
);
break;
case Providers.Mailgun:
response = await sdk.forProject.client.call(
'PATCH',
new URL(
sdk.forProject.client.config.endpoint +
'/messaging/providers/mailgun/' +
providerId
),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
},
{
enabled: enabled
}
);
break;
case Providers.Sendgrid:
response = await sdk.forProject.client.call(
'PATCH',
new URL(
sdk.forProject.client.config.endpoint +
'/messaging/providers/sendgrid/' +
providerId
),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
},
{
enabled: enabled
}
);
break;
case Providers.FCM:
response = await sdk.forProject.client.call(
'PATCH',
new URL(
sdk.forProject.client.config.endpoint +
'/messaging/providers/fcm/' +
providerId
),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
},
{
enabled: enabled
}
);
break;
case Providers.APNS:
response = await sdk.forProject.client.call(
'PATCH',
new URL(
sdk.forProject.client.config.endpoint +
'/messaging/providers/apns/' +
providerId
),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
},
{
enabled: enabled
}
);
break;
case Providers.MQTT:
response = await sdk.forProject.client.call(
'PATCH',
new URL(
sdk.forProject.client.config.endpoint +
'/messaging/providers/mqtt/' +
providerId
),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
},
{
enabled: enabled
}
);
break;
}
await invalidate(Dependencies.MESSAGING_PROVIDER);
addNotification({
type: 'success',
message: `${response.name} has been ${enabled ? 'enabled' : 'disabled'}`
});
trackEvent(Submit.MessagingProviderUpdate, {
provider: $provider
});
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.MessagingProviderUpdate);
}
}
</script>
<CardGrid>
<div class="grid-1-2-col-1 u-flex u-cross-center u-gap-16" data-private>
<Provider provider={$provider.provider} size="l">
<Heading tag="h6" size="7">{$provider.name}</Heading>
</Provider>
</div>
<svelte:fragment slot="aside">
<div class="u-flex u-main-space-between">
<div data-private>
<ul>
<InputSwitch
id="enabled"
label={enabled ? 'Enabled' : 'Disabled'}
bind:value={enabled} />
</ul>
<p class="title">Provider: <Provider noIcon provider={$provider.provider} /></p>
<p class="title">Channel: <ProviderType noIcon type={$provider.type} /></p>
<p>Created: {toLocaleDateTime($provider.$createdAt)}</p>
</div>
</div>
</svelte:fragment>
<svelte:fragment slot="actions">
<div class="u-flex u-flex-wrap u-gap-12">
<Button secondary on:click={() => configure()}>Configure</Button>
<Button disabled={$provider.enabled === enabled} on:click={() => updateStatus()}
>Update</Button>
</div>
</svelte:fragment>
</CardGrid>
@@ -0,0 +1,161 @@
import { writable } from 'svelte/store';
import type { Column } from '$lib/components/viewSelector.svelte';
export let showCreate = writable(false);
export const columns = writable<Column[]>([
{ id: '$id', title: 'Provider ID', show: true },
{ id: 'name', title: 'Name', show: true },
{ id: 'provider', title: 'Provider', show: true },
{ id: 'channel', title: 'Channel', show: true },
{ id: 'status', title: 'Status', show: true }
]);
export type Instruction = {
text: string;
input: {
label: string;
name: string;
type: 'text' | 'domain' | 'email';
placeholder: string;
};
};
export const providers = {
sms: {
name: 'SMS',
text: 'SMS',
icon: 'annotation',
providers: {
twilio: {
imageIcon: 'twilio',
title: 'Twilio',
description: ''
},
msg91: {
imageIcon: 'msg91',
title: 'MSG91',
description: ''
},
telesign: {
imageIcon: 'telesign',
title: 'Telesign',
description: ''
},
textmagic: {
imageIcon: 'textmagic',
title: 'Textmagic',
description: ''
},
vonage: {
imageIcon: 'vonage',
title: 'Vonage',
description: ''
}
}
},
email: {
name: 'Email',
text: 'emails',
icon: 'mail',
providers: {
mailgun: {
imageIcon: 'mailgun',
title: 'Mailgun',
description: '',
initialize: [
{
text: 'Before you can create a Mailgun provider, you need to first create a <a href="https://www.mailgun.com/" class="link" target="_blank" rel="noopener noreferrer">Mailgun</a> account.'
},
{
text: 'Head to your <b>Profile > API Security</b>.'
},
{
text: '<b>Generate a key</b> and give it a name. Copy and paste it in the field below.',
input: {
label: 'API key',
name: 'apiKey',
type: 'text',
placeholder: 'Enter API key'
}
},
{
// TODO: Update link to domain verification
text: 'Head to <b>Sending > Domains</b> and click on \'Add New Domain\'. Verify your domain by following the <a href="https://www.mailgun.com/" class="link" target="_blank" rel="noopener noreferrer">instructions</a>.',
input: {
label: 'Base URL',
name: 'baseUrl',
type: 'text',
placeholder: 'Enter base URL'
}
}
],
configure: [
{
text: 'Provide a display name your recipient will see when they receive your emails.',
input: {
label: 'From',
name: 'from',
type: 'text',
placeholder: 'Enter name'
}
},
{
text: 'Provide an email address that will be visible to the recipient as the senders email address for this message.',
input: {
label: 'From email address',
name: 'email',
type: 'email',
placeholder: 'Enter email'
}
},
{
text: 'Provide an email address for users to use when replying to your emails.',
input: {
label: 'Reply to',
name: 'replyTo',
type: 'email',
placeholder: 'Enter email'
}
},
{
text: 'Provide the domain as it is registered on Mailgun.',
input: {
label: 'Domain',
name: 'domain',
type: 'domain',
placeholder: 'Enter domain'
}
}
]
},
sendgrid: {
imageIcon: 'sendgrid',
title: 'Sendgrid',
description: ''
}
}
},
push: {
name: 'Push notification',
text: 'notifications',
icon: 'device-mobile',
providers: {
fcm: {
imageIcon: 'firebase',
title: 'FCM',
description: 'Firebase Cloud Messaging'
},
apns: {
imageIcon: 'apple',
title: 'APNS',
description: 'Apple Push Notification Service'
},
mqtt: {
imageIcon: 'mqtt',
title: 'MQTT',
description: 'Message Queuing Telemtry Transport'
}
}
}
};