From e6baace00d5f7a109e717b8a996bc2cda52cc9fa Mon Sep 17 00:00:00 2001 From: Dries Augustyns Date: Tue, 13 Jan 2026 10:19:54 +0100 Subject: [PATCH] feat: Remove domain from AWS if no longer in use by other projects --- apps/api/src/services/DomainService.ts | 24 +++++++++++++++++++++++- apps/api/src/services/SESService.ts | 9 ++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/apps/api/src/services/DomainService.ts b/apps/api/src/services/DomainService.ts index 4e07c9f..f6a7bd5 100644 --- a/apps/api/src/services/DomainService.ts +++ b/apps/api/src/services/DomainService.ts @@ -8,7 +8,7 @@ import {HttpException} from '../exceptions/index.js'; import {Keys} from './keys.js'; import {MembershipService} from './MembershipService.js'; import {NtfyService} from './NtfyService.js'; -import {getDomainVerificationAttributes, verifyDomain} from './SESService.js'; +import {deleteIdentity, getDomainVerificationAttributes, verifyDomain} from './SESService.js'; export class DomainService { /** @@ -249,6 +249,28 @@ export class DomainService { await prisma.domain.delete({where: {id: domainId}}); + // Check if this domain is still attached to another project + const domainExistsElsewhere = await prisma.domain.findFirst({ + where: { + domain: domainName, + }, + }); + + // If domain is not used by any other project, remove it from AWS SES + if (!domainExistsElsewhere) { + try { + await deleteIdentity(domainName); + signale.info(`[DOMAIN] Removed AWS SES identity for ${domainName} (no longer used by any project)`); + } catch (error) { + // Log error but don't fail the domain removal if AWS cleanup fails + signale.error(`[DOMAIN] Failed to remove AWS SES identity for ${domainName}:`, error); + } + } else { + signale.info( + `[DOMAIN] Keeping AWS SES identity for ${domainName} (still used by project ${domainExistsElsewhere.projectId})`, + ); + } + // Send notification about domain removal await NtfyService.notifyDomainRemoved(domainName, domain.project.name, domain.project.id); diff --git a/apps/api/src/services/SESService.ts b/apps/api/src/services/SESService.ts index 6ba246b..ef05b9a 100644 --- a/apps/api/src/services/SESService.ts +++ b/apps/api/src/services/SESService.ts @@ -8,7 +8,7 @@ import { DASHBOARD_URI, SES_CONFIGURATION_SET, SES_CONFIGURATION_SET_NO_TRACKING, - TRACKING_TOGGLE_ENABLED + TRACKING_TOGGLE_ENABLED, } from '../app/constants.js'; /** @@ -258,6 +258,13 @@ export const disableFeedbackForwarding = async (domain: string): Promise = }); }; +/** + * Delete a verified domain identity from AWS SES + */ +export const deleteIdentity = async (domain: string): Promise => { + await ses.deleteIdentity({Identity: domain}); +}; + /** * Get AWS SES account sending quota and rate limit * @returns MaxSendRate (emails per second) or null if the call fails