import * as z from "zod" import { defineAction } from "../../automation/actions" import { getBrevoApi, parseBrevoResponse, } from "@automate.ax/integration-contracts/brevo" const BREVO_ACCOUNT = "brevo" const DOMAIN_NAME_SCHEMA = z .string() .trim() .toLowerCase() .regex( /^(?:[a-z\d](?:[a-z\d-]{0,61}[a-z\d])?\.)+[a-z]{2,63}$/, "Provide a fully qualified domain name.", ) const DNS_RECORD_WIRE_SCHEMA = z.object({ host_name: z.string(), status: z.boolean(), type: z.string(), value: z.string(), }) const DNS_RECORD_SCHEMA = z.object({ /** DNS record hostname. */ hostName: z.string(), /** Whether Brevo sees the expected record. */ status: z.boolean(), /** DNS record type. */ type: z.string(), /** DNS record value. */ value: z.string(), }) const DNS_RECORDS_WIRE_SCHEMA = z.object({ brevo_code: DNS_RECORD_WIRE_SCHEMA, dkim_record: DNS_RECORD_WIRE_SCHEMA, dmarc_record: DNS_RECORD_WIRE_SCHEMA, }) const DNS_RECORDS_SCHEMA = z.object({ brevoCode: DNS_RECORD_SCHEMA, dkimRecord: DNS_RECORD_SCHEMA, dmarcRecord: DNS_RECORD_SCHEMA, }) const DOMAIN_CONFIGURATION_WIRE_SCHEMA = z.object({ authenticated: z.boolean(), dns_records: DNS_RECORDS_WIRE_SCHEMA, domain: DOMAIN_NAME_SCHEMA, verified: z.boolean(), }) const DOMAIN_CONFIGURATION_SCHEMA = z.object({ /** Whether Brevo authenticated the domain for sending. */ authenticated: z.boolean(), /** DNS records and their current verification state. */ dnsRecords: DNS_RECORDS_SCHEMA, /** Sending domain. */ domain: DOMAIN_NAME_SCHEMA, /** Whether Brevo verified ownership of the domain. */ verified: z.boolean(), }) /** Lists sending domains configured in Brevo. */ export const listBrevoDomains = defineAction("List Brevo domains") .describe("Lists configured sending domains and authentication states.") .account(BREVO_ACCOUNT) .output( z.object({ count: z.number().int().nonnegative(), currentPage: z.number().int().positive(), domains: z .object({ authenticated: z.boolean(), creator: z.object({ creationDate: z.string(), email: z.email(), id: z.string(), }), domainName: DOMAIN_NAME_SCHEMA, id: z.string(), ip: z.ipv4().nullable().optional(), provider: z.string(), verified: z.boolean(), }) .array(), totalPages: z.number().int().nonnegative(), }), ) .retry({ replaySafety: "safe" }) .handler(async ({ account }) => { const result = await parseBrevoResponse( await getBrevoApi(account.secret).request("senders/domains"), z.object({ count: z.number().int().nonnegative(), current_page: z.number().int().positive(), domains: z .object({ authenticated: z.boolean(), creator: z.object({ creationDate: z.string(), email: z.email(), id: z.string(), }), domain_name: DOMAIN_NAME_SCHEMA, id: z.string(), ip: z.ipv4().nullable().optional(), provider: z.string(), verified: z.boolean(), }) .array(), total_pages: z.number().int().nonnegative(), }), ) return { count: result.count, currentPage: result.current_page, domains: result.domains.map(({ domain_name, ...domain }) => ({ ...domain, domainName: domain_name, })), totalPages: result.total_pages, } }) /** Creates a Brevo sending domain and returns required DNS records. */ export const createBrevoDomain = defineAction("Create Brevo domain") .describe("Creates a sending domain and returns its DNS requirements.") .account(BREVO_ACCOUNT) .input(z.object({ domainName: DOMAIN_NAME_SCHEMA })) .output( z.object({ dnsRecords: DNS_RECORDS_SCHEMA.optional(), domainName: DOMAIN_NAME_SCHEMA, domainProvider: z.string().optional(), id: z.number().int().positive(), message: z.string(), }), ) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const result = await parseBrevoResponse( await getBrevoApi(account.secret).request("senders/domains", { body: JSON.stringify({ name: input.domainName }), method: "POST", }), z.object({ dns_records: DNS_RECORDS_WIRE_SCHEMA.optional(), domain_name: DOMAIN_NAME_SCHEMA, domain_provider: z.string().optional(), id: z.number().int().positive(), message: z.string(), }), ) return { dnsRecords: result.dns_records ? toDnsRecords(result.dns_records) : undefined, domainName: result.domain_name, domainProvider: result.domain_provider, id: result.id, message: result.message, } }) /** Retrieves a Brevo sending domain’s DNS configuration. */ export const getBrevoDomain = defineAction("Get Brevo domain") .describe("Retrieves DNS records and authentication state for a domain.") .account(BREVO_ACCOUNT) .input(z.object({ domainName: DOMAIN_NAME_SCHEMA })) .output(DOMAIN_CONFIGURATION_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const result = await parseBrevoResponse( await getBrevoApi(account.secret).request( `senders/domains/${encodeURIComponent(input.domainName)}`, ), DOMAIN_CONFIGURATION_WIRE_SCHEMA, ) return { authenticated: result.authenticated, dnsRecords: toDnsRecords(result.dns_records), domain: result.domain, verified: result.verified, } }) /** Deletes a Brevo sending domain. */ export const deleteBrevoDomain = defineAction("Delete Brevo domain") .describe("Permanently deletes a Brevo sending domain.") .account(BREVO_ACCOUNT) .input(z.object({ domainName: DOMAIN_NAME_SCHEMA })) .output(z.void()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { await getBrevoApi(account.secret).request( `senders/domains/${encodeURIComponent(input.domainName)}`, { method: "DELETE" }, ) }) /** Rechecks and authenticates a Brevo sending domain. */ export const authenticateBrevoDomain = defineAction("Authenticate Brevo domain") .describe("Rechecks DNS and authenticates a Brevo sending domain.") .account(BREVO_ACCOUNT) .input(z.object({ domainName: DOMAIN_NAME_SCHEMA })) .output(z.object({ domainName: DOMAIN_NAME_SCHEMA, message: z.string() })) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const result = await parseBrevoResponse( await getBrevoApi(account.secret).request( `senders/domains/${encodeURIComponent(input.domainName)}/authenticate`, { method: "PUT" }, ), z.object({ domain_name: DOMAIN_NAME_SCHEMA, message: z.string() }), ) return { domainName: result.domain_name, message: result.message } }) /** * Maps provider-native DNS record names to the public contract. * * @param records - Provider DNS records. */ function toDnsRecords(records: z.output) { return { brevoCode: toDnsRecord(records.brevo_code), dkimRecord: toDnsRecord(records.dkim_record), dmarcRecord: toDnsRecord(records.dmarc_record), } } /** * Maps one provider-native DNS record. * * @param record - Provider DNS record. */ function toDnsRecord(record: z.output) { const { host_name, ...rest } = record return { ...rest, hostName: host_name } }