chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import type { SuppressionListReason } from '@aws-sdk/client-sesv2'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSesCreateConfigurationSetContract } from '@/lib/api/contracts/tools/aws/ses-create-configuration-set'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createConfigurationSet, createSESClient } from '../utils'
|
||||
|
||||
const logger = createLogger('SESCreateConfigurationSetAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSesCreateConfigurationSetContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info('Creating SES configuration set')
|
||||
|
||||
const client = createSESClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const suppressedReasons = params.suppressedReasons
|
||||
? (params.suppressedReasons
|
||||
.split(',')
|
||||
.map((r) => r.trim())
|
||||
.filter(Boolean) as SuppressionListReason[])
|
||||
: null
|
||||
|
||||
const result = await createConfigurationSet(client, {
|
||||
configurationSetName: params.configurationSetName,
|
||||
customRedirectDomain: params.customRedirectDomain,
|
||||
httpsPolicy: params.httpsPolicy,
|
||||
tlsPolicy: params.tlsPolicy,
|
||||
sendingPoolName: params.sendingPoolName,
|
||||
reputationMetricsEnabled: params.reputationMetricsEnabled,
|
||||
sendingEnabled: params.sendingEnabled,
|
||||
suppressedReasons,
|
||||
tags: params.tags,
|
||||
})
|
||||
|
||||
logger.info(`Created configuration set '${params.configurationSetName}'`)
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to create configuration set:', error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to create configuration set: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,56 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSesCreateEmailIdentityContract } from '@/lib/api/contracts/tools/aws/ses-create-email-identity'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createEmailIdentity, createSESClient } from '../utils'
|
||||
|
||||
const logger = createLogger('SESCreateEmailIdentityAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSesCreateEmailIdentityContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info('Creating SES email identity')
|
||||
|
||||
const client = createSESClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await createEmailIdentity(client, {
|
||||
emailIdentity: params.emailIdentity,
|
||||
dkimSigningAttributes: params.dkimSigningAttributes,
|
||||
tags: params.tags,
|
||||
configurationSetName: params.configurationSetName,
|
||||
})
|
||||
|
||||
logger.info(`Created email identity '${params.emailIdentity}'`)
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to create email identity:', error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to create email identity: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,56 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSesCreateTemplateContract } from '@/lib/api/contracts/tools/aws/ses-create-template'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSESClient, createTemplate } from '../utils'
|
||||
|
||||
const logger = createLogger('SESCreateTemplateAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSesCreateTemplateContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Creating SES template '${params.templateName}'`)
|
||||
|
||||
const client = createSESClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await createTemplate(client, {
|
||||
templateName: params.templateName,
|
||||
subjectPart: params.subjectPart,
|
||||
textPart: params.textPart,
|
||||
htmlPart: params.htmlPart,
|
||||
})
|
||||
|
||||
logger.info(`Template '${params.templateName}' created successfully`)
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to create template:', error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to create template: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,51 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSesDeleteEmailIdentityContract } from '@/lib/api/contracts/tools/aws/ses-delete-email-identity'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSESClient, deleteEmailIdentity } from '../utils'
|
||||
|
||||
const logger = createLogger('SESDeleteEmailIdentityAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSesDeleteEmailIdentityContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info('Deleting SES email identity')
|
||||
|
||||
const client = createSESClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await deleteEmailIdentity(client, params.emailIdentity)
|
||||
|
||||
logger.info(`Deleted email identity '${params.emailIdentity}'`)
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to delete email identity:', error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to delete email identity: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,51 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSesDeleteSuppressedDestinationContract } from '@/lib/api/contracts/tools/aws/ses-delete-suppressed-destination'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSESClient, deleteSuppressedDestination } from '../utils'
|
||||
|
||||
const logger = createLogger('SESDeleteSuppressedDestinationAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSesDeleteSuppressedDestinationContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info('Removing email address from SES suppression list')
|
||||
|
||||
const client = createSESClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await deleteSuppressedDestination(client, params.emailAddress)
|
||||
|
||||
logger.info('Removed email address from suppression list')
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to remove suppressed destination:', error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to remove suppressed destination: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,51 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSesDeleteTemplateContract } from '@/lib/api/contracts/tools/aws/ses-delete-template'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSESClient, deleteTemplate } from '../utils'
|
||||
|
||||
const logger = createLogger('SESDeleteTemplateAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSesDeleteTemplateContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Deleting SES template '${params.templateName}'`)
|
||||
|
||||
const client = createSESClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await deleteTemplate(client, params.templateName)
|
||||
|
||||
logger.info(`Template '${params.templateName}' deleted successfully`)
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to delete template:', error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to delete template: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,51 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSesGetAccountContract } from '@/lib/api/contracts/tools/aws/ses-get-account'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSESClient, getAccount } from '../utils'
|
||||
|
||||
const logger = createLogger('SESGetAccountAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSesGetAccountContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info('Getting SES account information')
|
||||
|
||||
const client = createSESClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await getAccount(client)
|
||||
|
||||
logger.info('SES account info retrieved successfully')
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to get account information:', error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to get account information: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,51 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSesGetEmailIdentityContract } from '@/lib/api/contracts/tools/aws/ses-get-email-identity'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSESClient, getEmailIdentity } from '../utils'
|
||||
|
||||
const logger = createLogger('SESGetEmailIdentityAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSesGetEmailIdentityContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info('Fetching SES email identity')
|
||||
|
||||
const client = createSESClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await getEmailIdentity(client, params.emailIdentity)
|
||||
|
||||
logger.info(`Fetched email identity '${params.emailIdentity}'`)
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to get email identity:', error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to get email identity: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,51 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSesGetSuppressedDestinationContract } from '@/lib/api/contracts/tools/aws/ses-get-suppressed-destination'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSESClient, getSuppressedDestination } from '../utils'
|
||||
|
||||
const logger = createLogger('SESGetSuppressedDestinationAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSesGetSuppressedDestinationContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info('Fetching SES suppressed destination')
|
||||
|
||||
const client = createSESClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await getSuppressedDestination(client, params.emailAddress)
|
||||
|
||||
logger.info('Fetched suppressed destination')
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to get suppressed destination:', error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to get suppressed destination: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,51 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSesGetTemplateContract } from '@/lib/api/contracts/tools/aws/ses-get-template'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSESClient, getTemplate } from '../utils'
|
||||
|
||||
const logger = createLogger('SESGetTemplateAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSesGetTemplateContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Getting SES template '${params.templateName}'`)
|
||||
|
||||
const client = createSESClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await getTemplate(client, params.templateName)
|
||||
|
||||
logger.info(`Template '${params.templateName}' retrieved successfully`)
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to get template:', error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to get template: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,54 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSesListIdentitiesContract } from '@/lib/api/contracts/tools/aws/ses-list-identities'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSESClient, listIdentities } from '../utils'
|
||||
|
||||
const logger = createLogger('SESListIdentitiesAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSesListIdentitiesContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info('Listing SES email identities')
|
||||
|
||||
const client = createSESClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await listIdentities(client, {
|
||||
pageSize: params.pageSize,
|
||||
nextToken: params.nextToken,
|
||||
})
|
||||
|
||||
logger.info(`Listed ${result.count} identities`)
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to list identities:', error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to list identities: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,80 @@
|
||||
import type { SuppressionListReason } from '@aws-sdk/client-sesv2'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSesListSuppressedDestinationsContract } from '@/lib/api/contracts/tools/aws/ses-list-suppressed-destinations'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSESClient, listSuppressedDestinations } from '../utils'
|
||||
|
||||
const logger = createLogger('SESListSuppressedDestinationsAPI')
|
||||
|
||||
const VALID_SUPPRESSION_REASONS: SuppressionListReason[] = ['BOUNCE', 'COMPLAINT']
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSesListSuppressedDestinationsContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
let reasons: SuppressionListReason[] | null = null
|
||||
if (params.reasons) {
|
||||
const candidates = params.reasons
|
||||
.split(',')
|
||||
.map((r) => r.trim())
|
||||
.filter(Boolean)
|
||||
const invalid = candidates.filter(
|
||||
(r) => !VALID_SUPPRESSION_REASONS.includes(r as SuppressionListReason)
|
||||
)
|
||||
if (invalid.length > 0) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: `Invalid suppression reason(s): ${invalid.join(', ')}. Must be one of: ${VALID_SUPPRESSION_REASONS.join(', ')}`,
|
||||
},
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
reasons = candidates as SuppressionListReason[]
|
||||
}
|
||||
|
||||
logger.info('Listing SES suppressed destinations')
|
||||
|
||||
const client = createSESClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await listSuppressedDestinations(client, {
|
||||
reasons,
|
||||
startDate: params.startDate ? new Date(params.startDate) : null,
|
||||
endDate: params.endDate ? new Date(params.endDate) : null,
|
||||
pageSize: params.pageSize,
|
||||
nextToken: params.nextToken,
|
||||
})
|
||||
|
||||
logger.info(`Listed ${result.count} suppressed destinations`)
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to list suppressed destinations:', error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to list suppressed destinations: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,54 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSesListTemplatesContract } from '@/lib/api/contracts/tools/aws/ses-list-templates'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSESClient, listTemplates } from '../utils'
|
||||
|
||||
const logger = createLogger('SESListTemplatesAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSesListTemplatesContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info('Listing SES email templates')
|
||||
|
||||
const client = createSESClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await listTemplates(client, {
|
||||
pageSize: params.pageSize,
|
||||
nextToken: params.nextToken,
|
||||
})
|
||||
|
||||
logger.info(`Listed ${result.count} templates`)
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to list templates:', error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to list templates: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,54 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSesPutSuppressedDestinationContract } from '@/lib/api/contracts/tools/aws/ses-put-suppressed-destination'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSESClient, putSuppressedDestination } from '../utils'
|
||||
|
||||
const logger = createLogger('SESPutSuppressedDestinationAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSesPutSuppressedDestinationContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info('Adding email address to SES suppression list')
|
||||
|
||||
const client = createSESClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await putSuppressedDestination(client, {
|
||||
emailAddress: params.emailAddress,
|
||||
reason: params.reason,
|
||||
})
|
||||
|
||||
logger.info('Added email address to suppression list')
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to add suppressed destination:', error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to add suppressed destination: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,71 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSesSendBulkEmailContract } from '@/lib/api/contracts/tools/aws/ses-send-bulk-email'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSESClient, parseBulkEmailDestinations, sendBulkEmail } from '../utils'
|
||||
|
||||
const logger = createLogger('SESSendBulkEmailAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSesSendBulkEmailContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
let destinations: ReturnType<typeof parseBulkEmailDestinations>
|
||||
try {
|
||||
destinations = parseBulkEmailDestinations(params.destinations)
|
||||
} catch {
|
||||
return NextResponse.json(
|
||||
{ error: 'destinations must be a valid JSON array of destination objects' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Sending bulk email from ${params.fromAddress} to ${destinations.length} destination(s) using template '${params.templateName}'`
|
||||
)
|
||||
|
||||
const client = createSESClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await sendBulkEmail(client, {
|
||||
fromAddress: params.fromAddress,
|
||||
templateName: params.templateName,
|
||||
destinations,
|
||||
defaultTemplateData: params.defaultTemplateData,
|
||||
configurationSetName: params.configurationSetName,
|
||||
})
|
||||
|
||||
logger.info(
|
||||
`Bulk email sent: ${result.successCount} succeeded, ${result.failureCount} failed`
|
||||
)
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to send bulk email:', error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to send bulk email: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,55 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSesSendCustomVerificationEmailContract } from '@/lib/api/contracts/tools/aws/ses-send-custom-verification-email'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSESClient, sendCustomVerificationEmail } from '../utils'
|
||||
|
||||
const logger = createLogger('SESSendCustomVerificationEmailAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSesSendCustomVerificationEmailContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info('Sending SES custom verification email')
|
||||
|
||||
const client = createSESClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await sendCustomVerificationEmail(client, {
|
||||
emailAddress: params.emailAddress,
|
||||
templateName: params.templateName,
|
||||
configurationSetName: params.configurationSetName,
|
||||
})
|
||||
|
||||
logger.info(`Sent custom verification email to '${params.emailAddress}'`)
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to send custom verification email:', error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to send custom verification email: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,81 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSesSendEmailContract } from '@/lib/api/contracts/tools/aws/ses-send-email'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSESClient, sendEmail } from '../utils'
|
||||
|
||||
const logger = createLogger('SESSendEmailAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSesSendEmailContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
const toList = params.toAddresses
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
|
||||
logger.info(`Sending email from ${params.fromAddress} to ${toList.length} recipient(s)`)
|
||||
|
||||
const client = createSESClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await sendEmail(client, {
|
||||
fromAddress: params.fromAddress,
|
||||
toAddresses: toList,
|
||||
subject: params.subject,
|
||||
bodyText: params.bodyText,
|
||||
bodyHtml: params.bodyHtml,
|
||||
ccAddresses: params.ccAddresses
|
||||
? params.ccAddresses
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
: null,
|
||||
bccAddresses: params.bccAddresses
|
||||
? params.bccAddresses
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
: null,
|
||||
replyToAddresses: params.replyToAddresses
|
||||
? params.replyToAddresses
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
: null,
|
||||
configurationSetName: params.configurationSetName,
|
||||
})
|
||||
|
||||
logger.info(`Email sent successfully, messageId: ${result.messageId}`)
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to send email:', error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to send email: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,76 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSesSendTemplatedEmailContract } from '@/lib/api/contracts/tools/aws/ses-send-templated-email'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSESClient, sendTemplatedEmail } from '../utils'
|
||||
|
||||
const logger = createLogger('SESSendTemplatedEmailAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSesSendTemplatedEmailContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
const toList = params.toAddresses
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
|
||||
logger.info(
|
||||
`Sending templated email from ${params.fromAddress} using template '${params.templateName}'`
|
||||
)
|
||||
|
||||
const client = createSESClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await sendTemplatedEmail(client, {
|
||||
fromAddress: params.fromAddress,
|
||||
toAddresses: toList,
|
||||
templateName: params.templateName,
|
||||
templateData: params.templateData,
|
||||
ccAddresses: params.ccAddresses
|
||||
? params.ccAddresses
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
: null,
|
||||
bccAddresses: params.bccAddresses
|
||||
? params.bccAddresses
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
: null,
|
||||
configurationSetName: params.configurationSetName,
|
||||
})
|
||||
|
||||
logger.info(`Templated email sent successfully, messageId: ${result.messageId}`)
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to send templated email:', error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to send templated email: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,56 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsSesUpdateTemplateContract } from '@/lib/api/contracts/tools/aws/ses-update-template'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSESClient, updateTemplate } from '../utils'
|
||||
|
||||
const logger = createLogger('SESUpdateTemplateAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsSesUpdateTemplateContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info('Updating SES email template')
|
||||
|
||||
const client = createSESClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await updateTemplate(client, {
|
||||
templateName: params.templateName,
|
||||
subjectPart: params.subjectPart,
|
||||
textPart: params.textPart,
|
||||
htmlPart: params.htmlPart,
|
||||
})
|
||||
|
||||
logger.info(`Updated template '${params.templateName}'`)
|
||||
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to update template:', error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to update template: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,563 @@
|
||||
import {
|
||||
CreateConfigurationSetCommand,
|
||||
CreateEmailIdentityCommand,
|
||||
CreateEmailTemplateCommand,
|
||||
DeleteEmailIdentityCommand,
|
||||
DeleteEmailTemplateCommand,
|
||||
DeleteSuppressedDestinationCommand,
|
||||
GetAccountCommand,
|
||||
GetEmailIdentityCommand,
|
||||
GetEmailTemplateCommand,
|
||||
GetSuppressedDestinationCommand,
|
||||
ListEmailIdentitiesCommand,
|
||||
ListEmailTemplatesCommand,
|
||||
ListSuppressedDestinationsCommand,
|
||||
PutSuppressedDestinationCommand,
|
||||
SESv2Client,
|
||||
SendBulkEmailCommand,
|
||||
SendCustomVerificationEmailCommand,
|
||||
SendEmailCommand,
|
||||
type SuppressionListReason,
|
||||
type TlsPolicy,
|
||||
UpdateEmailTemplateCommand,
|
||||
} from '@aws-sdk/client-sesv2'
|
||||
import { z } from 'zod'
|
||||
import type { SESConnectionConfig } from '@/tools/ses/types'
|
||||
|
||||
const SesBulkEmailDestinationSchema = z.object({
|
||||
toAddresses: z.array(z.string().email()),
|
||||
templateData: z.string().optional(),
|
||||
})
|
||||
|
||||
type SesBulkEmailDestination = z.infer<typeof SesBulkEmailDestinationSchema>
|
||||
|
||||
export function createSESClient(config: SESConnectionConfig): SESv2Client {
|
||||
return new SESv2Client({
|
||||
region: config.region,
|
||||
credentials: {
|
||||
accessKeyId: config.accessKeyId,
|
||||
secretAccessKey: config.secretAccessKey,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export async function sendEmail(
|
||||
client: SESv2Client,
|
||||
params: {
|
||||
fromAddress: string
|
||||
toAddresses: string[]
|
||||
subject: string
|
||||
bodyText?: string | null
|
||||
bodyHtml?: string | null
|
||||
ccAddresses?: string[] | null
|
||||
bccAddresses?: string[] | null
|
||||
replyToAddresses?: string[] | null
|
||||
configurationSetName?: string | null
|
||||
}
|
||||
) {
|
||||
const command = new SendEmailCommand({
|
||||
FromEmailAddress: params.fromAddress,
|
||||
Destination: {
|
||||
ToAddresses: params.toAddresses,
|
||||
...(params.ccAddresses?.length ? { CcAddresses: params.ccAddresses } : {}),
|
||||
...(params.bccAddresses?.length ? { BccAddresses: params.bccAddresses } : {}),
|
||||
},
|
||||
Content: {
|
||||
Simple: {
|
||||
Subject: { Data: params.subject },
|
||||
Body: {
|
||||
...(params.bodyText ? { Text: { Data: params.bodyText } } : {}),
|
||||
...(params.bodyHtml ? { Html: { Data: params.bodyHtml } } : {}),
|
||||
},
|
||||
},
|
||||
},
|
||||
...(params.replyToAddresses?.length ? { ReplyToAddresses: params.replyToAddresses } : {}),
|
||||
...(params.configurationSetName ? { ConfigurationSetName: params.configurationSetName } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
|
||||
return {
|
||||
messageId: response.MessageId ?? '',
|
||||
}
|
||||
}
|
||||
|
||||
export async function sendTemplatedEmail(
|
||||
client: SESv2Client,
|
||||
params: {
|
||||
fromAddress: string
|
||||
toAddresses: string[]
|
||||
templateName: string
|
||||
templateData: string
|
||||
ccAddresses?: string[] | null
|
||||
bccAddresses?: string[] | null
|
||||
configurationSetName?: string | null
|
||||
}
|
||||
) {
|
||||
const command = new SendEmailCommand({
|
||||
FromEmailAddress: params.fromAddress,
|
||||
Destination: {
|
||||
ToAddresses: params.toAddresses,
|
||||
...(params.ccAddresses?.length ? { CcAddresses: params.ccAddresses } : {}),
|
||||
...(params.bccAddresses?.length ? { BccAddresses: params.bccAddresses } : {}),
|
||||
},
|
||||
Content: {
|
||||
Template: {
|
||||
TemplateName: params.templateName,
|
||||
TemplateData: params.templateData,
|
||||
},
|
||||
},
|
||||
...(params.configurationSetName ? { ConfigurationSetName: params.configurationSetName } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
|
||||
return {
|
||||
messageId: response.MessageId ?? '',
|
||||
}
|
||||
}
|
||||
|
||||
export function parseBulkEmailDestinations(destinationsJson: string): SesBulkEmailDestination[] {
|
||||
const destinations = JSON.parse(destinationsJson)
|
||||
return z.array(SesBulkEmailDestinationSchema).parse(destinations)
|
||||
}
|
||||
|
||||
export async function sendBulkEmail(
|
||||
client: SESv2Client,
|
||||
params: {
|
||||
fromAddress: string
|
||||
templateName: string
|
||||
destinations: SesBulkEmailDestination[]
|
||||
defaultTemplateData?: string | null
|
||||
configurationSetName?: string | null
|
||||
}
|
||||
) {
|
||||
const command = new SendBulkEmailCommand({
|
||||
FromEmailAddress: params.fromAddress,
|
||||
DefaultContent: {
|
||||
Template: {
|
||||
TemplateName: params.templateName,
|
||||
...(params.defaultTemplateData ? { TemplateData: params.defaultTemplateData } : {}),
|
||||
},
|
||||
},
|
||||
BulkEmailEntries: params.destinations.map((dest) => ({
|
||||
Destination: { ToAddresses: dest.toAddresses },
|
||||
...(dest.templateData
|
||||
? {
|
||||
ReplacementEmailContent: {
|
||||
ReplacementTemplate: {
|
||||
ReplacementTemplateData: dest.templateData,
|
||||
},
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
})),
|
||||
...(params.configurationSetName ? { ConfigurationSetName: params.configurationSetName } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
|
||||
const results = (response.BulkEmailEntryResults ?? []).map((r) => ({
|
||||
messageId: r.MessageId ?? null,
|
||||
status: r.Status ?? 'UNKNOWN',
|
||||
error: r.Error ?? null,
|
||||
}))
|
||||
|
||||
const successCount = results.filter((r) => r.status === 'SUCCESS').length
|
||||
const failureCount = results.length - successCount
|
||||
|
||||
return { results, successCount, failureCount }
|
||||
}
|
||||
|
||||
export async function listIdentities(
|
||||
client: SESv2Client,
|
||||
params: {
|
||||
pageSize?: number | null
|
||||
nextToken?: string | null
|
||||
}
|
||||
) {
|
||||
const command = new ListEmailIdentitiesCommand({
|
||||
...(params.pageSize != null ? { PageSize: params.pageSize } : {}),
|
||||
...(params.nextToken ? { NextToken: params.nextToken } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
|
||||
const identities = (response.EmailIdentities ?? []).map((identity) => ({
|
||||
identityName: identity.IdentityName ?? '',
|
||||
identityType: identity.IdentityType ?? '',
|
||||
sendingEnabled: identity.SendingEnabled ?? false,
|
||||
verificationStatus: identity.VerificationStatus ?? '',
|
||||
}))
|
||||
|
||||
return {
|
||||
identities,
|
||||
nextToken: response.NextToken ?? null,
|
||||
count: identities.length,
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAccount(client: SESv2Client) {
|
||||
const command = new GetAccountCommand({})
|
||||
const response = await client.send(command)
|
||||
|
||||
return {
|
||||
sendingEnabled: response.SendingEnabled ?? false,
|
||||
max24HourSend: response.SendQuota?.Max24HourSend ?? 0,
|
||||
maxSendRate: response.SendQuota?.MaxSendRate ?? 0,
|
||||
sentLast24Hours: response.SendQuota?.SentLast24Hours ?? 0,
|
||||
}
|
||||
}
|
||||
|
||||
export async function createTemplate(
|
||||
client: SESv2Client,
|
||||
params: {
|
||||
templateName: string
|
||||
subjectPart: string
|
||||
textPart?: string | null
|
||||
htmlPart?: string | null
|
||||
}
|
||||
) {
|
||||
const command = new CreateEmailTemplateCommand({
|
||||
TemplateName: params.templateName,
|
||||
TemplateContent: {
|
||||
Subject: params.subjectPart,
|
||||
...(params.textPart ? { Text: params.textPart } : {}),
|
||||
...(params.htmlPart ? { Html: params.htmlPart } : {}),
|
||||
},
|
||||
})
|
||||
|
||||
await client.send(command)
|
||||
|
||||
return {
|
||||
message: `Template '${params.templateName}' created successfully`,
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTemplate(client: SESv2Client, templateName: string) {
|
||||
const command = new GetEmailTemplateCommand({ TemplateName: templateName })
|
||||
const response = await client.send(command)
|
||||
|
||||
return {
|
||||
templateName: response.TemplateName ?? '',
|
||||
subjectPart: response.TemplateContent?.Subject ?? '',
|
||||
textPart: response.TemplateContent?.Text ?? null,
|
||||
htmlPart: response.TemplateContent?.Html ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
export async function listTemplates(
|
||||
client: SESv2Client,
|
||||
params: {
|
||||
pageSize?: number | null
|
||||
nextToken?: string | null
|
||||
}
|
||||
) {
|
||||
const command = new ListEmailTemplatesCommand({
|
||||
...(params.pageSize != null ? { PageSize: params.pageSize } : {}),
|
||||
...(params.nextToken ? { NextToken: params.nextToken } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
|
||||
const templates = (response.TemplatesMetadata ?? []).map((t) => ({
|
||||
templateName: t.TemplateName ?? '',
|
||||
createdTimestamp: t.CreatedTimestamp?.toISOString() ?? null,
|
||||
}))
|
||||
|
||||
return {
|
||||
templates,
|
||||
nextToken: response.NextToken ?? null,
|
||||
count: templates.length,
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteTemplate(client: SESv2Client, templateName: string) {
|
||||
const command = new DeleteEmailTemplateCommand({ TemplateName: templateName })
|
||||
await client.send(command)
|
||||
|
||||
return {
|
||||
message: `Template '${templateName}' deleted successfully`,
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateTemplate(
|
||||
client: SESv2Client,
|
||||
params: {
|
||||
templateName: string
|
||||
subjectPart: string
|
||||
textPart?: string | null
|
||||
htmlPart?: string | null
|
||||
}
|
||||
) {
|
||||
const command = new UpdateEmailTemplateCommand({
|
||||
TemplateName: params.templateName,
|
||||
TemplateContent: {
|
||||
Subject: params.subjectPart,
|
||||
...(params.textPart ? { Text: params.textPart } : {}),
|
||||
...(params.htmlPart ? { Html: params.htmlPart } : {}),
|
||||
},
|
||||
})
|
||||
|
||||
await client.send(command)
|
||||
|
||||
return {
|
||||
message: `Template '${params.templateName}' updated successfully`,
|
||||
}
|
||||
}
|
||||
|
||||
export async function putSuppressedDestination(
|
||||
client: SESv2Client,
|
||||
params: { emailAddress: string; reason: SuppressionListReason }
|
||||
) {
|
||||
const command = new PutSuppressedDestinationCommand({
|
||||
EmailAddress: params.emailAddress,
|
||||
Reason: params.reason,
|
||||
})
|
||||
|
||||
await client.send(command)
|
||||
|
||||
return {
|
||||
message: `Email address '${params.emailAddress}' added to the suppression list`,
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteSuppressedDestination(client: SESv2Client, emailAddress: string) {
|
||||
const command = new DeleteSuppressedDestinationCommand({ EmailAddress: emailAddress })
|
||||
await client.send(command)
|
||||
|
||||
return {
|
||||
message: `Email address '${emailAddress}' removed from the suppression list`,
|
||||
}
|
||||
}
|
||||
|
||||
export async function getSuppressedDestination(client: SESv2Client, emailAddress: string) {
|
||||
const command = new GetSuppressedDestinationCommand({ EmailAddress: emailAddress })
|
||||
const response = await client.send(command)
|
||||
const destination = response.SuppressedDestination
|
||||
|
||||
return {
|
||||
emailAddress: destination?.EmailAddress ?? emailAddress,
|
||||
reason: destination?.Reason ?? '',
|
||||
lastUpdateTime: destination?.LastUpdateTime?.toISOString() ?? null,
|
||||
messageId: destination?.Attributes?.MessageId ?? null,
|
||||
feedbackId: destination?.Attributes?.FeedbackId ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
export async function listSuppressedDestinations(
|
||||
client: SESv2Client,
|
||||
params: {
|
||||
reasons?: SuppressionListReason[] | null
|
||||
startDate?: Date | null
|
||||
endDate?: Date | null
|
||||
pageSize?: number | null
|
||||
nextToken?: string | null
|
||||
}
|
||||
) {
|
||||
const command = new ListSuppressedDestinationsCommand({
|
||||
...(params.reasons?.length ? { Reasons: params.reasons } : {}),
|
||||
...(params.startDate ? { StartDate: params.startDate } : {}),
|
||||
...(params.endDate ? { EndDate: params.endDate } : {}),
|
||||
...(params.pageSize != null ? { PageSize: params.pageSize } : {}),
|
||||
...(params.nextToken ? { NextToken: params.nextToken } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
|
||||
const destinations = (response.SuppressedDestinationSummaries ?? []).map((d) => ({
|
||||
emailAddress: d.EmailAddress ?? '',
|
||||
reason: d.Reason ?? '',
|
||||
lastUpdateTime: d.LastUpdateTime?.toISOString() ?? null,
|
||||
}))
|
||||
|
||||
return {
|
||||
destinations,
|
||||
nextToken: response.NextToken ?? null,
|
||||
count: destinations.length,
|
||||
}
|
||||
}
|
||||
|
||||
export async function createEmailIdentity(
|
||||
client: SESv2Client,
|
||||
params: {
|
||||
emailIdentity: string
|
||||
dkimSigningAttributes?: {
|
||||
domainSigningSelector?: string
|
||||
domainSigningPrivateKey?: string
|
||||
nextSigningKeyLength?: 'RSA_1024_BIT' | 'RSA_2048_BIT'
|
||||
} | null
|
||||
tags?: Array<{ key: string; value: string }> | null
|
||||
configurationSetName?: string | null
|
||||
}
|
||||
) {
|
||||
const command = new CreateEmailIdentityCommand({
|
||||
EmailIdentity: params.emailIdentity,
|
||||
...(params.dkimSigningAttributes
|
||||
? {
|
||||
DkimSigningAttributes: {
|
||||
...(params.dkimSigningAttributes.domainSigningSelector
|
||||
? { DomainSigningSelector: params.dkimSigningAttributes.domainSigningSelector }
|
||||
: {}),
|
||||
...(params.dkimSigningAttributes.domainSigningPrivateKey
|
||||
? { DomainSigningPrivateKey: params.dkimSigningAttributes.domainSigningPrivateKey }
|
||||
: {}),
|
||||
...(params.dkimSigningAttributes.nextSigningKeyLength
|
||||
? { NextSigningKeyLength: params.dkimSigningAttributes.nextSigningKeyLength }
|
||||
: {}),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(params.tags?.length
|
||||
? { Tags: params.tags.map((t) => ({ Key: t.key, Value: t.value })) }
|
||||
: {}),
|
||||
...(params.configurationSetName ? { ConfigurationSetName: params.configurationSetName } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
|
||||
return {
|
||||
identityType: response.IdentityType ?? '',
|
||||
verifiedForSendingStatus: response.VerifiedForSendingStatus ?? false,
|
||||
dkimAttributes: response.DkimAttributes
|
||||
? {
|
||||
signingEnabled: response.DkimAttributes.SigningEnabled ?? null,
|
||||
status: response.DkimAttributes.Status ?? null,
|
||||
tokens: response.DkimAttributes.Tokens ?? [],
|
||||
signingAttributesOrigin: response.DkimAttributes.SigningAttributesOrigin ?? null,
|
||||
nextSigningKeyLength: response.DkimAttributes.NextSigningKeyLength ?? null,
|
||||
currentSigningKeyLength: response.DkimAttributes.CurrentSigningKeyLength ?? null,
|
||||
lastKeyGenerationTimestamp:
|
||||
response.DkimAttributes.LastKeyGenerationTimestamp?.toISOString() ?? null,
|
||||
signingHostedZone: response.DkimAttributes.SigningHostedZone ?? null,
|
||||
}
|
||||
: null,
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteEmailIdentity(client: SESv2Client, emailIdentity: string) {
|
||||
const command = new DeleteEmailIdentityCommand({ EmailIdentity: emailIdentity })
|
||||
await client.send(command)
|
||||
|
||||
return {
|
||||
message: `Email identity '${emailIdentity}' deleted successfully`,
|
||||
}
|
||||
}
|
||||
|
||||
export async function getEmailIdentity(client: SESv2Client, emailIdentity: string) {
|
||||
const command = new GetEmailIdentityCommand({ EmailIdentity: emailIdentity })
|
||||
const response = await client.send(command)
|
||||
|
||||
return {
|
||||
identityType: response.IdentityType ?? '',
|
||||
verifiedForSendingStatus: response.VerifiedForSendingStatus ?? false,
|
||||
verificationStatus: response.VerificationStatus ?? null,
|
||||
feedbackForwardingStatus: response.FeedbackForwardingStatus ?? null,
|
||||
configurationSetName: response.ConfigurationSetName ?? null,
|
||||
dkimAttributes: response.DkimAttributes
|
||||
? {
|
||||
signingEnabled: response.DkimAttributes.SigningEnabled ?? null,
|
||||
status: response.DkimAttributes.Status ?? null,
|
||||
tokens: response.DkimAttributes.Tokens ?? [],
|
||||
signingAttributesOrigin: response.DkimAttributes.SigningAttributesOrigin ?? null,
|
||||
nextSigningKeyLength: response.DkimAttributes.NextSigningKeyLength ?? null,
|
||||
currentSigningKeyLength: response.DkimAttributes.CurrentSigningKeyLength ?? null,
|
||||
lastKeyGenerationTimestamp:
|
||||
response.DkimAttributes.LastKeyGenerationTimestamp?.toISOString() ?? null,
|
||||
signingHostedZone: response.DkimAttributes.SigningHostedZone ?? null,
|
||||
}
|
||||
: null,
|
||||
mailFromAttributes: response.MailFromAttributes
|
||||
? {
|
||||
mailFromDomain: response.MailFromAttributes.MailFromDomain ?? null,
|
||||
mailFromDomainStatus: response.MailFromAttributes.MailFromDomainStatus ?? null,
|
||||
behaviorOnMxFailure: response.MailFromAttributes.BehaviorOnMxFailure ?? null,
|
||||
}
|
||||
: null,
|
||||
policies: response.Policies ?? null,
|
||||
tags: (response.Tags ?? []).map((t) => ({ key: t.Key ?? '', value: t.Value ?? '' })),
|
||||
verificationInfo: response.VerificationInfo
|
||||
? {
|
||||
errorType: response.VerificationInfo.ErrorType ?? null,
|
||||
lastCheckedTimestamp:
|
||||
response.VerificationInfo.LastCheckedTimestamp?.toISOString() ?? null,
|
||||
lastSuccessTimestamp:
|
||||
response.VerificationInfo.LastSuccessTimestamp?.toISOString() ?? null,
|
||||
}
|
||||
: null,
|
||||
}
|
||||
}
|
||||
|
||||
export async function createConfigurationSet(
|
||||
client: SESv2Client,
|
||||
params: {
|
||||
configurationSetName: string
|
||||
customRedirectDomain?: string | null
|
||||
httpsPolicy?: 'REQUIRE' | 'REQUIRE_OPEN_ONLY' | 'OPTIONAL' | null
|
||||
tlsPolicy?: TlsPolicy | null
|
||||
sendingPoolName?: string | null
|
||||
reputationMetricsEnabled?: boolean | null
|
||||
sendingEnabled?: boolean | null
|
||||
suppressedReasons?: SuppressionListReason[] | null
|
||||
tags?: Array<{ key: string; value: string }> | null
|
||||
}
|
||||
) {
|
||||
const command = new CreateConfigurationSetCommand({
|
||||
ConfigurationSetName: params.configurationSetName,
|
||||
...(params.customRedirectDomain
|
||||
? {
|
||||
TrackingOptions: {
|
||||
CustomRedirectDomain: params.customRedirectDomain,
|
||||
...(params.httpsPolicy ? { HttpsPolicy: params.httpsPolicy } : {}),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(params.tlsPolicy || params.sendingPoolName
|
||||
? {
|
||||
DeliveryOptions: {
|
||||
...(params.tlsPolicy ? { TlsPolicy: params.tlsPolicy } : {}),
|
||||
...(params.sendingPoolName ? { SendingPoolName: params.sendingPoolName } : {}),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(params.reputationMetricsEnabled != null
|
||||
? { ReputationOptions: { ReputationMetricsEnabled: params.reputationMetricsEnabled } }
|
||||
: {}),
|
||||
...(params.sendingEnabled != null
|
||||
? { SendingOptions: { SendingEnabled: params.sendingEnabled } }
|
||||
: {}),
|
||||
...(params.suppressedReasons?.length
|
||||
? { SuppressionOptions: { SuppressedReasons: params.suppressedReasons } }
|
||||
: {}),
|
||||
...(params.tags?.length
|
||||
? { Tags: params.tags.map((t) => ({ Key: t.key, Value: t.value })) }
|
||||
: {}),
|
||||
})
|
||||
|
||||
await client.send(command)
|
||||
|
||||
return {
|
||||
message: `Configuration set '${params.configurationSetName}' created successfully`,
|
||||
}
|
||||
}
|
||||
|
||||
export async function sendCustomVerificationEmail(
|
||||
client: SESv2Client,
|
||||
params: {
|
||||
emailAddress: string
|
||||
templateName: string
|
||||
configurationSetName?: string | null
|
||||
}
|
||||
) {
|
||||
const command = new SendCustomVerificationEmailCommand({
|
||||
EmailAddress: params.emailAddress,
|
||||
TemplateName: params.templateName,
|
||||
...(params.configurationSetName ? { ConfigurationSetName: params.configurationSetName } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
|
||||
return {
|
||||
messageId: response.MessageId ?? '',
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user