feat: add ses tenant support for teams (#193)

This commit is contained in:
KM Koushik
2025-08-09 20:37:29 +10:00
committed by GitHub
parent e3b8a451da
commit da13107f88
11 changed files with 947 additions and 93 deletions
+77 -4
View File
@@ -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 {