Merge pull request #96 from parcelvoy/chore/provider-cleanup

Provider cleanup
This commit is contained in:
Chris Hills 2023-03-27 05:32:18 -05:00 committed by GitHub
commit 5e05e1c013
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 140 additions and 68 deletions

View file

@ -0,0 +1,16 @@
import type { Knex } from 'knex'
exports.up = async function(knex: Knex) {
await knex.schema
.alterTable('providers', function(table) {
table.dropIndex('external_id')
table.dropColumn('external_id')
})
}
exports.down = async function(knex: Knex) {
await knex.schema
.alterTable('providers', function(table) {
table.string('external_id').index()
})
}

View file

@ -11,6 +11,7 @@ export interface ProviderMeta {
type: string
channel: string
schema?: any
paths?: Record<string, string>
}
export const ProviderSchema = <T extends ExternalProviderParams, D>(id: string, data: JSONSchemaType<D>): JSONSchemaType<T> => {
@ -23,10 +24,6 @@ export const ProviderSchema = <T extends ExternalProviderParams, D>(id: string,
type: 'string',
nullable: true,
},
external_id: {
type: 'string',
nullable: true,
},
is_default: {
type: 'boolean',
nullable: true,
@ -41,7 +38,6 @@ export default class Provider extends Model {
type!: string
name!: string
project_id!: number
external_id?: string
group!: ProviderGroup
data!: Record<string, any>
is_default!: boolean
@ -53,13 +49,11 @@ export default class Provider extends Model {
name: '',
description: '',
url: '',
paths: {},
}
static get cacheKey() {
return {
external(externalId: string) {
return `providers:external:${externalId}`
},
internal(id: number) {
return `providers:id:${id}`
},

View file

@ -52,9 +52,6 @@ export const updateProvider = async (id: number, params: ExternalProviderParams,
const provider = await Provider.updateAndFetch(id, params)
app.remove(Provider.cacheKey.internal(provider.id))
app.remove(Provider.cacheKey.default(provider.project_id, provider.group))
if (provider.external_id) {
app.remove(Provider.cacheKey.external(provider.external_id))
}
return provider
}
@ -63,7 +60,4 @@ export const cacheProvider = (provider: Provider, app = App.main) => {
if (provider.is_default) {
app.set(Provider.cacheKey.default(provider.project_id, provider.group), provider)
}
if (provider.external_id) {
app.set(Provider.cacheKey.external(provider.external_id), provider)
}
}

View file

@ -41,7 +41,7 @@ export const loadControllers = <T extends Record<string, any>>(typeMap: T, chann
}
}
export const createController = <T extends ExternalProviderParams>(group: ProviderGroup, type: string, schema: JSONSchemaType<T>, externalKey?: (payload: T) => string): Router => {
export const createController = <T extends ExternalProviderParams>(group: ProviderGroup, type: string, schema: JSONSchemaType<T>): Router => {
const router = new Router<
ProjectState & { provider?: Provider }
>({
@ -51,7 +51,7 @@ export const createController = <T extends ExternalProviderParams>(group: Provid
router.post('/', async ctx => {
const payload = validate(schema, ctx.request.body)
ctx.body = await createProvider(ctx.state.project.id, { ...payload, external_id: externalKey?.(payload), type, group })
ctx.body = await createProvider(ctx.state.project.id, { ...payload, type, group })
})
router.param('providerId', async (value, ctx, next) => {

View file

@ -25,6 +25,9 @@ export default class SESEmailProvider extends EmailProvider {
name: 'Amazon SES',
url: 'https://aws.amazon.com/ses',
icon: 'https://parcelvoy.com/images/ses.svg',
paths: {
'Feedback URL': `/${this.namespace}`,
},
}
static schema = ProviderSchema<SESEmailProviderParams, SESDataParams>('sesProviderParams', {

View file

@ -28,6 +28,9 @@ export default class TwilioTextProvider extends TextProvider {
description: '',
url: 'https://twilio.com',
icon: 'https://parcelvoy.com/images/twilio.svg',
paths: {
'Unsubscribe URL': `/${this.namespace}/unsubscribe`,
},
}
static schema = ProviderSchema<TwilioProviderParams, TwilioDataParams>('twilioTextProviderParams', {

View file

@ -175,3 +175,8 @@ label .switch .slider.round {
label .switch .slider.round:before {
border-radius: 50%;
}
.icon {
width: 16px;
height: 16px;
}

View file

@ -416,6 +416,7 @@ export interface ProviderMeta {
type: string
channel: string
schema: any
paths?: Record<string, string>
}
export interface Image {

View file

@ -28,7 +28,7 @@
.ui-table .table-cell, .ui-table .table-header-cell {
display: table-cell;
padding: 10px 15px;
padding: 10px 15px;
border-bottom: 1px solid var(--color-grey);
}
@ -44,6 +44,12 @@
border-bottom: 0px;
}
.ui-table .table-cell .cell-content {
display: flex;
align-items: center;
gap: 5px;
}
.ui-table .table-header-cell {
font-weight: 500;
}

View file

@ -15,7 +15,7 @@
border-radius: var(--border-radius-outer);
background: var(--color-background);
width: 100%;
max-width: 400px;
max-width: 440px;
padding: 30px;
box-shadow: 0 20px 40px 0 rgba(0, 0, 0, 0.12);
max-height: 90vh;

View file

@ -45,7 +45,7 @@ export default function TextField<X extends FieldValues, P extends FieldPath<X>>
const onBlur = formParams?.onBlur
return (
<label ref={inputRef} className={clsx('ui-text-field', { 'hide-label': !form })}>
<label ref={inputRef} className={clsx('ui-text-field', { 'hide-label': !form && !label })}>
{
(!!form || !!label) && (
<span>
@ -87,6 +87,7 @@ export default function TextField<X extends FieldValues, P extends FieldPath<X>>
id={id}
min={min}
max={max}
disabled={disabled}
/>
)
}

View file

@ -1,159 +1,167 @@
export const ChevronLeftIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const ChevronLeftIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M15.75 19.5L8.25 12l7.5-7.5" />
</svg>
export const ChevronRightIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const ChevronRightIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
</svg>
export const ChevronUpDownIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const ChevronUpDownIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9" />
</svg>
export const CheckIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const CheckIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M4.5 12.75l6 6 9-13.5" />
</svg>
export const UserCircle = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const UserCircle = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
export const PlusIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const PlusIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
export const ThreeDotsIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const ThreeDotsIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M6.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM12.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM18.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0z" />
</svg>
export const DuplicateIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const DuplicateIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 01-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 011.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 00-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 01-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 00-3.375-3.375h-1.5a1.125 1.125 0 01-1.125-1.125v-1.5a3.375 3.375 0 00-3.375-3.375H9.75" />
</svg>
export const EditIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const EditIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" />
</svg>
export const ArchiveIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const ArchiveIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M20.25 7.5l-.625 10.632a2.25 2.25 0 01-2.247 2.118H6.622a2.25 2.25 0 01-2.247-2.118L3.75 7.5m6 4.125l2.25 2.25m0 0l2.25 2.25M12 13.875l2.25-2.25M12 13.875l-2.25 2.25M3.375 7.5h17.25c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125z" />
</svg>
export const TrashIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const TrashIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
</svg>
export const EmailIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const EmailIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75" />
</svg>
export const TextIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const TextIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M7.5 8.25h9m-9 3H12m-9.75 1.51c0 1.6 1.123 2.994 2.707 3.227 1.129.166 2.27.293 3.423.379.35.026.67.21.865.501L12 21l2.755-4.133a1.14 1.14 0 01.865-.501 48.172 48.172 0 003.423-.379c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0012 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018z" />
</svg>
export const PushIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const PushIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M10.5 1.5H8.25A2.25 2.25 0 006 3.75v16.5a2.25 2.25 0 002.25 2.25h7.5A2.25 2.25 0 0018 20.25V3.75a2.25 2.25 0 00-2.25-2.25H13.5m-3 0V3h3V1.5m-3 0h3m-3 18.75h3" />
</svg>
export const WebhookIcon = () => <svg height="24" viewBox="0 0 32 32" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m16 3c-3.855469 0-7 3.144531-7 7 0 2.09375 1.035156 3.871094 2.5 5.15625l-2.09375 3.875c-.136719-.019531-.265625-.03125-.40625-.03125-1.644531 0-3 1.355469-3 3s1.355469 3 3 3 3-1.355469 3-3c0-.796875-.328125-1.523437-.84375-2.0625l2.84375-5.28125-.75-.5c-1.347656-.894531-2.25-2.410156-2.25-4.15625 0-2.773437 2.226563-5 5-5 2.773438 0 5 2.226563 5 5 0 .585938-.097656 1.136719-.28125 1.65625l1.875.6875c.261719-.730469.40625-1.527344.40625-2.34375 0-3.855469-3.144531-7-7-7zm0 4c-1.644531 0-3 1.355469-3 3s1.355469 3 3 3c.140625 0 .269531-.011719.40625-.03125l2.75 4.375.5.8125.84375-.5c.734375-.425781 1.585938-.65625 2.5-.65625 2.773438 0 5 2.226563 5 5 0 2.773438-2.226562 5-5 5-1.488281 0-2.804687-.632812-3.71875-1.65625l-1.5 1.3125c1.28125 1.429688 3.152344 2.34375 5.21875 2.34375 3.855469 0 7-3.144531 7-7s-3.144531-7-7-7c-.921875 0-1.722656.363281-2.53125.6875l-2.28125-3.65625c.5-.535156.8125-1.25.8125-2.03125 0-1.644531-1.355469-3-3-3zm0 2c.5625 0 1 .4375 1 1s-.4375 1-1 1-1-.4375-1-1 .4375-1 1-1zm-8.15625 6.09375c-.800781.136719-1.601562.414063-2.34375.84375-3.335937 1.925781-4.488281 6.226563-2.5625 9.5625 1.925781 3.335938 6.222656 4.488281 9.5625 2.5625 1.910156-1.105469 2.941406-3.03125 3.25-5.0625h4.4375c.417969 1.15625 1.519531 2 2.8125 2 1.644531 0 3-1.355469 3-3s-1.355469-3-3-3c-1.292969 0-2.394531.84375-2.8125 2h-6.1875v1c0 1.726563-.890625 3.414063-2.5 4.34375-2.402344 1.386719-5.457031.558594-6.84375-1.84375s-.558594-5.457031 1.84375-6.84375c.53125-.308594 1.085938-.496094 1.65625-.59375zm1.15625 5.90625c.5625 0 1 .4375 1 1s-.4375 1-1 1-1-.4375-1-1 .4375-1 1-1zm14 0c.5625 0 1 .4375 1 1s-.4375 1-1 1-1-.4375-1-1 .4375-1 1-1z"/></svg>
export const WebhookIcon = () => <svg height="24" viewBox="0 0 32 32" width="24" xmlns="http://www.w3.org/2000/svg" className="icon">
<path d="m16 3c-3.855469 0-7 3.144531-7 7 0 2.09375 1.035156 3.871094 2.5 5.15625l-2.09375 3.875c-.136719-.019531-.265625-.03125-.40625-.03125-1.644531 0-3 1.355469-3 3s1.355469 3 3 3 3-1.355469 3-3c0-.796875-.328125-1.523437-.84375-2.0625l2.84375-5.28125-.75-.5c-1.347656-.894531-2.25-2.410156-2.25-4.15625 0-2.773437 2.226563-5 5-5 2.773438 0 5 2.226563 5 5 0 .585938-.097656 1.136719-.28125 1.65625l1.875.6875c.261719-.730469.40625-1.527344.40625-2.34375 0-3.855469-3.144531-7-7-7zm0 4c-1.644531 0-3 1.355469-3 3s1.355469 3 3 3c.140625 0 .269531-.011719.40625-.03125l2.75 4.375.5.8125.84375-.5c.734375-.425781 1.585938-.65625 2.5-.65625 2.773438 0 5 2.226563 5 5 0 2.773438-2.226562 5-5 5-1.488281 0-2.804687-.632812-3.71875-1.65625l-1.5 1.3125c1.28125 1.429688 3.152344 2.34375 5.21875 2.34375 3.855469 0 7-3.144531 7-7s-3.144531-7-7-7c-.921875 0-1.722656.363281-2.53125.6875l-2.28125-3.65625c.5-.535156.8125-1.25.8125-2.03125 0-1.644531-1.355469-3-3-3zm0 2c.5625 0 1 .4375 1 1s-.4375 1-1 1-1-.4375-1-1 .4375-1 1-1zm-8.15625 6.09375c-.800781.136719-1.601562.414063-2.34375.84375-3.335937 1.925781-4.488281 6.226563-2.5625 9.5625 1.925781 3.335938 6.222656 4.488281 9.5625 2.5625 1.910156-1.105469 2.941406-3.03125 3.25-5.0625h4.4375c.417969 1.15625 1.519531 2 2.8125 2 1.644531 0 3-1.355469 3-3s-1.355469-3-3-3c-1.292969 0-2.394531.84375-2.8125 2h-6.1875v1c0 1.726563-.890625 3.414063-2.5 4.34375-2.402344 1.386719-5.457031.558594-6.84375-1.84375s-.558594-5.457031 1.84375-6.84375c.53125-.308594 1.085938-.496094 1.65625-.59375zm1.15625 5.90625c.5625 0 1 .4375 1 1s-.4375 1-1 1-1-.4375-1-1 .4375-1 1-1zm14 0c.5625 0 1 .4375 1 1s-.4375 1-1 1-1-.4375-1-1 .4375-1 1-1z"/>
</svg>
export const UsersIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const UsersIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M15 19.128a9.38 9.38 0 002.625.372 9.337 9.337 0 004.121-.952 4.125 4.125 0 00-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 018.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0111.964-3.07M12 6.375a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zm8.25 2.25a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z" />
</svg>
export const CampaignsIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const CampaignsIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M10.34 15.84c-.688-.06-1.386-.09-2.09-.09H7.5a4.5 4.5 0 110-9h.75c.704 0 1.402-.03 2.09-.09m0 9.18c.253.962.584 1.892.985 2.783.247.55.06 1.21-.463 1.511l-.657.38c-.551.318-1.26.117-1.527-.461a20.845 20.845 0 01-1.44-4.282m3.102.069a18.03 18.03 0 01-.59-4.59c0-1.586.205-3.124.59-4.59m0 9.18a23.848 23.848 0 018.835 2.535M10.34 6.66a23.847 23.847 0 008.835-2.535m0 0A23.74 23.74 0 0018.795 3m.38 1.125a23.91 23.91 0 011.014 5.395m-1.014 8.855c-.118.38-.245.754-.38 1.125m.38-1.125a23.91 23.91 0 001.014-5.395m0-3.46c.495.413.811 1.035.811 1.73 0 .695-.316 1.317-.811 1.73m0-3.46a24.347 24.347 0 010 3.46" />
</svg>
export const JourneysIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16">
export const JourneysIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16" className="icon">
<path fillRule="evenodd" d="M6 3.5A1.5 1.5 0 0 1 7.5 2h1A1.5 1.5 0 0 1 10 3.5v1A1.5 1.5 0 0 1 8.5 6v1H11a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0V8h-5v.5a.5.5 0 0 1-1 0v-1A.5.5 0 0 1 5 7h2.5V6A1.5 1.5 0 0 1 6 4.5v-1zM8.5 5a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1zM3 11.5A1.5 1.5 0 0 1 4.5 10h1A1.5 1.5 0 0 1 7 11.5v1A1.5 1.5 0 0 1 5.5 14h-1A1.5 1.5 0 0 1 3 12.5v-1zm1.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-1zm4.5.5a1.5 1.5 0 0 1 1.5-1.5h1a1.5 1.5 0 0 1 1.5 1.5v1a1.5 1.5 0 0 1-1.5 1.5h-1A1.5 1.5 0 0 1 9 12.5v-1zm1.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-1z"/>
</svg>
export const ListsIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const ListsIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M8.25 6.75h12M8.25 12h12m-12 5.25h12M3.75 6.75h.007v.008H3.75V6.75zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zM3.75 12h.007v.008H3.75V12zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm-.375 5.25h.007v.008H3.75v-.008zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" />
</svg>
export const SettingsIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const SettingsIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M10.343 3.94c.09-.542.56-.94 1.11-.94h1.093c.55 0 1.02.398 1.11.94l.149.894c.07.424.384.764.78.93.398.164.855.142 1.205-.108l.737-.527a1.125 1.125 0 011.45.12l.773.774c.39.389.44 1.002.12 1.45l-.527.737c-.25.35-.272.806-.107 1.204.165.397.505.71.93.78l.893.15c.543.09.94.56.94 1.109v1.094c0 .55-.397 1.02-.94 1.11l-.893.149c-.425.07-.765.383-.93.78-.165.398-.143.854.107 1.204l.527.738c.32.447.269 1.06-.12 1.45l-.774.773a1.125 1.125 0 01-1.449.12l-.738-.527c-.35-.25-.806-.272-1.203-.107-.397.165-.71.505-.781.929l-.149.894c-.09.542-.56.94-1.11.94h-1.094c-.55 0-1.019-.398-1.11-.94l-.148-.894c-.071-.424-.384-.764-.781-.93-.398-.164-.854-.142-1.204.108l-.738.527c-.447.32-1.06.269-1.45-.12l-.773-.774a1.125 1.125 0 01-.12-1.45l.527-.737c.25-.35.273-.806.108-1.204-.165-.397-.505-.71-.93-.78l-.894-.15c-.542-.09-.94-.56-.94-1.109v-1.094c0-.55.398-1.02.94-1.11l.894-.149c.424-.07.765-.383.93-.78.165-.398.143-.854-.107-1.204l-.527-.738a1.125 1.125 0 01.12-1.45l.773-.773a1.125 1.125 0 011.45-.12l.737.527c.35.25.807.272 1.204.107.397-.165.71-.505.78-.929l.15-.894z" />
<path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
export const MoonIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const MoonIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M21.752 15.002A9.718 9.718 0 0118 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 003 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 009.002-5.998z" />
</svg>
export const SunIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const SunIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
</svg>
export const SendIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const SendIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5" />
</svg>
export const RestartIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const RestartIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" />
</svg>
export const ForbiddenIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const ForbiddenIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636" />
</svg>
export const CloseIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const CloseIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
export const UploadIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
export const UploadIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5" />
</svg>
export const ImageIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
export const ImageIcon = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" />
</svg>
export const EntranceStepIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16" className="icon">
<path fillRule="evenodd" d="M6 3.5a.5.5 0 0 1 .5-.5h8a.5.5 0 0 1 .5.5v9a.5.5 0 0 1-.5.5h-8a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 0-1 0v2A1.5 1.5 0 0 0 6.5 14h8a1.5 1.5 0 0 0 1.5-1.5v-9A1.5 1.5 0 0 0 14.5 2h-8A1.5 1.5 0 0 0 5 3.5v2a.5.5 0 0 0 1 0v-2z"/>
<path fillRule="evenodd" d="M11.854 8.354a.5.5 0 0 0 0-.708l-3-3a.5.5 0 1 0-.708.708L10.293 7.5H1.5a.5.5 0 0 0 0 1h8.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3z"/>
</svg>
)
export const ActionStepIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16" className="icon">
<path d="M5.52.359A.5.5 0 0 1 6 0h4a.5.5 0 0 1 .474.658L8.694 6H12.5a.5.5 0 0 1 .395.807l-7 9a.5.5 0 0 1-.873-.454L6.823 9.5H3.5a.5.5 0 0 1-.48-.641l2.5-8.5z"/>
</svg>
)
export const LinkStepIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16" className="icon">
<path fillRule="evenodd" d="M11 4a4 4 0 1 0 0 8 4 4 0 0 0 0-8zM6.025 7.5a5 5 0 1 1 0 1H4A1.5 1.5 0 0 1 2.5 10h-1A1.5 1.5 0 0 1 0 8.5v-1A1.5 1.5 0 0 1 1.5 6h1A1.5 1.5 0 0 1 4 7.5h2.025zM11 5a.5.5 0 0 1 .5.5v2h2a.5.5 0 0 1 0 1h-2v2a.5.5 0 0 1-1 0v-2h-2a.5.5 0 0 1 0-1h2v-2A.5.5 0 0 1 11 5zM1.5 7a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-1z"/>
</svg>
)
export const UpdateStepIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16" className="icon">
<path d="M6 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-5 6s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1H1zM11 3.5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5zm.5 2.5a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1h-4zm2 3a.5.5 0 0 0 0 1h2a.5.5 0 0 0 0-1h-2zm0 3a.5.5 0 0 0 0 1h2a.5.5 0 0 0 0-1h-2z"/>
</svg>
)
export const DelayStepIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16" className="icon">
<path d="M2.5 15a.5.5 0 1 1 0-1h1v-1a4.5 4.5 0 0 1 2.557-4.06c.29-.139.443-.377.443-.59v-.7c0-.213-.154-.451-.443-.59A4.5 4.5 0 0 1 3.5 3V2h-1a.5.5 0 0 1 0-1h11a.5.5 0 0 1 0 1h-1v1a4.5 4.5 0 0 1-2.557 4.06c-.29.139-.443.377-.443.59v.7c0 .213.154.451.443.59A4.5 4.5 0 0 1 12.5 13v1h1a.5.5 0 0 1 0 1h-11zm2-13v1c0 .537.12 1.045.337 1.5h6.326c.216-.455.337-.963.337-1.5V2h-7zm3 6.35c0 .701-.478 1.236-1.011 1.492A3.5 3.5 0 0 0 4.5 13s.866-1.299 3-1.48V8.35zm1 0v3.17c2.134.181 3 1.48 3 1.48a3.5 3.5 0 0 0-1.989-3.158C8.978 9.586 8.5 9.052 8.5 8.351z"/>
</svg>
)
export const ExperimentStepIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16" className="icon">
<path d="M7 7V1.414a1 1 0 0 1 2 0V2h5a1 1 0 0 1 .8.4l.975 1.3a.5.5 0 0 1 0 .6L14.8 5.6a1 1 0 0 1-.8.4H9v10H7v-5H2a1 1 0 0 1-.8-.4L.225 9.3a.5.5 0 0 1 0-.6L1.2 7.4A1 1 0 0 1 2 7h5zm1 3V8H2l-.75 1L2 10h6zm0-5h6l.75-1L14 3H8v2z"/>
</svg>
)
export const GateStepIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16" className="icon">
<path fillRule="evenodd" d="M6 3.5A1.5 1.5 0 0 1 7.5 2h1A1.5 1.5 0 0 1 10 3.5v1A1.5 1.5 0 0 1 8.5 6v1H11a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0V8h-5v.5a.5.5 0 0 1-1 0v-1A.5.5 0 0 1 5 7h2.5V6A1.5 1.5 0 0 1 6 4.5v-1zm-3 8A1.5 1.5 0 0 1 4.5 10h1A1.5 1.5 0 0 1 7 11.5v1A1.5 1.5 0 0 1 5.5 14h-1A1.5 1.5 0 0 1 3 12.5v-1zm6 0a1.5 1.5 0 0 1 1.5-1.5h1a1.5 1.5 0 0 1 1.5 1.5v1a1.5 1.5 0 0 1-1.5 1.5h-1A1.5 1.5 0 0 1 9 12.5v-1z"/>
</svg>
)
export const SearchIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16" className="icon">
<path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"/>
</svg>
)
export const CopyIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.75} stroke="currentColor" className="icon">
<path strokeLinecap="round" strokeLinejoin="round" d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184" />
</svg>
)

View file

@ -33,12 +33,12 @@ export default function Login() {
</div>
<div className="auth-step">
<h1>Login</h1>
{error && (
<Alert variant="error" title="Error">{error}</Alert>
)}
<FormWrapper<LoginBasicParams>
onSubmit={handleLogin}>
{form => <>
{error && (
<Alert variant="error" title="Error">{error}</Alert>
)}
<TextField form={form} name="email" />
<TextField form={form} name="password" type="password" />
</>}

View file

@ -8,10 +8,12 @@ import TextField from '../../ui/form/TextField'
import FormWrapper from '../../ui/form/FormWrapper'
import Modal from '../../ui/Modal'
import { SearchTable, useSearchTableState } from '../../ui/SearchTable'
import { ArchiveIcon, PlusIcon } from '../../ui/icons'
import { ArchiveIcon, CopyIcon, PlusIcon } from '../../ui/icons'
import Menu, { MenuItem } from '../../ui/Menu'
import { SingleSelect } from '../../ui/form/SingleSelect'
import { snakeToTitle } from '../../utils'
import { toast } from 'react-hot-toast'
import Alert from '../../ui/Alert'
export default function ProjectApiKeys() {
@ -27,6 +29,13 @@ export default function ProjectApiKeys() {
}
}
const handleCopy = async (event: React.MouseEvent<HTMLButtonElement, MouseEvent>, value: string) => {
await navigator.clipboard.writeText(value)
event.stopPropagation()
toast.success('Copied API Key')
}
return (
<>
<SearchTable
@ -36,9 +45,17 @@ export default function ProjectApiKeys() {
{ key: 'scope' },
{
key: 'role',
cell: ({ item }) => item.scope === 'public' ? '--' : item.role,
cell: ({ item }) => item.scope === 'public' ? undefined : item.role,
},
{
key: 'value',
cell: ({ item }) => (
<div className="cell-content">
{item.value}
<Button icon={<CopyIcon />} size="small" variant="plain" onClickCapture={async (e) => await handleCopy(e, item.value)} />
</div>
),
},
{ key: 'value' },
{ key: 'description' },
{
key: 'options',
@ -69,6 +86,7 @@ export default function ProjectApiKeys() {
open={Boolean(editing)}
onClose={() => setEditing(null)}
>
<Alert variant="plain" title="Key Value">{editing?.value}</Alert>
{
editing && (
<FormWrapper<ProjectApiKey>

View file

@ -39,7 +39,12 @@ export default function IntegrationModal({ onChange, provider, ...props }: Integ
return <Modal
{...props}
title={meta ? 'Setup Integration' : 'Integrations'}
title={meta
? provider?.id
? `${provider?.name} (${meta.name})`
: 'Setup Integration'
: 'Integrations'
}
size="regular"
>
{!meta && <>
@ -71,7 +76,20 @@ export default function IntegrationModal({ onChange, provider, ...props }: Integ
defaultValues={provider}>
{form =>
<>
<Alert title={meta.name} variant="plain">Fill out the fields below to setup this integration. For more information on this integration please see the documentation on our website</Alert>
{provider?.id
? <>
<h4>Details</h4>
<TextField name="id" label="ID" value={provider.id} disabled />
{meta.paths && Object.keys(meta.paths).map(key => {
const value = meta.paths?.[key]
const url = `${window.location.origin}/providers/${provider?.id}${value}`
return <TextField name="unsubscribe" key={key} label={key} value={url} disabled />
})}
</>
: <Alert title={meta.name} variant="plain">Fill out the fields below to setup this integration. For more information on this integration please see the documentation on our website</Alert>
}
<h4>Config</h4>
<TextField form={form} name="name" required />
<SchemaFields parent="data" schema={meta.schema.properties.data} form={form} />
</>

View file

@ -28,7 +28,6 @@ export default function Integrations() {
{ key: 'name' },
{ key: 'type' },
{ key: 'group' },
{ key: 'external_id' },
{ key: 'created_at' },
]}
itemKey={({ item }) => item.id}

View file

@ -40,7 +40,9 @@ Find below a list of all environment variables that can be set at launch to conf
### Auth
| key | type | required |
|--|--|--|
| AUTH_DRIVER | 'openid' or 'saml' | true |
| AUTH_DRIVER | 'basic', 'openid' or 'saml' | true |
| AUTH_BASIC_EMAIL | string | If driver is Basic |
| AUTH_BASIC_PASSWORD | string | If driver is Basic |
| AUTH_SAML_CALLBACK_URL | string | If driver is SAML |
| AUTH_SAML_ENTRY_POINT_URL | string | If driver is SAML |
| AUTH_SAML_ISSUER | string | If driver is SAML |

View file

@ -6,7 +6,7 @@ Amazon SES is quite possibly the most cost effective email service available. If
Once you are ready to use SES you must request approval to send emails in production. In general it is recommended you try and request access earlier than later as you may be denied. The process usually does not take longer than a day, but providing lots of context is important.
To request access, do the following:
1. Navigate to the AWS SES portal (https://console.aws.amazon.com/ses/home)
1. Navigate to the [AWS SES portal](https://console.aws.amazon.com/ses/home)
2. On your account dashboard you should see an alert informing you that your account is in sandbox mode. Hit the `Request Production Access` button.
3. Fill out the form with the details of your product and submit. The more details the better as it will improve your chances of being approved.
@ -40,14 +40,14 @@ To request access, do the following:
Email sending is not hte only important part, you also need to keep track of things like email opens, clicks, unsubscribes, bounces and complaints. Parcelvoy automatically takes care of opens, clicks and unsubscribes for you, but bounces and complaints require notifications from SES.
To setup inbound notifications, do the following:
1. Open the [https://console.aws.amazon.com/sns/home](Amazon SNS console) and choose `Topics`.
1. Open the [Amazon SNS console](https://console.aws.amazon.com/sns/home) and choose `Topics`.
2. On the Topics page, choose Create topic.
3. In the `Details` section of the Create topic page, choose Standard for type and provider a name.
4. Choose `Create topic`
5. From the Topic details of the topic that you created, choose `Create subscription`
6. For Protocol, select `HTTPS` and enter the Parcelvoy SES unsubscribe URL for your provider (which can be found on the provider details screen)
7. Hit save
8. Navigate to the [https://console.aws.amazon.com/ses/home]SES console) and choose `Verified identities`
8. Navigate to the [SES console](https://console.aws.amazon.com/ses/home) and choose `Verified identities`
9. Select your previously created identity and go to the `Notifications` tab.
10. Disable Email feedback forwarding
11. Under Feedback notifications, hit `Edit`

View file

@ -154,6 +154,10 @@ h1, h2, h3, h4, h5, h6 {
font-weight: 600;
}
.theme-doc-markdown.markdown a {
text-decoration: underline;
}
.footer {
background: var(--color-primary);
}