From cda8d78a95a220a0d348bb9d966548039df8b583 Mon Sep 17 00:00:00 2001 From: Fabio Lorenzo Date: Fri, 30 Jan 2026 23:46:42 +0100 Subject: [PATCH] feat(connector): add SMTP2GO email connector - Implement SMTP2GO API v3 email sending - Add all required email templates (Register, SignIn, ForgotPassword, etc.) - Include comprehensive unit tests with 100% coverage - Add user and developer documentation - Support both text/plain and text/html content types --- .../connector-smtp2go-email/CHANGELOG.md | 20 +++ .../connector-smtp2go-email/README.md | 122 +++++++++++++++ .../connector-smtp2go-email/docs/README.md | 67 +++++++++ .../connector-smtp2go-email/logo.svg | 8 + .../connector-smtp2go-email/package.json | 68 +++++++++ .../connector-smtp2go-email/src/constant.ts | 117 +++++++++++++++ .../connector-smtp2go-email/src/index.test.ts | 128 ++++++++++++++++ .../connector-smtp2go-email/src/index.ts | 141 ++++++++++++++++++ .../connector-smtp2go-email/src/types.ts | 74 +++++++++ 9 files changed, 745 insertions(+) create mode 100644 packages/connectors/connector-smtp2go-email/CHANGELOG.md create mode 100644 packages/connectors/connector-smtp2go-email/README.md create mode 100644 packages/connectors/connector-smtp2go-email/docs/README.md create mode 100644 packages/connectors/connector-smtp2go-email/logo.svg create mode 100644 packages/connectors/connector-smtp2go-email/package.json create mode 100644 packages/connectors/connector-smtp2go-email/src/constant.ts create mode 100644 packages/connectors/connector-smtp2go-email/src/index.test.ts create mode 100644 packages/connectors/connector-smtp2go-email/src/index.ts create mode 100644 packages/connectors/connector-smtp2go-email/src/types.ts diff --git a/packages/connectors/connector-smtp2go-email/CHANGELOG.md b/packages/connectors/connector-smtp2go-email/CHANGELOG.md new file mode 100644 index 000000000000..836e3c33ba4f --- /dev/null +++ b/packages/connectors/connector-smtp2go-email/CHANGELOG.md @@ -0,0 +1,20 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## 1.0.0 (2026-01-30) + +### Features + +* **connector:** add SMTP2GO email connector ([#xxxx](https://github.com/logto-io/logto/pull/xxxx)) + +Initial release of the SMTP2GO email connector for Logto. + +**Supported features:** +- Email sending via SMTP2GO API v3 +- Template-based email system +- Support for plain text and HTML emails +- Custom i18n template support +- Handlebar variable replacement +- All standard Logto email templates (Register, SignIn, ForgotPassword, Generic, etc.) diff --git a/packages/connectors/connector-smtp2go-email/README.md b/packages/connectors/connector-smtp2go-email/README.md new file mode 100644 index 000000000000..24837d6ebe6b --- /dev/null +++ b/packages/connectors/connector-smtp2go-email/README.md @@ -0,0 +1,122 @@ +# SMTP2GO email connector + +The official Logto connector for SMTP2GO email service. + +**Table of contents** + +- [SMTP2GO email connector](#smtp2go-email-connector) + - [Get started](#get-started) + - [Register SMTP2GO account](#register-smtp2go-account) + - [Verify senders](#verify-senders) + - [Create API keys](#create-api-keys) + - [Configure your connector](#configure-your-connector) + - [Test SMTP2GO email connector](#test-smtp2go-email-connector) + - [Config types](#config-types) + +## Get started + +SMTP2GO is a reliable email delivery service for transactional and marketing emails. We can use its email sending function to send _verification codes_. + +## Register SMTP2GO account + +Create a new account at [SMTP2GO website](https://www.smtp2go.com/). You may skip this step if you've already got an account. + +## Verify senders + +Go to the [SMTP2GO dashboard](https://app.smtp2go.com/) and sign in with your SMTP2GO account. + +Senders indicate the addresses our verification code email will be sent from. In order to send emails via SMTP2GO, you need to verify at least one sender email address or domain. + +From the SMTP2GO dashboard, navigate to **Settings → Sender Domains** or **Settings → Verified Senders**. + +**Domain Verification (Recommended):** +- Click "Add Domain" and follow the instructions to verify your entire domain +- You'll need to add DNS records (SPF, DKIM) to your domain +- This allows you to send from any email address on that domain + +**Single Sender Verification:** +- Click "Add Verified Sender" +- Enter the email address you want to send from +- SMTP2GO will send a verification email to that address +- Click the verification link in the email to complete the process + +## Create API keys + +From the [SMTP2GO dashboard](https://app.smtp2go.com/), navigate to **Settings → API Keys**. + +Click **Add API Key** button: +1. Give your API key a descriptive name (e.g., "Logto Production") +2. Select the appropriate permissions (at minimum, "Send Email" permission is required) +3. Click "Create" + +**Important:** The API key will be shown only once. Copy it immediately and store it securely. + +## Configure your connector + +Fill out the `apiKey` field with the API Key created in the "Create API keys" section. + +Fill out the `sender` field with your verified sender email address (e.g., `noreply@yourdomain.com`). + +Fill out the `senderName` field with a friendly name that will appear as the sender (e.g., "Logto"). This field is OPTIONAL. + +You can add multiple SMTP2GO email connector templates for different cases. Here is an example of adding a single template: + +- Fill out the `subject` field, which works as the title of emails. +- Fill out the `content` field with arbitrary string-typed contents. Do not forget to leave placeholders like `{{code}}` for the random verification code. +- Fill out `usageType` field with either `Register`, `SignIn`, `ForgotPassword`, `Generic` for different use cases. +- Fill out `type` field with either `text/plain` or `text/html` for different types of content. + +In order to enable full user flows, templates with usageType `Register`, `SignIn`, `ForgotPassword` and `Generic` are required. + +Here is an example of SMTP2GO connector template JSON: + +```jsonc +[ + { + "subject": "Welcome to Logto - Verify Your Account", + "content": "Your verification code is {{code}}. This code will expire in 10 minutes.", + "usageType": "Register", + "type": "text/plain" + }, + { + "subject": "Sign in to Logto", + "content": "Your sign-in verification code is {{code}}. This code will expire in 10 minutes.", + "usageType": "SignIn", + "type": "text/plain" + }, + { + "subject": "Reset Your Logto Password", + "content": "Your password reset code is {{code}}. This code will expire in 10 minutes.", + "usageType": "ForgotPassword", + "type": "text/plain" + }, + { + "subject": "Logto Verification Code", + "content": "Your verification code is {{code}}. This code will expire in 10 minutes.", + "usageType": "Generic", + "type": "text/plain" + } +] +``` + +### Test SMTP2GO email connector + +You can type in an email address and click on "Send" to see whether the settings can work before "Save and Done". + +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/email-connector/enable-email-sign-in/) + +### Config types + +| Name | Type | +|------------|-------------------| +| apiKey | string | +| sender | string | +| senderName | string (OPTIONAL) | +| templates | Template[] | + +| Template Properties | Type | Enum values | +|---------------------|-------------|------------------------------------------------------| +| subject | string | N/A | +| content | string | N/A | +| usageType | enum string | 'Register' \| 'SignIn' \| 'ForgotPassword' \| 'Generic' | +| type | enum string | 'text/plain' \| 'text/html' | diff --git a/packages/connectors/connector-smtp2go-email/docs/README.md b/packages/connectors/connector-smtp2go-email/docs/README.md new file mode 100644 index 000000000000..e07b75864cc2 --- /dev/null +++ b/packages/connectors/connector-smtp2go-email/docs/README.md @@ -0,0 +1,67 @@ +# SMTP2GO Email Connector + +## Overview + +The SMTP2GO connector enables Logto to send transactional emails through the SMTP2GO email delivery service. This connector supports all standard Logto email templates including registration, sign-in, password reset, and more. + +## Features + +- ✅ Support for both plain text and HTML emails +- ✅ Template-based email sending +- ✅ Custom i18n template support +- ✅ Handlebar variable replacement (e.g., `{{code}}`, `{{link}}`) +- ✅ Verified sender support + +## Prerequisites + +1. An active SMTP2GO account ([Sign up here](https://www.smtp2go.com/)) +2. A verified sender email address or domain +3. An SMTP2GO API key with "Send Email" permissions + +## Configuration + +See the main [README.md](../README.md) for detailed setup instructions. + +### Required Configuration + +- **apiKey**: Your SMTP2GO API key +- **sender**: Your verified sender email address +- **templates**: Array of email templates for different use cases + +### Optional Configuration + +- **senderName**: Friendly name to display as the email sender + +## API Reference + +This connector uses the SMTP2GO `/v3/email/send` API endpoint. For more information, see the [SMTP2GO API documentation](https://apidoc.smtp2go.com/documentation/). + +## Development + +### Install Dependencies + +```bash +pnpm install +``` + +### Run Tests + +```bash +pnpm test +``` + +### Build + +```bash +pnpm build +``` + +### Development Mode + +```bash +pnpm dev +``` + +## License + +MPL-2.0 diff --git a/packages/connectors/connector-smtp2go-email/logo.svg b/packages/connectors/connector-smtp2go-email/logo.svg new file mode 100644 index 000000000000..dce60e730cce --- /dev/null +++ b/packages/connectors/connector-smtp2go-email/logo.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/packages/connectors/connector-smtp2go-email/package.json b/packages/connectors/connector-smtp2go-email/package.json new file mode 100644 index 000000000000..f4bcc63a6764 --- /dev/null +++ b/packages/connectors/connector-smtp2go-email/package.json @@ -0,0 +1,68 @@ +{ + "name": "@logto/connector-smtp2go-email", + "version": "1.0.0", + "description": "SMTP2GO Email Service connector implementation.", + "author": "Fabio Lorenzo ", + "dependencies": { + "@logto/connector-kit": "workspace:^", + "@silverhand/essentials": "^2.9.1", + "got": "^14.0.0", + "zod": "3.24.3" + }, + "main": "./lib/index.js", + "module": "./lib/index.js", + "exports": "./lib/index.js", + "license": "MPL-2.0", + "type": "module", + "files": [ + "lib", + "docs", + "logo.svg", + "logo-dark.svg" + ], + "scripts": { + "precommit": "lint-staged", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", + "lint": "eslint --ext .ts src", + "lint:report": "pnpm lint --format json --output-file report.json", + "test": "vitest src", + "test:ci": "pnpm run test --silent --coverage", + "prepublishOnly": "pnpm build" + }, + "engines": { + "node": "^22.14.0" + }, + "eslintConfig": { + "extends": "@silverhand", + "settings": { + "import/core-modules": [ + "@silverhand/essentials", + "got", + "nock", + "snakecase-keys", + "zod" + ] + } + }, + "prettier": "@silverhand/eslint-config/.prettierrc", + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "@silverhand/eslint-config": "6.0.1", + "@silverhand/ts-config": "6.0.0", + "@types/node": "^22.14.0", + "@types/supertest": "^6.0.2", + "@vitest/coverage-v8": "^3.1.1", + "eslint": "^8.56.0", + "lint-staged": "^15.0.2", + "nock": "^14.0.3", + "prettier": "^3.5.3", + "supertest": "^7.0.0", + "tsup": "^8.5.0", + "typescript": "^5.5.3", + "vitest": "^3.1.1" + } +} diff --git a/packages/connectors/connector-smtp2go-email/src/constant.ts b/packages/connectors/connector-smtp2go-email/src/constant.ts new file mode 100644 index 000000000000..1a7ae15bb7e6 --- /dev/null +++ b/packages/connectors/connector-smtp2go-email/src/constant.ts @@ -0,0 +1,117 @@ +import type { ConnectorMetadata } from '@logto/connector-kit'; +import { ConnectorConfigFormItemType } from '@logto/connector-kit'; + +export const endpoint = 'https://api.smtp2go.com/v3/email/send'; + +export const defaultMetadata: ConnectorMetadata = { + id: 'smtp2go-email-service', + target: 'smtp2go-email', + platform: null, + name: { + en: 'SMTP2GO Email', + 'zh-CN': 'SMTP2GO 邮件', + de: 'SMTP2GO E-Mail', + }, + logo: './logo.svg', + logoDark: null, + description: { + en: 'SMTP2GO is a reliable email delivery service for transactional and marketing emails.', + 'zh-CN': 'SMTP2GO 是一个可靠的事务性和营销电子邮件服务。', + de: 'SMTP2GO ist ein zuverlässiger E-Mail-Zustelldienst für transaktionale und Marketing-E-Mails.', + }, + readme: './README.md', + formItems: [ + { + key: 'apiKey', + label: 'API Key', + type: ConnectorConfigFormItemType.Text, + required: true, + placeholder: '', + }, + { + key: 'sender', + label: 'Sender Email', + type: ConnectorConfigFormItemType.Text, + required: true, + placeholder: 'noreply@example.com', + }, + { + key: 'senderName', + label: 'Sender Name', + type: ConnectorConfigFormItemType.Text, + required: false, + placeholder: 'Logto', + }, + { + key: 'templates', + label: 'Templates', + type: ConnectorConfigFormItemType.Json, + required: true, + defaultValue: [ + { + usageType: 'SignIn', + type: 'text/plain', + subject: 'Logto Sign-In Verification Code', + content: + 'Your Logto sign-in verification code is {{code}}. The code will remain active for 10 minutes.', + }, + { + usageType: 'Register', + type: 'text/plain', + subject: 'Logto Registration Verification Code', + content: + 'Your Logto sign-up verification code is {{code}}. The code will remain active for 10 minutes.', + }, + { + usageType: 'ForgotPassword', + type: 'text/plain', + subject: 'Logto Password Reset Verification Code', + content: + 'Your Logto password reset verification code is {{code}}. The code will remain active for 10 minutes.', + }, + { + usageType: 'OrganizationInvitation', + type: 'text/plain', + subject: 'Logto Organization Invitation', + content: + 'You have been invited to join an organization. Your invitation link is {{link}}.', + }, + { + usageType: 'Generic', + type: 'text/plain', + subject: 'Logto Verification Code', + content: + 'Your Logto verification code is {{code}}. The code will remain active for 10 minutes.', + }, + { + usageType: 'UserPermissionValidation', + type: 'text/plain', + subject: 'Logto Permission Validation Code', + content: + 'Your Logto permission validation code is {{code}}. The code will remain active for 10 minutes.', + }, + { + usageType: 'BindNewIdentifier', + type: 'text/plain', + subject: 'Logto New Identifier Binding Code', + content: + 'Your Logto new identifier binding code is {{code}}. The code will remain active for 10 minutes.', + }, + { + usageType: 'MfaVerification', + type: 'text/plain', + subject: 'Logto MFA Verification Code', + content: + 'Your Logto MFA verification code is {{code}}. The code will remain active for 10 minutes.', + }, + { + usageType: 'BindMfa', + type: 'text/plain', + subject: 'Logto 2-Step Verification Setup Code', + content: + 'Your Logto 2-step verification setup code is {{code}}. The code will remain active for 10 minutes.', + }, + ], + }, + ], +}; diff --git a/packages/connectors/connector-smtp2go-email/src/index.test.ts b/packages/connectors/connector-smtp2go-email/src/index.test.ts new file mode 100644 index 000000000000..f378f47a321d --- /dev/null +++ b/packages/connectors/connector-smtp2go-email/src/index.test.ts @@ -0,0 +1,128 @@ +import nock from 'nock'; + +import { TemplateType } from '@logto/connector-kit'; + +import createConnector from './index.js'; +import type { Smtp2goEmailConfig } from './types.js'; +import { ContentType } from './types.js'; + +const getConfig = vi.fn(); +// eslint-disable-next-line unicorn/no-useless-undefined +const getI18nEmailTemplate = vi.fn().mockResolvedValue(undefined); + +const connector = await createConnector({ getConfig, getI18nEmailTemplate }); + +const sender = 'noreply@example.com'; +const toEmail = 'test@example.com'; + +const mockedConfig: Smtp2goEmailConfig = { + apiKey: 'test-api-key-123', + sender, + senderName: 'Logto Test', + templates: [ + { + usageType: 'SignIn', + type: ContentType.Text, + subject: 'Sign-In Code', + content: 'Your code is {{code}}', + }, + { + usageType: 'Register', + type: ContentType.Text, + subject: 'Registration Code', + content: 'Your code is {{code}}', + }, + { + usageType: 'ForgotPassword', + type: ContentType.Text, + subject: 'Password Reset Code', + content: 'Your code is {{code}}', + }, + { + usageType: 'Generic', + type: ContentType.Text, + subject: 'Verification Code', + content: 'Your code is {{code}}', + }, + ], +}; + +const nockMessages = ( + expectation: Record, + endpoint = 'https://api.smtp2go.com' +) => + nock(endpoint) + .post('/v3/email/send') + .reply((_, body, callback) => { + expect(body).toMatchObject(expectation); + callback(null, [200, { data: { succeeded: 1, failed: 0 } }]); + }); + +describe('SMTP2GO Email connector', () => { + beforeEach(() => { + nock.cleanAll(); + }); + + it('should send generic email with default config', async () => { + nockMessages({ + api_key: mockedConfig.apiKey, + to: [toEmail], + sender: `${mockedConfig.senderName} <${sender}>`, + subject: 'Verification Code', + text_body: 'Your code is 123456', + }); + + getConfig.mockResolvedValue(mockedConfig); + + await connector.sendMessage({ + to: toEmail, + type: TemplateType.Generic, + payload: { code: '123456' }, + }); + }); + + it('should send email with custom i18n template', async () => { + getI18nEmailTemplate.mockResolvedValue({ + subject: 'Custom Passcode {{code}}', + content: '

