mirror of
https://github.com/logto-io/logto.git
synced 2025-01-13 21:30:30 -05:00
refactor(connector): support mailgun endpoint (#4290)
This commit is contained in:
parent
56b0a2cd18
commit
521b3a5c51
6 changed files with 81 additions and 14 deletions
|
@ -21,6 +21,7 @@ The official Logto connector for Mailgun email service.
|
|||
|
||||
## Basic configuration
|
||||
|
||||
- Fill out the `endpoint` field when you are using a different Mailgun API endpoint, for example, EU region should be `https://api.eu.mailgun.net`. The default value is `https://api.mailgun.net`.
|
||||
- Fill out the `domain` field with the domain you have registered in your Mailgun account. This value can be found in the **Domains** section of the Mailgun dashboard. The domain should be in the format `example.com`, without the `https://` or `http://` prefix.
|
||||
- Fill out the `apiKey` field with the API key you have generated in your Mailgun account.
|
||||
- Fill out the `from` field with the email address you want to send emails from. This email address must be registered in your Mailgun account. The email address should be in the format `Sender Name <sender@example.com>`.
|
||||
|
|
|
@ -17,12 +17,19 @@ export const defaultMetadata: ConnectorMetadata = {
|
|||
},
|
||||
readme: './README.md',
|
||||
formItems: [
|
||||
{
|
||||
key: 'endpoint',
|
||||
label: 'Mailgun endpoint',
|
||||
type: ConnectorConfigFormItemType.Text,
|
||||
required: false,
|
||||
placeholder: 'https://api.mailgun.net',
|
||||
},
|
||||
{
|
||||
key: 'domain',
|
||||
label: 'Domain',
|
||||
type: ConnectorConfigFormItemType.Text,
|
||||
required: true,
|
||||
placeholder: 'https://your-mailgun-domain.com',
|
||||
placeholder: 'your-mailgun-domain.com',
|
||||
},
|
||||
{
|
||||
key: 'apiKey',
|
||||
|
|
|
@ -25,8 +25,11 @@ const baseConfig: Partial<MailgunConfig> = {
|
|||
*
|
||||
* @param expectation - The expected request body.
|
||||
*/
|
||||
const nockMessages = (expectation: Record<string, string | string[] | undefined>) =>
|
||||
nock('https://api.mailgun.net')
|
||||
const nockMessages = (
|
||||
expectation: Record<string, string | string[] | undefined>,
|
||||
endpoint = 'https://api.mailgun.net'
|
||||
) =>
|
||||
nock(endpoint)
|
||||
.post(`/v3/${domain}/messages`)
|
||||
.basicAuth({ user: 'api', pass: apiKey })
|
||||
.reply((_, body, callback) => {
|
||||
|
@ -104,6 +107,39 @@ describe('Maligun connector', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should send email with template and EU endpoint', async () => {
|
||||
nockMessages(
|
||||
{
|
||||
from: 'foo@example.com',
|
||||
to: 'bar@example.com',
|
||||
subject: 'Verification code is 123456',
|
||||
template: 'template',
|
||||
'h:X-Mailgun-Variables': JSON.stringify({ foo: 'bar', code: '123456' }),
|
||||
},
|
||||
'https://api.eu.mailgun.net'
|
||||
);
|
||||
|
||||
getConfig.mockResolvedValue({
|
||||
...baseConfig,
|
||||
endpoint: 'https://api.eu.mailgun.net',
|
||||
deliveries: {
|
||||
[VerificationCodeType.Generic]: {
|
||||
template: 'template',
|
||||
variables: { foo: 'bar' },
|
||||
subject: 'Verification code is {{code}}',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await connector.sendMessage({
|
||||
to: 'bar@example.com',
|
||||
type: VerificationCodeType.Generic,
|
||||
payload: {
|
||||
code: '123456',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should fall back to generic template if type not found', async () => {
|
||||
nockMessages({
|
||||
from: 'foo@example.com',
|
||||
|
|
|
@ -49,7 +49,7 @@ const sendMessage = (getConfig: GetConnectorConfig): SendMessageFunction => {
|
|||
const config = inputConfig ?? (await getConfig(defaultMetadata.id));
|
||||
validateConfig(config, mailgunConfigGuard);
|
||||
|
||||
const { domain, apiKey, from, deliveries } = config;
|
||||
const { endpoint, domain, apiKey, from, deliveries } = config;
|
||||
const type = supportTemplateGuard.safeParse(typeInput);
|
||||
|
||||
if (!type.success) {
|
||||
|
@ -63,15 +63,18 @@ const sendMessage = (getConfig: GetConnectorConfig): SendMessageFunction => {
|
|||
}
|
||||
|
||||
try {
|
||||
return await got.post(`https://api.mailgun.net/v3/${domain}/messages`, {
|
||||
username: 'api',
|
||||
password: apiKey,
|
||||
form: {
|
||||
from,
|
||||
to,
|
||||
...removeUndefinedKeys(getDataFromDeliveryConfig(template, code)),
|
||||
},
|
||||
});
|
||||
return await got.post(
|
||||
new URL(`/v3/${domain}/messages`, endpoint ?? 'https://api.mailgun.net').toString(),
|
||||
{
|
||||
username: 'api',
|
||||
password: apiKey,
|
||||
form: {
|
||||
from,
|
||||
to,
|
||||
...removeUndefinedKeys(getDataFromDeliveryConfig(template, code)),
|
||||
},
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
if (error instanceof HTTPError) {
|
||||
const {
|
||||
|
|
|
@ -48,7 +48,7 @@ describe('Mailgun config guard', () => {
|
|||
expect(() => mailgunConfigGuard.parse(validConfig)).not.toThrow();
|
||||
});
|
||||
|
||||
it('should fail with invalid config', () => {
|
||||
it('should fail with invalid delivery config', () => {
|
||||
const invalidConfig = {
|
||||
domain: 'example.com',
|
||||
apiKey: 'key',
|
||||
|
@ -62,4 +62,21 @@ describe('Mailgun config guard', () => {
|
|||
};
|
||||
expect(() => mailgunConfigGuard.parse(invalidConfig)).toThrow();
|
||||
});
|
||||
|
||||
it('should fail with invalid endpoint', () => {
|
||||
const invalidConfig = {
|
||||
endpoint: 'https://api.mailgun1.net',
|
||||
domain: 'example.com',
|
||||
apiKey: 'key',
|
||||
from: 'from',
|
||||
deliveries: {
|
||||
[VerificationCodeType.ForgotPassword]: {
|
||||
html: 'html',
|
||||
subject: 'subject',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(() => mailgunConfigGuard.parse(invalidConfig)).toThrow();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -53,6 +53,8 @@ const templateConfigGuard = z.union([
|
|||
]) satisfies z.ZodType<DeliveryConfig>;
|
||||
|
||||
export type MailgunConfig = {
|
||||
/** Mailgun endpoint. For EU region, use `https://api.eu.mailgun.net`. */
|
||||
endpoint?: string;
|
||||
/** Mailgun domain. */
|
||||
domain: string;
|
||||
/** Mailgun API key. */
|
||||
|
@ -67,6 +69,7 @@ export type MailgunConfig = {
|
|||
};
|
||||
|
||||
export const mailgunConfigGuard = z.object({
|
||||
endpoint: z.string().url().endsWith('.mailgun.net').optional(),
|
||||
domain: z.string(),
|
||||
apiKey: z.string(),
|
||||
from: z.string(),
|
||||
|
|
Loading…
Add table
Reference in a new issue