studiocms
Version:
Astro Native CMS for AstroDB. Built from the ground up by the Astro community.
162 lines (146 loc) • 4.94 kB
text/typescript
/* v8 ignore start */
// Runtime utilities for sending emails using SMTP with Effect
// SMTP implementation is already tested in @withstudiocms/effect
// We import and create a custom SMTPMailer service here for StudioCMS specific use cases
import { db, eq } from 'astro:db';
import {
type Mail,
SMTPService,
type SMTPTransport,
SMTPTransportConfig,
TransportConfig,
} from '@withstudiocms/effect/smtp';
import { CMSMailerConfigId } from '../../consts.js';
import { tsMailerConfig } from '../../db/config.js';
import { Effect, genLogger, Layer, pipeLogger } from '../../effect.js';
export type { Mail, SMTPTransport };
/**
* Converts a null value to undefined.
*
* @param value - The value to convert.
* @returns The value if it is not null, otherwise undefined.
*/
function nullToUndefined<T>(value: T | null): T | undefined {
return value === null ? undefined : value;
}
/**
* TypeSafe Table definition for use in StudioCMS Integrations
*/
export type tsMailer = typeof tsMailerConfig.$inferSelect;
/**
* TypeSafe Table definition for use in StudioCMS Integrations
*/
export type tsMailerInsert = Omit<typeof tsMailerConfig.$inferInsert, 'id'>;
/**
* Converts a mailer configuration object into a TransportConfig object.
*
* This function extracts the necessary fields from the mailer configuration
* and constructs a TransportConfig object that can be used by the SMTP service.
*
* @param config - The mailer configuration object to convert.
* @returns An Effect that resolves to a TransportConfig object.
*/
const convertTransporterConfig = (config: tsMailer) =>
pipeLogger('studiocms/lib/effects/smtp/convertTransporterConfig')(
Effect.try(() => {
// Extract the required fields from the configuration object
const {
host,
port,
secure,
proxy,
auth_user,
auth_pass,
tls_rejectUnauthorized,
tls_servername,
default_sender,
} = config;
// Return the transporter configuration object
return TransportConfig({
transport: {
host,
port,
secure,
proxy: nullToUndefined(proxy),
auth:
auth_user !== null || auth_pass !== null
? {
user: nullToUndefined(auth_user),
pass: nullToUndefined(auth_pass),
}
: undefined,
tls:
tls_rejectUnauthorized !== null || tls_servername !== null
? {
rejectUnauthorized: nullToUndefined(tls_rejectUnauthorized),
servername: nullToUndefined(tls_servername),
}
: undefined,
},
defaults: {
from: nullToUndefined(default_sender),
},
});
})
);
/**
* Builds the SMTP transporter configuration from the database.
* This function retrieves the mailer configuration from the database
* and converts it into a TransportConfig object.
* @returns An Effect that resolves to a TransportConfig object.
* If the configuration is not found, it returns an empty TransportConfig.
*/
const buildTransporterConfig = genLogger('studiocms/lib/effects/smtp/buildTransporterConfig')(
function* () {
const configTable = yield* Effect.tryPromise(() =>
db.select().from(tsMailerConfig).where(eq(tsMailerConfig.id, CMSMailerConfigId)).get()
);
// If the mailer configuration is not found, return an Empty config
if (!configTable) {
return TransportConfig({
transport: {},
defaults: {},
});
}
return yield* convertTransporterConfig(configTable);
}
);
/**
* Represents the return type for the SMTPMailer service.
*/
export type SMTPMailerReturn = {
getVersionString: () => Effect.Effect<string, Error, never>;
isIdle: () => Effect.Effect<boolean, Error, never>;
sendMail: (
mailOptions: Mail.Options
) => Effect.Effect<SMTPTransport.SentMessageInfo, Error, never>;
verifyTransport: () => Effect.Effect<true, Error, never>;
};
/**
* Builds a Layer that provides the SMTPTransportConfig for the given TransportConfig.
* @param config - The TransportConfig to use for the SMTPTransportConfig.
* @returns The Layer that provides the SMTPTransportConfig.
*/
const buildProvide = (config: TransportConfig) =>
Effect.provide(Layer.provide(SMTPService.Default, SMTPTransportConfig.makeLive(config)));
/**
* SMTPMailer service for sending emails.
*
* This service provides methods to send emails using the SMTP protocol.
* It uses the configuration provided by the SMTPTransportConfig context.
*
* @extends Effect.Service
* @template {SMTPMailer} - The type of the service.
*/
export class SMTPMailer extends Effect.Service<SMTPMailer>()(
'studiocms/lib/effects/smtp/SMTPMailer',
{
effect: genLogger('studiocms/lib/effects/smtp/SMTPMailer.effect')(function* () {
const config = yield* buildTransporterConfig;
const { getVersionString, isIdle, sendMail, verifyTransport }: SMTPMailerReturn =
yield* SMTPService.pipe(buildProvide(config));
return { verifyTransport, sendMail, isIdle, getVersionString };
}),
}
) {}
/* v8 ignore stop */