diff --git a/apps/web/src/app/(dashboard)/domains/[domainId]/delete-domain.tsx b/apps/web/src/app/(dashboard)/domains/[domainId]/delete-domain.tsx
index b5e8fab9..ae9dacc7 100644
--- a/apps/web/src/app/(dashboard)/domains/[domainId]/delete-domain.tsx
+++ b/apps/web/src/app/(dashboard)/domains/[domainId]/delete-domain.tsx
@@ -33,6 +33,9 @@ export const DeleteDomain: React.FC<{ domain: Domain }> = ({ domain }) => {
toast.success(`Domain ${domain.name} deleted`);
router.replace("/domains");
},
+ onError: (error) => {
+ toast.error(`Failed to delete domain: ${error.message}`);
+ },
},
);
}
diff --git a/apps/web/src/app/(dashboard)/domains/domain-list.tsx b/apps/web/src/app/(dashboard)/domains/domain-list.tsx
index a2ef5f4e..58254cc7 100644
--- a/apps/web/src/app/(dashboard)/domains/domain-list.tsx
+++ b/apps/web/src/app/(dashboard)/domains/domain-list.tsx
@@ -9,6 +9,7 @@ import React from "react";
import { StatusIndicator } from "./status-indicator";
import { DomainStatusBadge } from "./domain-badge";
import Spinner from "@usesend/ui/src/spinner";
+import { DeleteDomain } from "./[domainId]/delete-domain";
export default function DomainsList() {
const domainsQuery = api.domain.domains.useQuery();
@@ -116,6 +117,9 @@ const DomainItem: React.FC<{ domain: Domain }> = ({ domain }) => {
/>
+
+
+
diff --git a/apps/web/src/server/aws/ses.ts b/apps/web/src/server/aws/ses.ts
index 0fda94c8..62d4d0d6 100644
--- a/apps/web/src/server/aws/ses.ts
+++ b/apps/web/src/server/aws/ses.ts
@@ -150,30 +150,67 @@ export async function deleteDomain(
const sesClient = getSesClient(region);
if (sesTenantId) {
- const tenantResourceAssociationCommand =
- new DeleteTenantResourceAssociationCommand({
- TenantName: sesTenantId,
- ResourceArn: await getIdentityArn(domain, region),
- });
+ try {
+ const tenantResourceAssociationCommand =
+ new DeleteTenantResourceAssociationCommand({
+ TenantName: sesTenantId,
+ ResourceArn: await getIdentityArn(domain, region),
+ });
+
+ const tenantResourceAssociationResponse = await sesClient.send(
+ tenantResourceAssociationCommand
+ );
- const tenantResourceAssociationResponse = await sesClient.send(
- tenantResourceAssociationCommand
- );
+ if (tenantResourceAssociationResponse.$metadata.httpStatusCode !== 200) {
+ logger.error(
+ { domain, region, sesTenantId, tenantResourceAssociationResponse },
+ "[ses.deleteDomain] non-200 from DeleteTenantResourceAssociation"
+ );
+ throw new Error("Failed to delete tenant resource association");
+ }
+ } catch (error: any) {
+ if (error?.name === "NotFoundException") {
+ logger.warn(
+ { domain, region, sesTenantId, errorName: error.name },
+ "[ses.deleteDomain] tenant association already gone, continuing"
+ );
+ } else {
+ logger.error(
+ { err: error, domain, region, sesTenantId },
+ "[ses.deleteDomain] DeleteTenantResourceAssociation failed"
+ );
+ throw error;
+ }
+ }
+ }
- if (tenantResourceAssociationResponse.$metadata.httpStatusCode !== 200) {
+ try {
+ const command = new DeleteEmailIdentityCommand({
+ EmailIdentity: domain,
+ });
+ const response = await sesClient.send(command);
+ if (response.$metadata.httpStatusCode !== 200) {
logger.error(
- { tenantResourceAssociationResponse },
- "Failed to delete tenant resource association"
+ { domain, region, response },
+ "[ses.deleteDomain] non-200 from DeleteEmailIdentity"
);
- throw new Error("Failed to delete tenant resource association");
+ return false;
}
+ return true;
+ } catch (error: any) {
+ if (error?.name === "NotFoundException") {
+ logger.warn(
+ { domain, region, errorName: error.name },
+ "[ses.deleteDomain] identity already gone on SES, continuing"
+ );
+ return true;
+ }
+ logger.error(
+ { err: error, domain, region },
+ "[ses.deleteDomain] DeleteEmailIdentity failed"
+ );
+ throw error;
}
-
- const command = new DeleteEmailIdentityCommand({
- EmailIdentity: domain,
- });
- const response = await sesClient.send(command);
- return response.$metadata.httpStatusCode === 200;
}
export async function getDomainIdentity(domain: string, region: string) {