Your passcode is {{code}}

', + contentType: 'text/html', + sendFrom: '{{application.name}}', + }); + + nockMessages({ + api_key: mockedConfig.apiKey, + to: [toEmail], + sender: 'Test App ', + subject: 'Custom Passcode 123456', + html_body: '

Your passcode is 123456

', + }); + + getConfig.mockResolvedValue(mockedConfig); + + await connector.sendMessage({ + to: toEmail, + type: TemplateType.Generic, + payload: { + code: '123456', + application: { name: 'Test App' }, + }, + }); + }); + + it('should throw error if required template (generic) is not found', async () => { + getConfig.mockResolvedValue({ + ...mockedConfig, + templates: mockedConfig.templates.filter( + (template) => template.usageType !== TemplateType.Generic + ), + }); + + await expect( + connector.sendMessage({ + to: toEmail, + type: TemplateType.OrganizationInvitation, + payload: { link: 'https://example.com' }, + }) + ).rejects.toThrow(); + }); +}); diff --git a/packages/connectors/connector-smtp2go-email/src/index.ts b/packages/connectors/connector-smtp2go-email/src/index.ts new file mode 100644 index 000000000000..619266b538e0 --- /dev/null +++ b/packages/connectors/connector-smtp2go-email/src/index.ts @@ -0,0 +1,141 @@ +import { assert, conditional, trySafe } from '@silverhand/essentials'; +import { got, HTTPError } from 'got'; + +import type { + GetConnectorConfig, + SendMessageFunction, + CreateConnector, + EmailConnector, + GetI18nEmailTemplate, + EmailTemplateDetails, + SendMessagePayload, +} from '@logto/connector-kit'; +import { + ConnectorError, + ConnectorErrorCodes, + validateConfig, + ConnectorType, + replaceSendMessageHandlebars, + getConfigTemplateByType, +} from '@logto/connector-kit'; + +import { defaultMetadata, endpoint } from './constant.js'; +import { ContentType, smtp2goEmailConfigGuard } from './types.js'; +import type { Smtp2goEmailConfig, Smtp2goEmailRequest, Smtp2goEmailResponse } from './types.js'; + +const buildRequestFromDefaultTemplate = ( + to: string, + config: Smtp2goEmailConfig, + template: Smtp2goEmailConfig['templates'][0], + payload: SendMessagePayload +): Smtp2goEmailRequest => { + const subject = replaceSendMessageHandlebars(template.subject, payload); + const content = replaceSendMessageHandlebars(template.content, payload); + + return { + api_key: config.apiKey, + to: [to], + sender: config.senderName ? `${config.senderName} <${config.sender}>` : config.sender, + subject, + ...(template.type === ContentType.Html ? { html_body: content } : { text_body: content }), + }; +}; + +const buildRequestFromCustomTemplate = ( + to: string, + config: Smtp2goEmailConfig, + { subject, content, sendFrom, contentType = 'text/html' }: EmailTemplateDetails, + payload: SendMessagePayload +): Smtp2goEmailRequest => { + const processedSubject = replaceSendMessageHandlebars(subject, payload); + const processedContent = replaceSendMessageHandlebars(content, payload); + const senderName = sendFrom ? replaceSendMessageHandlebars(sendFrom, payload) : config.senderName; + + return { + api_key: config.apiKey, + to: [to], + sender: senderName ? `${senderName} <${config.sender}>` : config.sender, + subject: processedSubject, + ...(contentType === 'text/html' + ? { html_body: processedContent } + : { text_body: processedContent }), + }; +}; + +const sendMessage = + ( + getConfig: GetConnectorConfig, + getI18nEmailTemplate?: GetI18nEmailTemplate + ): SendMessageFunction => + async (data, inputConfig) => { + const { to, type, payload } = data; + const config = inputConfig ?? (await getConfig(defaultMetadata.id)); + validateConfig(config, smtp2goEmailConfigGuard); + + const customTemplate = await trySafe(async () => getI18nEmailTemplate?.(type, payload.locale)); + + const template = getConfigTemplateByType(type, config); + + const requestPayload = customTemplate + ? buildRequestFromCustomTemplate(to, config, customTemplate, payload) + : template && buildRequestFromDefaultTemplate(to, config, template, payload); + + assert(requestPayload, new ConnectorError(ConnectorErrorCodes.TemplateNotFound)); + + try { + const response = await got.post(endpoint, { + json: requestPayload, + responseType: 'json', + }); + + // Check for errors in response + if (response.body.data?.error) { + throw new ConnectorError( + ConnectorErrorCodes.General, + `SMTP2GO API Error: ${response.body.data.error} (${response.body.data.error_code || 'unknown'})` + ); + } + + return response; + } catch (error: unknown) { + if (error instanceof HTTPError) { + const { + response: { body: rawBody }, + } = error; + + assert( + typeof rawBody === 'string' || typeof rawBody === 'object', + new ConnectorError( + ConnectorErrorCodes.InvalidResponse, + `Invalid response raw body type: ${typeof rawBody}` + ) + ); + + const errorMessage = + typeof rawBody === 'string' ? rawBody : JSON.stringify(rawBody); + + throw new ConnectorError(ConnectorErrorCodes.General, errorMessage); + } + + // Re-throw if already a ConnectorError + if (error instanceof ConnectorError) { + throw error; + } + + throw new ConnectorError(ConnectorErrorCodes.General, error); + } + }; + +const createSmtp2goEmailConnector: CreateConnector = async ({ + getConfig, + getI18nEmailTemplate, +}) => { + return { + metadata: defaultMetadata, + type: ConnectorType.Email, + configGuard: smtp2goEmailConfigGuard, + sendMessage: sendMessage(getConfig, getI18nEmailTemplate), + }; +}; + +export default createSmtp2goEmailConnector; diff --git a/packages/connectors/connector-smtp2go-email/src/types.ts b/packages/connectors/connector-smtp2go-email/src/types.ts new file mode 100644 index 000000000000..8009e79c3ce8 --- /dev/null +++ b/packages/connectors/connector-smtp2go-email/src/types.ts @@ -0,0 +1,74 @@ +import { z } from 'zod'; + +/** + * @doc https://apidoc.smtp2go.com/documentation/#/POST/email/send + */ + +export enum ContentType { + Text = 'text/plain', + Html = 'text/html', +} + +/** + * SMTP2GO API request payload + */ +export type Smtp2goEmailRequest = { + api_key: string; + to: string[]; + sender: string; + subject: string; + text_body?: string; + html_body?: string; + custom_headers?: Array<{ + header: string; + value: string; + }>; +}; + +/** + * SMTP2GO API response + */ +export type Smtp2goEmailResponse = { + data?: { + error?: string; + error_code?: string; + succeeded?: number; + failed?: number; + failures?: string[]; + }; + request_id?: string; +}; + +/** + * Template configuration for different usage types + */ +const templateGuard = z.object({ + usageType: z.string(), + type: z.nativeEnum(ContentType), + subject: z.string(), + content: z.string(), // With variable {{code}}, supports HTML +}); + +/** + * SMTP2GO connector configuration + */ +export const smtp2goEmailConfigGuard = z.object({ + apiKey: z.string(), + sender: z.string().email(), + senderName: z.string().optional(), + templates: z.array(templateGuard).refine( + (templates) => + ['Register', 'SignIn', 'ForgotPassword', 'Generic'].every((requiredType) => + templates.map((template) => template.usageType).includes(requiredType) + ), + (templates) => ({ + message: `Template with UsageType (${['Register', 'SignIn', 'ForgotPassword', 'Generic'] + .filter( + (requiredType) => !templates.map((template) => template.usageType).includes(requiredType) + ) + .join(', ')}) should be provided!`, + }) + ), +}); + +export type Smtp2goEmailConfig = z.infer;