feat: add ses tenant support for teams (#193)
This commit is contained in:
@@ -9,16 +9,38 @@ import {
|
||||
CreateConfigurationSetCommand,
|
||||
EventType,
|
||||
GetAccountCommand,
|
||||
CreateTenantResourceAssociationCommand,
|
||||
DeleteTenantResourceAssociationCommand,
|
||||
} from "@aws-sdk/client-sesv2";
|
||||
import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts";
|
||||
import { generateKeyPairSync } from "crypto";
|
||||
import mime from "mime-types";
|
||||
import nodemailer from "nodemailer";
|
||||
import { Readable } from "stream";
|
||||
import { env } from "~/env";
|
||||
import { EmailContent } from "~/types";
|
||||
import { nanoid } from "../nanoid";
|
||||
import { logger } from "../logger/log";
|
||||
|
||||
let accountId: string | undefined = undefined;
|
||||
|
||||
async function getAccountId(region: string) {
|
||||
if (accountId) {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
const stsClient = new STSClient({
|
||||
region: region,
|
||||
});
|
||||
const command = new GetCallerIdentityCommand({});
|
||||
const response = await stsClient.send(command);
|
||||
accountId = response.Account;
|
||||
return accountId;
|
||||
}
|
||||
|
||||
async function getIdentityArn(domain: string, region: string) {
|
||||
const accountId = await getAccountId(region);
|
||||
return `arn:aws:ses:${region}:${accountId}:identity/${domain}`;
|
||||
}
|
||||
|
||||
function getSesClient(region: string) {
|
||||
return new SESv2Client({
|
||||
region: region,
|
||||
@@ -56,7 +78,11 @@ function generateKeyPair() {
|
||||
return { privateKey: base64PrivateKey, publicKey: base64PublicKey };
|
||||
}
|
||||
|
||||
export async function addDomain(domain: string, region: string) {
|
||||
export async function addDomain(
|
||||
domain: string,
|
||||
region: string,
|
||||
sesTenantId?: string
|
||||
) {
|
||||
const sesClient = getSesClient(region);
|
||||
|
||||
const { privateKey, publicKey } = generateKeyPair();
|
||||
@@ -76,6 +102,26 @@ export async function addDomain(domain: string, region: string) {
|
||||
|
||||
const emailIdentityResponse = await sesClient.send(emailIdentityCommand);
|
||||
|
||||
if (sesTenantId) {
|
||||
const tenantResourceAssociationCommand =
|
||||
new CreateTenantResourceAssociationCommand({
|
||||
TenantName: sesTenantId,
|
||||
ResourceArn: await getIdentityArn(domain, region),
|
||||
});
|
||||
|
||||
const tenantResourceAssociationResponse = await sesClient.send(
|
||||
tenantResourceAssociationCommand
|
||||
);
|
||||
|
||||
if (tenantResourceAssociationResponse.$metadata.httpStatusCode !== 200) {
|
||||
logger.error(
|
||||
{ tenantResourceAssociationResponse },
|
||||
"Failed to associate domain with tenant"
|
||||
);
|
||||
throw new Error("Failed to associate domain with tenant");
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
response.$metadata.httpStatusCode !== 200 ||
|
||||
emailIdentityResponse.$metadata.httpStatusCode !== 200
|
||||
@@ -90,8 +136,33 @@ export async function addDomain(domain: string, region: string) {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
export async function deleteDomain(domain: string, region: string) {
|
||||
export async function deleteDomain(
|
||||
domain: string,
|
||||
region: string,
|
||||
sesTenantId?: string
|
||||
) {
|
||||
const sesClient = getSesClient(region);
|
||||
|
||||
if (sesTenantId) {
|
||||
const tenantResourceAssociationCommand =
|
||||
new DeleteTenantResourceAssociationCommand({
|
||||
TenantName: sesTenantId,
|
||||
ResourceArn: await getIdentityArn(domain, region),
|
||||
});
|
||||
|
||||
const tenantResourceAssociationResponse = await sesClient.send(
|
||||
tenantResourceAssociationCommand
|
||||
);
|
||||
|
||||
if (tenantResourceAssociationResponse.$metadata.httpStatusCode !== 200) {
|
||||
logger.error(
|
||||
{ tenantResourceAssociationResponse },
|
||||
"Failed to delete tenant resource association"
|
||||
);
|
||||
throw new Error("Failed to delete tenant resource association");
|
||||
}
|
||||
}
|
||||
|
||||
const command = new DeleteEmailIdentityCommand({
|
||||
EmailIdentity: domain,
|
||||
});
|
||||
@@ -124,6 +195,7 @@ export async function sendRawEmail({
|
||||
isBulk,
|
||||
inReplyToMessageId,
|
||||
emailId,
|
||||
sesTenantId,
|
||||
}: Partial<EmailContent> & {
|
||||
region: string;
|
||||
configurationSetName: string;
|
||||
@@ -187,6 +259,7 @@ export async function sendRawEmail({
|
||||
},
|
||||
},
|
||||
ConfigurationSetName: configurationSetName,
|
||||
TenantName: sesTenantId ? sesTenantId : undefined,
|
||||
});
|
||||
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user