studiocms
Version:
Astro Native CMS for AstroDB. Built from the ground up by the Astro community.
216 lines (189 loc) • 6.51 kB
text/typescript
import { logger as _logger, isVerbose } from 'studiocms:logger';
import { SDKCoreJs as sdk } from 'studiocms:sdk';
import { Effect, genLogger, Layer, pipeLogger } from '../../effect.js';
import { type Mail, SMTPMailer } from '../../utils/effects/smtp.js';
import type { ConfigFinal, StudioCMSMailerConfig } from '../sdk/modules/config.js';
/**
* Interface representing the options for sending an email.
*/
export interface MailOptions {
/**
* The recipient(s) of the email. Can be a single email address or an array of email addresses.
*/
to: string | string[];
/**
* The subject of the email.
*/
subject: string;
/**
* The plain text content of the email. Optional.
*/
text?: string | undefined;
/**
* The HTML content of the email. Optional.
*/
html?: string | undefined;
}
/**
* Interface representing an error response from the mailer.
*/
export interface MailerErrorResponse {
error: string;
}
/**
* Interface representing a success response from the mailer.
*/
export interface MailerSuccessResponse {
message: string;
}
/**
* Interface representing the response from a mail verification operation.
*/
export type MailerResponse = MailerSuccessResponse | MailerErrorResponse;
const forked = _logger.fork('studiocms:runtime/mailer');
export const makeLogger = Effect.succeed(forked);
export class Logger extends Effect.Tag('studiocms/lib/mailer/Logger')<
Logger,
Effect.Effect.Success<typeof makeLogger>
>() {
static Live = makeLogger;
static Layer = Layer.scoped(this, this.Live);
}
export class Mailer extends Effect.Service<Mailer>()('studiocms/lib/mailer/Mailer', {
effect: genLogger('studiocms/lib/mailer/Mailer.effect')(function* () {
const logger = yield* Logger;
const SMTP = yield* SMTPMailer;
/**
* Logs the response from the mailer.
*
* @param data - The response from the mailer.
* @returns The response from the mailer.
*/
const mailerResponse = (data: MailerResponse) => {
if ('error' in data) {
logger.error(data.error);
return data;
}
isVerbose && logger.info(data.message);
return data;
};
/**
* Gets the mailer configuration from the database.
*
* @returns A promise that resolves with the mailer configuration object.
*/
const getMailerConfigTable = pipeLogger('studiocms/lib/mailer/Mailer.getMailerConfigTable')(
sdk.CONFIG.mailerConfig.get()
);
/**
* Updates the mailer configuration in the database.
*
* @param config - The new mailer configuration object.
* @returns A promise that resolves when the mailer configuration has been updated.
*/
const updateMailerConfigTable = (config: ConfigFinal<StudioCMSMailerConfig>) =>
genLogger('studiocms/lib/mailer/Mailer.updateMailerConfigTable')(function* () {
yield* sdk.CONFIG.mailerConfig
.update(config)
.pipe(
Effect.catchAll((e) =>
Effect.succeed(
mailerResponse({ error: `Error updating mailer configuration: ${String(e)}` })
)
)
);
return mailerResponse({ message: 'Mailer configuration updated successfully' });
});
const createMailerConfigTable = (config: ConfigFinal<StudioCMSMailerConfig>) =>
pipeLogger('studiocms/lib/mailer/Mailer.createMailerConfigTable')(
sdk.CONFIG.mailerConfig.init(config)
);
/**
* Sends an email using the provided mailer configuration and mail options.
*
* @param mailOptions - The options for the mail, including the subject and other message details.
* @returns A promise that resolves with the result of the mail sending operation.
* @throws Will throw an error if the mail sending operation fails.
*
* @example
* ```typescript
* const mailOptions = {
* subject: 'Test Email',
* to: 'recipient@example.com',
* text: 'This is a test email.'
* };
*
* sendMail(mailerConfig, mailOptions)
* .then(result => console.log('Email sent:', result))
* .catch(error => console.error('Error sending email:', error));
* ```
*/
const sendMail = ({ subject, ...message }: MailOptions) =>
genLogger('studiocms/lib/mailer/Mailer.sendMail')(function* () {
// Create the mail options object
const toSend: Mail.Options = { subject };
// Set the to field in the mail options
toSend.to = Array.isArray(message.to) ? message.to.join(', ') : message.to;
// If the message has a text field and no html field, set the text field in the mail options
if (message.text && !message.html) {
toSend.text = message.text;
}
// If the message has an html field, set the html field in the mail options
if (message.html) {
toSend.html = message.html;
}
const result = yield* SMTP.sendMail(toSend);
return mailerResponse({ message: `Message sent: ${result.messageId}` });
});
/**
* Verifies the mail connection using the provided transporter configuration.
*
* @returns A promise that resolves to an object containing either a success message or an error message.
*
* @example
* ```typescript
* const result = await verifyMailConnection();
* if ('message' in result) {
* console.log(result.message);
* } else {
* console.error(result.error);
* }
* ```
*/
const verifyMailConnection = genLogger('studiocms/lib/mailer/Mailer.verifyMailConnection')(
function* () {
const result = yield* SMTP.verifyTransport();
// If the result is not true, log an error and return an error message
if (result !== true) {
return mailerResponse({ error: 'Mail connection verification failed' });
}
// Log a success message and return a success message
return mailerResponse({ message: 'Mail connection verified successfully' });
}
);
/**
* Checks if the mailer service is enabled in the StudioCMS configuration.
*
* This function retrieves the configuration from the StudioCMS SDK and
* returns the value of the `enableMailer` property. If the configuration
* is not available, it defaults to `false`.
*/
const isEnabled = genLogger('studiocms/lib/mailer/Mailer.isEnabled')(function* () {
const config = yield* sdk.GET.siteConfig();
const status = config?.data?.enableMailer || false;
return status;
});
return {
getMailerConfigTable,
updateMailerConfigTable,
createMailerConfigTable,
sendMail,
verifyMailConnection,
isEnabled,
};
}),
dependencies: [Logger.Layer, SMTPMailer.Default],
accessors: true,
}) {
static Provide = Effect.provide(this.Default);
}