-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
309 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,8 @@ | ||
tests/** | ||
**/*.test.ts | ||
tsconfig.json | ||
.env | ||
.volumes/ | ||
yarn-error.log | ||
.github/** | ||
src/** | ||
coverage/ | ||
.dependabot/ | ||
.github/ | ||
.husky/ | ||
coverage/ | ||
dist/tests/ | ||
src/ | ||
tsconfig.json | ||
yarn-error.log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,7 +16,7 @@ | |
## Features | ||
|
||
- Send emails | ||
- Webhooks _(coming soon)_ | ||
- Webhooks | ||
- Inbound emails _(coming soon)_ | ||
|
||
## Installation | ||
|
@@ -53,6 +53,46 @@ server.ohmysmtp.sendEmail({ | |
|
||
You can provide the API token via the configuration or via the `OHMYSMTP_API_TOKEN` environment variable. | ||
|
||
## Webhooks | ||
|
||
You can enable reception of webhook events in the configuration object: | ||
|
||
```ts | ||
server.register(fastifyOhMySMTP, { | ||
apiToken: 'my-api-token', | ||
webhooks: { | ||
// Where to attach the webhook endpoint (POST) | ||
path: '/webhook', | ||
|
||
/* | ||
* An object map of handlers, where keys are the event types, listed here: | ||
* https://docs.ohmysmtp.com/guide/webhooks#email-events | ||
* | ||
* Values are async handlers that take as argument: | ||
* - The payload object of the webhook event | ||
* - The request object from Fastify | ||
* - The Fastify instance | ||
*/ | ||
handlers: { | ||
'email.spam': async (event, req, fastify) => { | ||
req.log.info(event, 'Spam detected') | ||
await fastify.ohmysmtp.sendEmail({ | ||
from: '[email protected]', | ||
to: '[email protected]', | ||
subject: 'Spam detected', | ||
textbody: `Check event ${event.id} on OhMySMTP` | ||
}) | ||
} | ||
}, | ||
|
||
// You can pass additional Fastify route props here: | ||
routeConfig: { | ||
logLevel: 'warn' | ||
} | ||
} | ||
}) | ||
``` | ||
|
||
## License | ||
|
||
[MIT](https://github.com/47ng/fastify-ohmysmtp/blob/main/LICENSE) - Made with ❤️ by [François Best](https://francoisbest.com) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import Fastify from 'fastify' | ||
import fastifyOhMySMTP from '../../../dist' | ||
|
||
const server = Fastify({ | ||
logger: true | ||
}) | ||
|
||
server.register(fastifyOhMySMTP, { | ||
apiToken: 'my-api-token', | ||
webhooks: { | ||
path: '/webhook', | ||
handlers: { | ||
'email.spam': async (event, req, app) => { | ||
req.log.info(event) | ||
await app.ohmysmtp.sendEmail({ | ||
from: '[email protected]', | ||
to: '[email protected]', | ||
subject: 'Spam detected', | ||
textbody: `Check event ${event.id} on OhMySMTP` | ||
}) | ||
} | ||
} | ||
} | ||
}) | ||
|
||
server.ready(error => { | ||
if (error) { | ||
throw error | ||
} | ||
console.log(server.printPlugins()) | ||
console.log(server.printRoutes()) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import { webhookBody, webhookBodySchema, WebhookJSONBody } from '../webhooks' | ||
|
||
describe('webhooks', () => { | ||
test('body schema', () => { | ||
const expected = { | ||
$schema: 'http://json-schema.org/draft-07/schema#', | ||
type: 'object', | ||
properties: { | ||
event: { | ||
type: 'string', | ||
enum: [ | ||
'email.queued', | ||
'email.delivered', | ||
'email.deferred', | ||
'email.bounced', | ||
'email.spam' | ||
] | ||
}, | ||
payload: { | ||
type: 'object', | ||
properties: { | ||
status: { | ||
type: 'string', | ||
enum: ['queued', 'delivered', 'deferred', 'bounced', 'spam'] | ||
}, | ||
id: { type: 'number' }, | ||
domain_id: { type: 'number' }, | ||
created_at: { | ||
anyOf: [ | ||
{ type: 'integer', exclusiveMinimum: 0 }, | ||
{ type: 'string', format: 'date-time' } | ||
] | ||
}, | ||
updated_at: { $ref: '#/properties/payload/properties/created_at' }, | ||
from: { type: 'string', format: 'email' }, | ||
to: { type: ['string', 'null'] }, | ||
htmlbody: { type: ['string', 'null'] }, | ||
textbody: { type: ['string', 'null'] }, | ||
cc: { type: ['string', 'null'] }, | ||
bcc: { type: ['string', 'null'] }, | ||
subject: { type: ['string', 'null'] }, | ||
replyto: { | ||
anyOf: [{ type: 'string', format: 'email' }, { type: 'null' }] | ||
}, | ||
message_id: { type: 'string' }, | ||
list_unsubscribe: { type: ['string', 'null'] } | ||
}, | ||
required: [ | ||
'status', | ||
'id', | ||
'domain_id', | ||
'created_at', | ||
'updated_at', | ||
'from', | ||
'to', | ||
'htmlbody', | ||
'textbody', | ||
'cc', | ||
'bcc', | ||
'subject', | ||
'replyto', | ||
'message_id', | ||
'list_unsubscribe' | ||
], | ||
additionalProperties: false | ||
} | ||
}, | ||
required: ['event', 'payload'], | ||
additionalProperties: false | ||
} | ||
expect(webhookBodySchema).toEqual(expected) | ||
}) | ||
|
||
test('parse correctly shaped webhook body', () => { | ||
const body: WebhookJSONBody = { | ||
event: 'email.delivered', | ||
payload: { | ||
status: 'delivered', | ||
id: 1, | ||
domain_id: 2, | ||
created_at: 123, | ||
updated_at: 123, | ||
message_id: '', | ||
from: '[email protected]', | ||
replyto: null, | ||
to: '[email protected]', | ||
cc: null, | ||
bcc: null, | ||
subject: 'Hey!', | ||
htmlbody: 'Hello', | ||
textbody: 'Hello', | ||
list_unsubscribe: null | ||
} | ||
} | ||
const result = webhookBody.safeParse(body) | ||
expect(result.success).toBe(true) | ||
if (result.success) { | ||
expect(result.data.payload.created_at.valueOf()).toEqual(123) | ||
} | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import type { FastifyInstance, FastifyRequest } from 'fastify' | ||
import { z } from 'zod' | ||
import zodToJsonSchema from 'zod-to-json-schema' | ||
|
||
const dateLike = z | ||
.union([z.number().positive().int(), z.date()]) | ||
.refine(value => Number.isSafeInteger(new Date(value).valueOf()), { | ||
message: 'Invalid date input' | ||
}) | ||
.transform(value => new Date(value)) | ||
|
||
export const webhookBody = z.object({ | ||
event: z.enum([ | ||
'email.queued', | ||
'email.delivered', | ||
'email.deferred', | ||
'email.bounced', | ||
'email.spam' | ||
] as const), | ||
payload: z.object({ | ||
status: z.enum([ | ||
'queued', | ||
'delivered', | ||
'deferred', | ||
'bounced', | ||
'spam' | ||
] as const), | ||
id: z.number(), | ||
domain_id: z.number(), | ||
created_at: dateLike, | ||
updated_at: dateLike, | ||
from: z.string().email(), | ||
to: z.string().nullable(), | ||
htmlbody: z.string().nullable(), | ||
textbody: z.string().nullable(), | ||
cc: z.string().nullable(), | ||
bcc: z.string().nullable(), | ||
subject: z.string().nullable(), | ||
replyto: z.string().email().nullable(), | ||
message_id: z.string(), | ||
list_unsubscribe: z.string().nullable() | ||
}) | ||
}) | ||
|
||
export type WebhookJSONBody = z.input<typeof webhookBody> | ||
export type WebhookBody = z.output<typeof webhookBody> | ||
export const webhookBodySchema = zodToJsonSchema(webhookBody) | ||
|
||
export type WebhookHandlersMap = Partial<{ | ||
[Event in WebhookBody['event']]: ( | ||
payload: WebhookBody['payload'], | ||
request: FastifyRequest, | ||
fastify: FastifyInstance | ||
) => Promise<void> | ||
}> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.