Merge branch '1.5.x' of https://github.com/appwrite/console into fix-messaging-smtp-provider

This commit is contained in:
Torsten Dittmann
2024-02-24 19:06:28 +01:00
28 changed files with 230 additions and 148 deletions
+4 -4
View File
@@ -6,7 +6,7 @@
"": {
"name": "@appwrite/console",
"dependencies": {
"@appwrite.io/console": "^0.6.0-rc.12",
"@appwrite.io/console": "^0.6.0-rc.13",
"@appwrite.io/pink": "0.4.0",
"@appwrite.io/pink-icons": "0.4.0",
"@popperjs/core": "^2.11.8",
@@ -158,9 +158,9 @@
"integrity": "sha512-TD+xbmsBLyYy/IxFimW/YL/9L2IEnM7/EoV9Aeh56U64Ify8o27HJcKjo38XY9Tcn0uOq1AX3thkKgvtWvwFQg=="
},
"node_modules/@appwrite.io/console": {
"version": "0.6.0-rc.12",
"resolved": "https://registry.npmjs.org/@appwrite.io/console/-/console-0.6.0-rc.12.tgz",
"integrity": "sha512-pDov75TCkBmJdUPrWuzRW7uM28HYqiMFtVFrUoavDd/lMPBS7RnPXL4sBHsGY8AUldHE3oBcGVPkNoQfEAgLlw==",
"version": "0.6.0-rc.13",
"resolved": "https://registry.npmjs.org/@appwrite.io/console/-/console-0.6.0-rc.13.tgz",
"integrity": "sha512-/c+HGkqdqWjjlzVgOmC6vb4t2jsHgbmX/aaDLoQHXFmaakEDWzK1cgVYRHoU3o14xIEpBupteh3KzOoqdLvvsQ==",
"dependencies": {
"cross-fetch": "3.1.5",
"isomorphic-form-data": "2.0.0"
+1 -1
View File
@@ -18,7 +18,7 @@
"e2e": "playwright test tests/e2e"
},
"dependencies": {
"@appwrite.io/console": "^0.6.0-rc.12",
"@appwrite.io/console": "^0.6.0-rc.13",
"@appwrite.io/pink": "0.4.0",
"@appwrite.io/pink-icons": "0.4.0",
"@popperjs/core": "^2.11.8",
+13
View File
@@ -0,0 +1,13 @@
import { AppwriteException } from '@appwrite.io/console';
import type { HandleClientError } from '@sveltejs/kit';
export const handleError: HandleClientError = async ({ error, message, status }) => {
if (error instanceof AppwriteException && error.code === 0) {
status = undefined;
message = error.message;
}
return {
message,
status
};
};
+34 -13
View File
@@ -1,18 +1,39 @@
<script lang="ts">
export let href: string;
export let href: string = null;
export let external: boolean = false;
</script>
<li class="clickable-list-item">
<a {href} class="clickable-list-button" on:click>
{#if $$slots.default}
<h5 class="clickable-list-title u-trim-1">
<slot />
</h5>
{/if}
{#if $$slots.desc}
<div class="clickable-list-desc">
<p class="text u-margin-block-start-8"><slot name="desc" /></p>
</div>
{/if}
</a>
{#if href}
<a
{href}
class="clickable-list-button"
target={external ? '_blank' : ''}
rel={external ? 'noopener noreferrer' : ''}
on:click>
{#if $$slots.default}
<h5 class="clickable-list-title u-trim-1">
<slot />
</h5>
{/if}
{#if $$slots.desc}
<div class="clickable-list-desc">
<p class="text u-margin-block-start-8"><slot name="desc" /></p>
</div>
{/if}
</a>
{:else}
<button class="clickable-list-button u-width-full-line" on:click>
{#if $$slots.default}
<h5 class="clickable-list-title u-trim-1">
<slot />
</h5>
{/if}
{#if $$slots.desc}
<div class="clickable-list-desc">
<p class="text u-margin-block-start-8"><slot name="desc" /></p>
</div>
{/if}
</button>
{/if}
</li>
+2 -2
View File
@@ -6,7 +6,7 @@
</script>
<li class="collapsible-item">
<details class="collapsible-wrapper" {open}>
<details class="collapsible-wrapper" bind:open>
<!-- svelte-ignore a11y-no-redundant-roles -->
<summary
class="collapsible-button u-position-relative"
@@ -22,7 +22,7 @@
{/if}
</div>
<div class="icon">
<span class="icon-cheveron-down" aria-hidden="true" />
<span class="icon-cheveron-down u-font-size-20" aria-hidden="true" />
</div>
</summary>
<div
+2 -2
View File
@@ -134,7 +134,7 @@
bind:value={operatorKey} />
</ul>
{#if column && operator && !operator?.hideInput}
<div class="u-margin-block-start-8">
<ul class="u-margin-block-start-8">
{#if column.type === 'integer' || column.type === 'double'}
<InputNumber id="value" bind:value placeholder="Enter value" />
{:else if column.type === 'boolean'}
@@ -150,7 +150,7 @@
{:else}
<InputText id="value" bind:value placeholder="Enter value" />
{/if}
</div>
</ul>
{/if}
<Button text disabled={isDisabled} class="u-margin-block-start-4" submit>
<i class="icon-plus" />
+1 -1
View File
@@ -6,7 +6,7 @@
</script>
<Unauthenticated>
<Heading size="1" tag="h3">{$page.status}</Heading>
<Heading size="1" tag="h3">{$page.error.status || 'Invalid Argument'}</Heading>
<Heading size="3" tag="h4">{$page.error.message}</Heading>
<Button href="/">Back to the console</Button>
</Unauthenticated>
+4 -3
View File
@@ -13,9 +13,10 @@ export const ssr = false;
export const load: LayoutLoad = async ({ depends, url }) => {
depends(Dependencies.ACCOUNT);
redirectTo.set(url.searchParams.get('forceRedirect') || null);
url.searchParams.delete('forceRedirect');
if (url.searchParams.has('forceRedirect')) {
redirectTo.set(url.searchParams.get('forceRedirect') || null);
url.searchParams.delete('forceRedirect');
}
try {
const account = await sdk.forConsole.account.get<{ organization?: string }>();
+1 -1
View File
@@ -6,7 +6,7 @@
</script>
<Container>
<Heading size="1" tag="h3">{$page.status}</Heading>
<Heading size="1" tag="h3">{$page.error.status || 'Invalid Argument'}</Heading>
<Heading size="3" tag="h4">{$page.error.message}</Heading>
<Button href="/console">Back to the console</Button>
</Container>
@@ -10,6 +10,7 @@ import { headerAlert } from '$lib/stores/headerAlert';
import ProjectsAtRisk from '$lib/components/billing/alerts/projectsAtRisk.svelte';
import { get } from 'svelte/store';
import { preferences } from '$lib/stores/preferences';
import type { Organization } from '$lib/stores/organization';
export const load: LayoutLoad = async ({ params, depends }) => {
depends(Dependencies.ORGANIZATION);
@@ -37,7 +38,9 @@ export const load: LayoutLoad = async ({ params, depends }) => {
return {
header: Header,
breadcrumbs: Breadcrumbs,
organization: await sdk.forConsole.teams.get(params.organization),
organization: await (sdk.forConsole.teams.get(
params.organization
) as Promise<Organization>),
members: await sdk.forConsole.teams.listMemberships(params.organization)
};
} catch (e) {
@@ -57,7 +57,7 @@
}
</script>
<Modal title="Invite Member" {error} size="big" bind:show={showCreate} onSubmit={create}>
<Modal title="Invite member" {error} size="big" bind:show={showCreate} onSubmit={create}>
{#if isCloud}
<Alert type="info">
{#if $organization?.billingPlan === BillingPlan.SCALE}
@@ -5,6 +5,6 @@
</script>
<Container>
<Heading size="1" tag="h3">{$page.status}</Heading>
<Heading size="1" tag="h3">{$page.error.status || 'Invalid Argument'}</Heading>
<p class="body-text-2 u-bold u-margin-block-start-4">{$page.error.message}</p>
</Container>
@@ -5,6 +5,7 @@ import type { LayoutLoad } from './$types';
import { preferences } from '$lib/stores/preferences';
import { failedInvoice } from '$lib/stores/billing';
import { isCloud } from '$lib/system';
import type { Organization } from '$lib/stores/organization';
export const load: LayoutLoad = async ({ params, depends }) => {
depends(Dependencies.PROJECT);
@@ -22,7 +23,7 @@ export const load: LayoutLoad = async ({ params, depends }) => {
return {
project,
organization: await sdk.forConsole.teams.get(project.teamId)
organization: await (sdk.forConsole.teams.get(project.teamId) as Promise<Organization>)
};
} catch (e) {
error(e.code, e.message);
@@ -32,7 +32,7 @@
}
</script>
<CardGrid>
<CardGrid hideFooter={$message.status != 'failed'}>
<div class="grid-1-2-col-1 u-flex u-cross-center u-gap-16" data-private>
<ProviderType type={$message.providerType} size="l">
<Heading tag="h6" size="7">{providerType}</Heading>
@@ -51,15 +51,13 @@
</svelte:fragment>
<svelte:fragment slot="actions">
{#if $message.status === 'failed'}
<Button
secondary
on:click={(e) => {
e.preventDefault();
errors = $message.deliveryErrors;
showFailed = true;
}}>View logs</Button>
{/if}
<Button
secondary
on:click={(e) => {
e.preventDefault();
errors = $message.deliveryErrors;
showFailed = true;
}}>View logs</Button>
</svelte:fragment>
</CardGrid>
@@ -129,6 +129,7 @@
$providerParams[$provider].authKeyId,
$providerParams[$provider].teamId,
$providerParams[$provider].bundleId,
$providerParams[$provider].sandbox,
$providerParams[$provider].enabled
);
break;
@@ -69,13 +69,13 @@
);
break;
case Providers.FCM:
response = await sdk.forProject.messaging.updateFCMProvider(
response = await sdk.forProject.messaging.updateFcmProvider(
providerId,
providerName
);
break;
case Providers.APNS:
response = await sdk.forProject.messaging.updateAPNSProvider(
response = await sdk.forProject.messaging.updateApnsProvider(
providerId,
providerName
);
@@ -160,7 +160,7 @@
);
break;
case Providers.FCM:
response = await sdk.forProject.messaging.updateFCMProvider(
response = await sdk.forProject.messaging.updateFcmProvider(
providerId,
undefined,
undefined,
@@ -168,7 +168,7 @@
);
break;
case Providers.APNS:
response = await sdk.forProject.messaging.updateAPNSProvider(
response = await sdk.forProject.messaging.updateApnsProvider(
providerId,
undefined,
undefined,
@@ -94,14 +94,14 @@
);
break;
case Providers.FCM:
response = await sdk.forProject.messaging.updateFCMProvider(
response = await sdk.forProject.messaging.updateFcmProvider(
providerId,
undefined,
enabled
);
break;
case Providers.APNS:
response = await sdk.forProject.messaging.updateAPNSProvider(
response = await sdk.forProject.messaging.updateApnsProvider(
providerId,
undefined,
enabled
@@ -1,7 +1,7 @@
import type { Column } from '$lib/helpers/types';
import { writable } from 'svelte/store';
import { Providers } from '../provider.svelte';
import { MessagingProviderType, Encryption } from '@appwrite.io/console';
import { MessagingProviderType, SmtpEncryption } from '@appwrite.io/console';
export const columns = writable<Column[]>([
{ id: '$id', title: 'Provider ID', type: 'string', show: true },
@@ -162,6 +162,12 @@ export const providers: ProvidersMap = {
},
alt: 'Screenshot of where to download the Authentication Key in the Apple Developer Member Center'
}
},
{
label: 'Sandbox',
name: 'sandbox',
type: 'switch',
description: 'Use the development APNS server.'
}
]
}
@@ -348,9 +354,9 @@ export const providers: ProvidersMap = {
name: 'encryption',
type: 'select',
options: [
{ label: 'None', value: Encryption.None },
{ label: 'SSL', value: Encryption.Ssl },
{ label: 'TLS', value: Encryption.Tls }
{ label: 'None', value: SmtpEncryption.None },
{ label: 'SSL', value: SmtpEncryption.Ssl },
{ label: 'TLS', value: SmtpEncryption.Tls }
]
},
{
@@ -559,7 +565,6 @@ type ProviderParams = {
/**
* SMS providers
*/
export type TwilioProviderParams = ProviderParams & {
accountSid: string;
authToken: string;
@@ -593,7 +598,6 @@ export type VonageProviderParams = ProviderParams & {
/**
* Email providers
*/
export type MailgunProviderParams = ProviderParams & {
fromEmail: string;
fromName: string;
@@ -621,7 +625,7 @@ export type SMTPProviderParams = ProviderParams & {
port: number;
username: string;
password: string;
encryption: Encryption;
encryption: SmtpEncryption;
autoTLS: boolean;
mailer: string;
};
@@ -629,7 +633,6 @@ export type SMTPProviderParams = ProviderParams & {
/**
* Push providers
*/
export type FCMProviderParams = ProviderParams & {
serviceAccountJSON: string;
};
@@ -639,6 +642,7 @@ export type APNSProviderParams = ProviderParams & {
authKeyId: string;
teamId: string;
bundleId: string;
sandbox: boolean;
};
export type MQTTProviderParams = ProviderParams & {
@@ -6,7 +6,7 @@
import { FormList, InputText } from '$lib/elements/forms';
import { Pill } from '$lib/elements';
import { Providers } from '../../provider.svelte';
import { Encryption } from '@appwrite.io/console';
import { SmtpEncryption } from '@appwrite.io/console';
let name = '';
let showCustomId = false;
@@ -100,7 +100,7 @@
username: '',
password: '',
autoTLS: true,
encryption: Encryption.Tls,
encryption: SmtpEncryption.Tls,
mailer: '',
fromEmail: '',
fromName: '',
@@ -124,7 +124,8 @@
authKey: '',
authKeyId: '',
teamId: '',
bundleId: ''
bundleId: '',
sandbox: false
};
break;
}
@@ -4,17 +4,21 @@
import { providers } from '../store';
import { providerType, provider, providerParams } from './store';
import SettingsFormList from '../settingsFormList.svelte';
import { Table, TableBody, TableCell, TableRowButton, TableRowLink } from '$lib/elements/table';
import { newMemberModal } from '$lib/stores/organization';
import CreateMember from '$routes/console/organization-[organization]/createMember.svelte';
import Collapsible from '$lib/components/collapsible.svelte';
import CollapsibleItem from '$lib/components/collapsibleItem.svelte';
import Provider from '../../provider.svelte';
import { MessagingProviderType } from '@appwrite.io/console';
import ProviderTypeComponent from '$routes/console/project-[project]/messaging/providerType.svelte';
import {
Collapsible,
CollapsibleItem,
ClickableList,
ClickableListItem
} from '$lib/components';
let files: Record<string, FileList> = {};
const inputs = providers[$providerType].providers[$provider].configure;
let open = false;
onMount(() => {
for (const input of inputs) {
@@ -55,81 +59,115 @@
<div class="need-a-hand u-flex-vertical u-gap-8">
<p class="body-text-2 u-bold u-margin-block-start-48">Need a hand?</p>
{#if providers[$providerType].providers[$provider].needAHand}
{@const needAHand = providers[$providerType].providers[$provider].needAHand}
<div class="box u-padding-inline-8 u-padding-0 u-border-width-0">
<Collapsible>
<CollapsibleItem withIndentation>
<svelte:fragment slot="beforetitle">
<div class="u-flex u-cross-center u-gap-16">
<div class="avatar is-size-small">
<span
class="icon-info"
style:--p-text-size="1.25rem"
aria-hidden="true" />
<div>
{#if providers[$providerType].providers[$provider].needAHand}
{@const needAHand = providers[$providerType].providers[$provider].needAHand}
<div
class="how-to-enable u-padding-inline-8 u-padding-0"
style={!open ? '' : 'background-color: hsl(var(--p-bg-color-hover));'}>
<Collapsible>
<CollapsibleItem withIndentation bind:open>
<svelte:fragment slot="beforetitle">
<div class="u-flex u-cross-center u-gap-16">
<div class="avatar is-size-small">
<span
class="icon-info"
style:--p-text-size="1.25rem"
aria-hidden="true" />
</div>
<span class="title body-text-2 u-small" class:u-bold={open}
>How to enable <Provider provider={$provider} noIcon />
{#if $providerType == MessagingProviderType.Push}notifications{:else}<ProviderTypeComponent
type={$providerType}
noIcon />{/if} service?</span>
</div>
<span class="body-text-2 u-small u-bold"
>How to enable <Provider provider={$provider} noIcon />
{#if $providerType == MessagingProviderType.Push}notifications{:else}<ProviderTypeComponent
type={$providerType}
noIcon />{/if} service?</span>
</svelte:fragment>
<div class="u-flex-vertical u-gap-16">
{#each needAHand as p}
<p>
{@html p}
</p>
{/each}
</div>
</svelte:fragment>
<div class="u-flex-vertical u-gap-16">
{#each needAHand as p}
<p>
{@html p}
</p>
{/each}
</div>
</CollapsibleItem>
</Collapsible>
</div>
{/if}
</CollapsibleItem>
</Collapsible>
</div>
<div class="u-margin-block-start-4 u-sep-block-end" />
{/if}
<Table noMargin noStyles>
<TableBody>
<TableRowLink href={`https://appwrite.io/docs/messaging/${$provider}`}>
<TableCell>
<div class="u-flex u-cross-center u-main-space-between u-padding-8">
<div class="u-flex u-cross-center u-gap-16">
<div class="avatar is-size-small">
<span
class="icon-book-open"
style:--p-text-size="1.25rem"
aria-hidden="true" />
</div>
Read the full guide in the documentation
<ClickableList>
<ClickableListItem
href={`https://appwrite.io/docs/messaging/${$provider}`}
external>
<div class="u-flex u-cross-center u-main-space-between">
<div class="u-flex u-cross-center u-gap-16">
<div class="avatar is-size-small">
<span
class="icon-book-open"
style:--p-text-size="1.25rem"
aria-hidden="true" />
</div>
<span class="icon-arrow-right" aria-hidden="true" />
<p>Read the full guide in the documentation</p>
</div>
</TableCell>
</TableRowLink>
<TableRowButton on:click={() => ($newMemberModal = true)}>
<TableCell>
<div class="u-flex u-cross-center u-main-space-between u-padding-8">
<div class="u-flex u-cross-center u-gap-16">
<div class="avatar is-size-small">
<span
class="icon-user-group"
style:--p-text-size="1.25rem"
aria-hidden="true" />
</div>
Invite a team member to complete this step
<span class="icon-arrow-sm-right u-font-size-20" aria-hidden="true" />
</div>
</ClickableListItem>
<ClickableListItem on:click={() => ($newMemberModal = true)}>
<div class="u-flex u-cross-center u-main-space-between">
<div class="u-flex u-cross-center u-gap-16">
<div class="avatar is-size-small">
<span
class="icon-user-group"
style:--p-text-size="1.25rem"
aria-hidden="true" />
</div>
<span class="icon-arrow-right" aria-hidden="true" />
<p>Invite a team member to complete this step</p>
</div>
</TableCell>
</TableRowButton>
</TableBody>
</Table>
<span class="icon-arrow-sm-right u-font-size-20" aria-hidden="true" />
</div>
</ClickableListItem>
</ClickableList>
</div>
</div>
</WizardStep>
<CreateMember bind:showCreate={$newMemberModal} />
<style lang="scss">
.need-a-hand :global(.table-row):last-child:last-child {
border-block-end: none;
.need-a-hand {
--p-bg-color-hover: var(--color-neutral-10);
.how-to-enable {
border-radius: var(--border-radius-small);
&:hover,
&:focus {
background-color: hsl(var(--p-bg-color-hover));
.title {
font-weight: 600;
}
}
}
:global(.clickable-list) {
--color-border: var(--color-neutral-10);
:global(.theme-dark) & {
--color-border: var(--color-neutral-85);
}
}
:global(.clickable-list-button) {
padding-inline: 0.5rem;
}
:global(.theme-dark) & {
--p-bg-color-hover: var(--color-neutral-85);
}
}
:global(.clickable-list-button):hover p,
:global(.clickable-list-button):focus p {
font-weight: 600;
}
</style>
@@ -2,16 +2,16 @@ import type { MessagingProviderType } from '@appwrite.io/console';
import { writable } from 'svelte/store';
import type { Providers } from '../../provider.svelte';
import type {
TwilioProviderParams,
APNSProviderParams,
FCMProviderParams,
MailgunProviderParams,
Msg91ProviderParams,
SMTPProviderParams,
SendgridProviderParams,
TelesignProviderParams,
TextmagicProviderParams,
VonageProviderParams,
MailgunProviderParams,
SendgridProviderParams,
SMTPProviderParams,
FCMProviderParams,
APNSProviderParams
TwilioProviderParams,
VonageProviderParams
} from '../store';
export const providerType = writable<MessagingProviderType>(null);
@@ -46,9 +46,14 @@
const targetIds = Object.keys($targetsById).filter(
(targetId) => !(targetId in subscribersByTargetId)
);
const promises = targetIds.map((targetId) =>
sdk.forProject.messaging.createSubscriber($page.params.topic, ID.unique(), targetId)
);
const promises = targetIds.map(async (targetId) => {
const subscriber = await sdk.forProject.messaging.createSubscriber(
$page.params.topic,
ID.unique(),
targetId
);
subscribersByTargetId[targetId] = subscriber;
});
try {
await Promise.all(promises);
@@ -121,7 +121,7 @@
<span
class="collapsible-button-optional"
style="--p-toggle-optional-color: var(--color-neutral-50);">
({getTotal(topic)} subscribers)
({getTotal(topic)} targets)
</span>
</span></span>
</svelte:fragment>
@@ -1,24 +1,22 @@
import type { Aggregation, Invoice } from '$lib/sdk/billing';
import { accumulateUsage } from '$lib/sdk/usage';
import { getSdkForProject, sdk } from '$lib/stores/sdk';
import { Query } from '@appwrite.io/console';
import type { PageLoad } from './$types';
import type { Organization } from '$lib/stores/organization';
import type { Aggregation, Invoice } from '$lib/sdk/billing';
import { accumulateUsage } from '$lib/sdk/usage';
export const load: PageLoad = async ({ params, parent }) => {
const { invoice, project } = params;
const parentData = await parent();
const org = parentData.organization as Organization;
const { organization } = await parent();
let startDate: string = org.billingCurrentInvoiceDate;
let endDate: string = org.billingNextInvoiceDate;
let startDate: string = organization.billingCurrentInvoiceDate;
let endDate: string = organization.billingNextInvoiceDate;
let currentInvoice: Invoice = undefined;
let currentAggregation: Aggregation = undefined;
if (invoice) {
currentInvoice = await sdk.forConsole.billing.getInvoice(org.$id, invoice);
currentInvoice = await sdk.forConsole.billing.getInvoice(organization.$id, invoice);
currentAggregation = await sdk.forConsole.billing.getAggregation(
org.$id,
organization.$id,
currentInvoice.aggregationId
);
@@ -27,7 +25,7 @@ export const load: PageLoad = async ({ params, parent }) => {
}
const [invoices, usage] = await Promise.all([
sdk.forConsole.billing.listInvoices(org.$id, [Query.orderDesc('from')]),
sdk.forConsole.billing.listInvoices(organization.$id, [Query.orderDesc('from')]),
/**
* Workaround because project id might not be populated yet.
*/
-1
View File
@@ -75,7 +75,6 @@
OAuthProvider.Github,
window.location.origin + url,
window.location.origin,
undefined,
['read:user', 'user:email']
);
}
+1 -1
View File
@@ -16,7 +16,7 @@
async function verify() {
try {
disabled = true;
const challenge = await sdk.forConsole.account.create2FAChallenge(
const challenge = await sdk.forConsole.account.createChallenge(
AuthenticationFactor.Totp
);
await sdk.forConsole.account.updateChallenge(challenge.$id, code);
-1
View File
@@ -64,7 +64,6 @@
OAuthProvider.Github,
window.location.origin,
window.location.origin,
undefined,
['read:user', 'user:email']
);
}